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