93 def GetSection(self, section): |
95 def GetSection(self, section): |
94 return self.PreSectionsTexts.get(section, "") + "\n" + \ |
96 return self.PreSectionsTexts.get(section, "") + "\n" + \ |
95 getattr(self.CodeFile, section).getanyText() + "\n" + \ |
97 getattr(self.CodeFile, section).getanyText() + "\n" + \ |
96 self.PostSectionsTexts.get(section, "") |
98 self.PostSectionsTexts.get(section, "") |
97 |
99 |
|
100 def CTNGlobalInstances(self): |
|
101 variables = self.CodeFileVariables(self.CodeFile) |
|
102 ret = [(variable.getname(), |
|
103 variable.gettype(), |
|
104 variable.getinitial()) |
|
105 for variable in variables] |
|
106 location_str = "_".join(map(str, self.GetCurrentLocation())) |
|
107 ret.append(("On_"+location_str+"_Change", "python_poll", "")) |
|
108 return ret |
|
109 |
|
110 @staticmethod |
|
111 def GetVarOnChangeContent(var): |
|
112 """ |
|
113 returns given variable onchange field |
|
114 function is meant to allow customization |
|
115 """ |
|
116 return var.getonchange() |
|
117 |
98 def CTNGenerate_C(self, buildpath, locations): |
118 def CTNGenerate_C(self, buildpath, locations): |
99 # location string for that CTN |
119 # location string for that CTN |
100 location_str = "_".join(map(str, self.GetCurrentLocation())) |
120 location_str = "_".join(map(str, self.GetCurrentLocation())) |
101 configname = self.GetCTRoot().GetProjectConfigNames()[0] |
121 configname = self.GetCTRoot().GetProjectConfigNames()[0] |
102 |
122 |
103 def _onchangecode(var): |
123 def _onchangecode(var): |
104 return '"' + var.getonchange() + \ |
124 result = [] |
105 "('" + var.getname() + "')\"" \ |
125 for onchangecall in self.GetVarOnChangeContent(var).split(','): |
106 if var.getonchange() else '""' |
126 onchangecall = onchangecall.strip() |
|
127 if onchangecall: |
|
128 result.append(onchangecall + "('" + var.getname() + "')") |
|
129 return result |
|
130 |
107 |
131 |
108 def _onchange(var): |
132 def _onchange(var): |
109 return repr(var.getonchange()) \ |
133 content = self.GetVarOnChangeContent(var) |
110 if var.getonchange() else None |
134 return repr(content) if content else None |
111 |
135 |
112 pyextname = self.CTNName() |
136 pyextname = self.CTNName() |
113 varinfos = map( |
137 varinfos = map( |
114 lambda variable: { |
138 lambda variable: { |
115 "name": variable.getname(), |
139 "name": variable.getname(), |
122 "IECtype": self.GetCTRoot().GetBaseType(variable.gettype()), |
146 "IECtype": self.GetCTRoot().GetBaseType(variable.gettype()), |
123 "initial": repr(variable.getinitial()), |
147 "initial": repr(variable.getinitial()), |
124 "pyextname": pyextname |
148 "pyextname": pyextname |
125 }, |
149 }, |
126 self.CodeFile.variables.variable) |
150 self.CodeFile.variables.variable) |
|
151 |
|
152 onchange_var_count = len([None for varinfo in varinfos if varinfo["onchange"]]) |
|
153 |
127 # python side PLC global variables access stub |
154 # python side PLC global variables access stub |
128 globalstubs = "\n".join([ |
155 globalstubs = "\n".join([ |
129 """\ |
156 """\ |
130 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\ |
157 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\ |
131 TypeTranslator["%(IECtype)s"] |
158 TypeTranslator["%(IECtype)s"] |
140 "%(IECtype)s", |
167 "%(IECtype)s", |
141 %(initial)s, |
168 %(initial)s, |
142 %(desc)s, |
169 %(desc)s, |
143 %(onchange)s, |
170 %(onchange)s, |
144 %(opts)s)) |
171 %(opts)s)) |
145 """ % varinfo for varinfo in varinfos]) |
172 """ % varinfo + (""" |
146 |
173 _PyOnChangeCount_%(name)s = ctypes.c_uint.in_dll(PLCBinary,"__%(name)s_onchange_count") |
|
174 _PyOnChangeFirst_%(name)s = _%(name)s_ctype.in_dll(PLCBinary,"__%(name)s_onchange_firstval") |
|
175 _PyOnChangeLast_%(name)s = _%(name)s_ctype.in_dll(PLCBinary,"__%(name)s_onchange_lastval") |
|
176 """ % varinfo if varinfo["onchange"] else "") for varinfo in varinfos]) |
|
177 |
|
178 on_change_func_body = "\n".join([""" |
|
179 if _PyOnChangeCount_%(name)s.value > 0: |
|
180 # %(name)s |
|
181 try:""" % varinfo + """ |
|
182 """ + """ |
|
183 """.join(varinfo['onchangecode'])+""" |
|
184 except Exception as e: |
|
185 errors.append("%(name)s: "+str(e)) |
|
186 """ % varinfo for varinfo in varinfos if varinfo["onchange"]]) |
147 # Runtime calls (start, stop, init, and cleanup) |
187 # Runtime calls (start, stop, init, and cleanup) |
148 rtcalls = "" |
188 rtcalls = "" |
149 for section in self.SECTIONS_NAMES: |
189 for section in self.SECTIONS_NAMES: |
150 if section != "globals": |
190 if section != "globals": |
151 rtcalls += "def _runtime_%s_%s():\n" % (location_str, section) |
191 rtcalls += "def _runtime_%s_%s():\n" % (location_str, section) |
172 ## |
215 ## |
173 |
216 |
174 ## Code for PLC global variable access |
217 ## Code for PLC global variable access |
175 from runtime.typemapping import TypeTranslator |
218 from runtime.typemapping import TypeTranslator |
176 import ctypes |
219 import ctypes |
|
220 |
|
221 _PySafeGetChanges_%(pyextname)s = PLCBinary.PySafeGetChanges_%(location_str)s |
|
222 _PySafeGetChanges_%(pyextname)s.restype = None |
|
223 _PySafeGetChanges_%(pyextname)s.argtypes = None |
|
224 |
177 _%(pyextname)sGlobalsDesc = [] |
225 _%(pyextname)sGlobalsDesc = [] |
178 __ext_name__ = "%(pyextname)s" |
226 __ext_name__ = "%(pyextname)s" |
179 PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc )) |
227 PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc )) |
180 %(globalstubs)s |
228 %(globalstubs)s |
181 |
229 |
182 ## User code in "global" scope |
230 ## User code in "global" scope |
183 %(globalsection)s |
231 %(globalsection)s |
184 |
232 |
185 ## Beremiz python runtime calls |
233 ## Beremiz python runtime calls |
186 %(rtcalls)s |
234 %(rtcalls)s |
|
235 |
|
236 def On_%(pyextname)s_Change(): |
|
237 _PySafeGetChanges_%(pyextname)s() |
|
238 errors = [] |
|
239 %(on_change_func_body)s |
|
240 if len(errors)>0 : |
|
241 raise Exception("Exception in %(pyextname)s OnChange call:\\\\n" + "\\\\n".join(errors)) |
187 |
242 |
188 del __ext_name__ |
243 del __ext_name__ |
189 |
244 |
190 """ % loc_dict |
245 """ % loc_dict |
191 |
246 |
241 |
301 |
242 varpubonchangefmt = """\ |
302 varpubonchangefmt = """\ |
243 if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){ |
303 if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){ |
244 IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s); |
304 IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s); |
245 if(NE_%(IECtype)s(1, NULL, __%(name)s_rbuffer, tmp)){ |
305 if(NE_%(IECtype)s(1, NULL, __%(name)s_rbuffer, tmp)){ |
|
306 if(__%(name)s_rbuffer_written == 0); |
|
307 __%(name)s_rbuffer_firstval = __%(name)s_rbuffer; |
|
308 __%(name)s_rbuffer_lastval = tmp; |
246 __%(name)s_rbuffer = tmp; |
309 __%(name)s_rbuffer = tmp; |
247 PYTHON_POLL_body__(__%(name)s_notifier); |
310 /* count one more change */ |
|
311 __%(name)s_rbuffer_written += 1; |
|
312 some_change_found = 1; |
248 } |
313 } |
249 AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); |
314 AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); |
250 } |
315 } |
251 """ |
316 """ |
252 varinitonchangefmt = """\ |
317 |
253 __%(name)s_notifier = __GET_GLOBAL_ON%(uppername)sCHANGE(); |
318 varcollectchangefmt = """\ |
254 __SET_VAR(__%(name)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE)); |
319 while(AtomicCompareExchange(&__%(name)s_rlock, 0, 1)); |
255 __SET_VAR(__%(name)s_notifier->,CODE,,__STRING_LITERAL(%(onchangelen)d,%(onchangecode)s)); |
320 __%(name)s_onchange_count = __%(name)s_rbuffer_written; |
|
321 __%(name)s_onchange_firstval = __%(name)s_rbuffer_firstval; |
|
322 __%(name)s_onchange_lastval = __%(name)s_rbuffer_lastval; |
|
323 /* mark variable as unchanged */ |
|
324 __%(name)s_rbuffer_written = 0; |
|
325 AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); |
|
326 |
256 """ |
327 """ |
257 vardec = "\n".join([(vardecfmt + vardeconchangefmt |
328 vardec = "\n".join([(vardecfmt + vardeconchangefmt |
258 if varinfo["onchange"] else vardecfmt) % varinfo |
329 if varinfo["onchange"] else vardecfmt) % varinfo |
259 for varinfo in varinfos]) |
330 for varinfo in varinfos]) |
260 varret = "\n".join([varretfmt % varinfo for varinfo in varinfos]) |
331 varret = "\n".join([varretfmt % varinfo for varinfo in varinfos]) |
261 varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else |
332 varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else |
262 varpubfmt) % varinfo |
333 varpubfmt) % varinfo |
263 for varinfo in varinfos]) |
334 for varinfo in varinfos]) |
264 varinit = "\n".join([varinitonchangefmt % |
335 varcollectchange = "\n".join([varcollectchangefmt % varinfo |
265 dict(onchangelen=len(varinfo["onchangecode"]), **varinfo) |
|
266 for varinfo in varinfos if varinfo["onchange"]]) |
336 for varinfo in varinfos if varinfo["onchange"]]) |
|
337 |
|
338 pysafe_pypoll_code = "On_"+pyextname+"_Change()" |
267 |
339 |
268 loc_dict = { |
340 loc_dict = { |
269 "vardec": vardec, |
341 "vardec": vardec, |
270 "varinit": varinit, |
|
271 "varret": varret, |
342 "varret": varret, |
272 "varpub": varpub, |
343 "varpub": varpub, |
273 "location_str": location_str, |
344 "location_str": location_str, |
|
345 "pysafe_pypoll_code": '"'+pysafe_pypoll_code+'"', |
|
346 "pysafe_pypoll_code_len": len(pysafe_pypoll_code), |
|
347 "varcollectchange": varcollectchange, |
|
348 "onchange_var_count": onchange_var_count |
274 } |
349 } |
275 |
350 |
276 # TODO : use config name obtained from model instead of default |
351 # TODO : use config name obtained from model instead of default |
277 # "config.h". User cannot change config name, but project imported |
352 # "config.h". User cannot change config name, but project imported |
278 # or created in older beremiz vesion could use different name. |
353 # or created in older beremiz vesion could use different name. |
284 #include "iec_types_all.h" |
359 #include "iec_types_all.h" |
285 #include "POUS.h" |
360 #include "POUS.h" |
286 #include "config.h" |
361 #include "config.h" |
287 #include "beremiz.h" |
362 #include "beremiz.h" |
288 |
363 |
|
364 PYTHON_POLL* __%(location_str)s_notifier; |
|
365 |
289 /* User variables reference */ |
366 /* User variables reference */ |
290 %(vardec)s |
367 %(vardec)s |
291 |
368 |
292 /* Beremiz confnode functions */ |
369 /* Beremiz confnode functions */ |
293 int __init_%(location_str)s(int argc,char **argv){ |
370 int __init_%(location_str)s(int argc,char **argv){ |
294 %(varinit)s |
371 __%(location_str)s_notifier = __GET_GLOBAL_ON_%(location_str)s_CHANGE(); |
|
372 __SET_VAR(__%(location_str)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE)); |
|
373 __SET_VAR(__%(location_str)s_notifier->,CODE,,__STRING_LITERAL(%(pysafe_pypoll_code_len)d,%(pysafe_pypoll_code)s)); |
|
374 |
295 return 0; |
375 return 0; |
296 } |
376 } |
297 |
377 |
298 void __cleanup_%(location_str)s(void){ |
378 void __cleanup_%(location_str)s(void){ |
299 } |
379 } |
300 |
380 |
301 void __retrieve_%(location_str)s(void){ |
381 void __retrieve_%(location_str)s(void){ |
302 %(varret)s |
382 %(varret)s |
303 } |
383 } |
304 |
384 |
|
385 static int passing_changes_to_python = 0; |
305 void __publish_%(location_str)s(void){ |
386 void __publish_%(location_str)s(void){ |
|
387 int some_change_found = 0; |
306 %(varpub)s |
388 %(varpub)s |
307 } |
389 passing_changes_to_python |= some_change_found; |
|
390 // call python part if there was at least a change |
|
391 if(passing_changes_to_python){ |
|
392 PYTHON_POLL_body__(__%(location_str)s_notifier); |
|
393 passing_changes_to_python &= !(__GET_VAR(__%(location_str)s_notifier->ACK,)); |
|
394 } |
|
395 } |
|
396 |
|
397 void* PySafeGetChanges_%(location_str)s(void){ |
|
398 %(varcollectchange)s |
|
399 } |
|
400 |
308 """ % loc_dict |
401 """ % loc_dict |
309 |
402 |
310 Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str) |
403 Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str) |
311 pycfile = open(Gen_PyCfile_path, 'w') |
404 pycfile = open(Gen_PyCfile_path, 'w') |
312 pycfile.write(PyCFileContent) |
405 pycfile.write(PyCFileContent) |