99 variables = self.CodeFileVariables(self.CodeFile) |
99 variables = self.CodeFileVariables(self.CodeFile) |
100 ret = [(variable.getname(), |
100 ret = [(variable.getname(), |
101 variable.gettype(), |
101 variable.gettype(), |
102 variable.getinitial()) |
102 variable.getinitial()) |
103 for variable in variables] |
103 for variable in variables] |
104 ret.extend([("On"+variable.getname()+"Change", "python_poll", "") |
104 location_str = "_".join(map(str, self.GetCurrentLocation())) |
105 for variable in variables |
105 ret.append(("On_"+location_str+"_Change", "python_poll", "")) |
106 if variable.getonchange()]) |
|
107 return ret |
106 return ret |
108 |
107 |
109 def CTNGenerate_C(self, buildpath, locations): |
108 def CTNGenerate_C(self, buildpath, locations): |
110 # location string for that CTN |
109 # location string for that CTN |
111 location_str = "_".join(map(str, self.GetCurrentLocation())) |
110 location_str = "_".join(map(str, self.GetCurrentLocation())) |
112 configname = self.GetCTRoot().GetProjectConfigNames()[0] |
111 configname = self.GetCTRoot().GetProjectConfigNames()[0] |
113 |
112 |
114 def _onchangecode(var): |
113 def _onchangecode(var): |
115 return '"' + var.getonchange() + \ |
114 return var.getonchange() + "('" + var.getname() + "')" |
116 "('" + var.getname() + "')\"" \ |
|
117 if var.getonchange() else '""' |
|
118 |
115 |
119 def _onchange(var): |
116 def _onchange(var): |
120 return repr(var.getonchange()) \ |
117 return repr(var.getonchange()) \ |
121 if var.getonchange() else None |
118 if var.getonchange() else None |
122 |
119 |
133 "IECtype": self.GetCTRoot().GetBaseType(variable.gettype()), |
130 "IECtype": self.GetCTRoot().GetBaseType(variable.gettype()), |
134 "initial": repr(variable.getinitial()), |
131 "initial": repr(variable.getinitial()), |
135 "pyextname": pyextname |
132 "pyextname": pyextname |
136 }, |
133 }, |
137 self.CodeFile.variables.variable) |
134 self.CodeFile.variables.variable) |
|
135 |
|
136 onchange_var_count = len([None for varinfo in varinfos if varinfo["onchange"]]) |
|
137 |
138 # python side PLC global variables access stub |
138 # python side PLC global variables access stub |
139 globalstubs = "\n".join([ |
139 globalstubs = "\n".join([ |
140 """\ |
140 """\ |
141 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\ |
141 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\ |
142 TypeTranslator["%(IECtype)s"] |
142 TypeTranslator["%(IECtype)s"] |
153 %(desc)s, |
153 %(desc)s, |
154 %(onchange)s, |
154 %(onchange)s, |
155 %(opts)s)) |
155 %(opts)s)) |
156 """ % varinfo for varinfo in varinfos]) |
156 """ % varinfo for varinfo in varinfos]) |
157 |
157 |
|
158 on_change_func_body = "\n".join([""" |
|
159 if changes.next(): |
|
160 # %(name)s |
|
161 try: |
|
162 %(onchangecode)s |
|
163 except Exception as e: |
|
164 errors.append("%(name)s: "+str(e)) |
|
165 """ % varinfo for varinfo in varinfos if varinfo["onchange"]]) |
158 # Runtime calls (start, stop, init, and cleanup) |
166 # Runtime calls (start, stop, init, and cleanup) |
159 rtcalls = "" |
167 rtcalls = "" |
160 for section in self.SECTIONS_NAMES: |
168 for section in self.SECTIONS_NAMES: |
161 if section != "globals": |
169 if section != "globals": |
162 rtcalls += "def _runtime_%s_%s():\n" % (location_str, section) |
170 rtcalls += "def _runtime_%s_%s():\n" % (location_str, section) |
172 loc_dict = { |
180 loc_dict = { |
173 "pyextname": pyextname, |
181 "pyextname": pyextname, |
174 "globalstubs": globalstubs, |
182 "globalstubs": globalstubs, |
175 "globalsection": globalsection, |
183 "globalsection": globalsection, |
176 "rtcalls": rtcalls, |
184 "rtcalls": rtcalls, |
|
185 "location_str": location_str, |
|
186 "on_change_func_body":on_change_func_body, |
|
187 "onchange_var_count": onchange_var_count |
177 } |
188 } |
178 |
189 |
179 PyFileContent = """\ |
190 PyFileContent = """\ |
180 #!/usr/bin/env python |
191 #!/usr/bin/env python |
181 # -*- coding: utf-8 -*- |
192 # -*- coding: utf-8 -*- |
183 ## |
194 ## |
184 |
195 |
185 ## Code for PLC global variable access |
196 ## Code for PLC global variable access |
186 from runtime.typemapping import TypeTranslator |
197 from runtime.typemapping import TypeTranslator |
187 import ctypes |
198 import ctypes |
|
199 |
|
200 _PySafeGetChanges_%(pyextname)s = PLCBinary.PySafeGetChanges_%(location_str)s |
|
201 _PySafeGetChanges_%(pyextname)s.restype = ctypes.POINTER(ctypes.c_int * %(onchange_var_count)d) |
|
202 _PySafeGetChanges_%(pyextname)s.argtypes = None |
|
203 |
188 _%(pyextname)sGlobalsDesc = [] |
204 _%(pyextname)sGlobalsDesc = [] |
189 __ext_name__ = "%(pyextname)s" |
205 __ext_name__ = "%(pyextname)s" |
190 PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc )) |
206 PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc )) |
191 %(globalstubs)s |
207 %(globalstubs)s |
192 |
208 |
193 ## User code in "global" scope |
209 ## User code in "global" scope |
194 %(globalsection)s |
210 %(globalsection)s |
195 |
211 |
196 ## Beremiz python runtime calls |
212 ## Beremiz python runtime calls |
197 %(rtcalls)s |
213 %(rtcalls)s |
|
214 |
|
215 def On_%(pyextname)s_Change(): |
|
216 changesP = _PySafeGetChanges_%(pyextname)s() |
|
217 if not changesP: |
|
218 raise Exception("PySafeGetChanges returned NULL!") |
|
219 changes = iter(changesP.contents) |
|
220 errors = [] |
|
221 %(on_change_func_body)s |
|
222 if len(errors)>0 : |
|
223 raise Exception("Exception in %(pyextname)s OnChange call:\\\\n" + "\\\\n".join(errors)) |
198 |
224 |
199 del __ext_name__ |
225 del __ext_name__ |
200 |
226 |
201 """ % loc_dict |
227 """ % loc_dict |
202 |
228 |
253 varpubonchangefmt = """\ |
279 varpubonchangefmt = """\ |
254 if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){ |
280 if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){ |
255 IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s); |
281 IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s); |
256 if(NE_%(IECtype)s(1, NULL, __%(name)s_rbuffer, tmp)){ |
282 if(NE_%(IECtype)s(1, NULL, __%(name)s_rbuffer, tmp)){ |
257 __%(name)s_rbuffer = tmp; |
283 __%(name)s_rbuffer = tmp; |
258 PYTHON_POLL_body__(__%(name)s_notifier); |
284 /* mark variable as changed */ |
|
285 __%(name)s_rbuffer_written = 1; |
|
286 some_change = 1; |
259 } |
287 } |
260 AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); |
288 AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); |
261 } |
289 } |
262 """ |
290 """ |
263 varinitonchangefmt = """\ |
291 |
264 __%(name)s_notifier = __GET_GLOBAL_ON%(uppername)sCHANGE(); |
292 varcollectchangefmt = """\ |
265 __SET_VAR(__%(name)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE)); |
293 while(AtomicCompareExchange(&__%(name)s_wlock, 0, 1)); |
266 __SET_VAR(__%(name)s_notifier->,CODE,,__STRING_LITERAL(%(onchangelen)d,%(onchangecode)s)); |
294 pysafe_changes[change_index++] = __%(name)s_rbuffer_written; |
|
295 /* mark variable as unchanged */ |
|
296 __%(name)s_rbuffer_written = 0; |
|
297 AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0); |
|
298 |
267 """ |
299 """ |
268 vardec = "\n".join([(vardecfmt + vardeconchangefmt |
300 vardec = "\n".join([(vardecfmt + vardeconchangefmt |
269 if varinfo["onchange"] else vardecfmt) % varinfo |
301 if varinfo["onchange"] else vardecfmt) % varinfo |
270 for varinfo in varinfos]) |
302 for varinfo in varinfos]) |
271 varret = "\n".join([varretfmt % varinfo for varinfo in varinfos]) |
303 varret = "\n".join([varretfmt % varinfo for varinfo in varinfos]) |
272 varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else |
304 varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else |
273 varpubfmt) % varinfo |
305 varpubfmt) % varinfo |
274 for varinfo in varinfos]) |
306 for varinfo in varinfos]) |
275 varinit = "\n".join([varinitonchangefmt % |
307 varcollectchange = "\n".join([varcollectchangefmt % varinfo |
276 dict(onchangelen=len(varinfo["onchangecode"]), **varinfo) |
|
277 for varinfo in varinfos if varinfo["onchange"]]) |
308 for varinfo in varinfos if varinfo["onchange"]]) |
|
309 |
|
310 pysafe_pypoll_code = "On_"+pyextname+"_Change()" |
278 |
311 |
279 loc_dict = { |
312 loc_dict = { |
280 "vardec": vardec, |
313 "vardec": vardec, |
281 "varinit": varinit, |
|
282 "varret": varret, |
314 "varret": varret, |
283 "varpub": varpub, |
315 "varpub": varpub, |
284 "location_str": location_str, |
316 "location_str": location_str, |
|
317 "pysafe_pypoll_code": '"'+pysafe_pypoll_code+'"', |
|
318 "pysafe_pypoll_code_len": len(pysafe_pypoll_code), |
|
319 "varcollectchange": varcollectchange, |
|
320 "onchange_var_count": onchange_var_count |
285 } |
321 } |
286 |
322 |
287 # TODO : use config name obtained from model instead of default |
323 # TODO : use config name obtained from model instead of default |
288 # "config.h". User cannot change config name, but project imported |
324 # "config.h". User cannot change config name, but project imported |
289 # or created in older beremiz vesion could use different name. |
325 # or created in older beremiz vesion could use different name. |
295 #include "iec_types_all.h" |
331 #include "iec_types_all.h" |
296 #include "POUS.h" |
332 #include "POUS.h" |
297 #include "config.h" |
333 #include "config.h" |
298 #include "beremiz.h" |
334 #include "beremiz.h" |
299 |
335 |
|
336 PYTHON_POLL* __%(location_str)s_notifier; |
|
337 |
300 /* User variables reference */ |
338 /* User variables reference */ |
301 %(vardec)s |
339 %(vardec)s |
302 |
340 |
303 /* Beremiz confnode functions */ |
341 /* Beremiz confnode functions */ |
304 int __init_%(location_str)s(int argc,char **argv){ |
342 int __init_%(location_str)s(int argc,char **argv){ |
305 %(varinit)s |
343 __%(location_str)s_notifier = __GET_GLOBAL_ON_%(location_str)s_CHANGE(); |
|
344 __SET_VAR(__%(location_str)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE)); |
|
345 __SET_VAR(__%(location_str)s_notifier->,CODE,,__STRING_LITERAL(%(pysafe_pypoll_code_len)d,%(pysafe_pypoll_code)s)); |
|
346 |
306 return 0; |
347 return 0; |
307 } |
348 } |
308 |
349 |
309 void __cleanup_%(location_str)s(void){ |
350 void __cleanup_%(location_str)s(void){ |
310 } |
351 } |
312 void __retrieve_%(location_str)s(void){ |
353 void __retrieve_%(location_str)s(void){ |
313 %(varret)s |
354 %(varret)s |
314 } |
355 } |
315 |
356 |
316 void __publish_%(location_str)s(void){ |
357 void __publish_%(location_str)s(void){ |
|
358 int some_change = 0; |
317 %(varpub)s |
359 %(varpub)s |
318 } |
360 // call python part if there was at least a change |
|
361 if(some_change){ |
|
362 PYTHON_POLL_body__(__%(location_str)s_notifier); |
|
363 } |
|
364 } |
|
365 |
|
366 static int pysafe_changes[%(onchange_var_count)d]; |
|
367 void* PySafeGetChanges_%(location_str)s(void){ |
|
368 int change_index=0; |
|
369 %(varcollectchange)s |
|
370 return (void*)&pysafe_changes[0]; |
|
371 } |
|
372 |
319 """ % loc_dict |
373 """ % loc_dict |
320 |
374 |
321 Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str) |
375 Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str) |
322 pycfile = open(Gen_PyCfile_path, 'w') |
376 pycfile = open(Gen_PyCfile_path, 'w') |
323 pycfile.write(PyCFileContent) |
377 pycfile.write(PyCFileContent) |