5 |
5 |
6 from CodeFileTreeNode import CodeFile |
6 from CodeFileTreeNode import CodeFile |
7 from PythonEditor import PythonEditor |
7 from PythonEditor import PythonEditor |
8 |
8 |
9 class PythonFileCTNMixin(CodeFile): |
9 class PythonFileCTNMixin(CodeFile): |
10 |
10 |
11 CODEFILE_NAME = "PyFile" |
11 CODEFILE_NAME = "PyFile" |
12 SECTIONS_NAMES = [ |
12 SECTIONS_NAMES = [ |
13 "globals", |
13 "globals", |
14 "init", |
14 "init", |
15 "cleanup", |
15 "cleanup", |
16 "start", |
16 "start", |
17 "stop"] |
17 "stop"] |
18 EditorType = PythonEditor |
18 EditorType = PythonEditor |
19 |
19 |
20 def __init__(self): |
20 def __init__(self): |
21 CodeFile.__init__(self) |
21 CodeFile.__init__(self) |
22 |
22 |
23 filepath = self.PythonFileName() |
23 filepath = self.PythonFileName() |
24 |
24 |
25 if os.path.isfile(filepath): |
25 if os.path.isfile(filepath): |
26 PythonParser = GenerateParserFromXSD( |
26 PythonParser = GenerateParserFromXSD( |
27 os.path.join(os.path.dirname(__file__), "py_ext_xsd.xsd")) |
27 os.path.join(os.path.dirname(__file__), "py_ext_xsd.xsd")) |
28 |
28 |
29 xmlfile = open(filepath, 'r') |
29 xmlfile = open(filepath, 'r') |
30 pythonfile_xml = xmlfile.read() |
30 pythonfile_xml = xmlfile.read() |
31 xmlfile.close() |
31 xmlfile.close() |
32 |
32 |
33 pythonfile_xml = pythonfile_xml.replace( |
33 pythonfile_xml = pythonfile_xml.replace( |
34 'xmlns="http://www.w3.org/2001/XMLSchema"', |
34 'xmlns="http://www.w3.org/2001/XMLSchema"', |
35 'xmlns:xhtml="http://www.w3.org/1999/xhtml"') |
35 'xmlns:xhtml="http://www.w3.org/1999/xhtml"') |
36 for cre, repl in [ |
36 for cre, repl in [ |
37 (re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["), |
37 (re.compile("(?<!<xhtml:p>)(?:<!\[CDATA\[)"), "<xhtml:p><![CDATA["), |
38 (re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]: |
38 (re.compile("(?:]]>)(?!</xhtml:p>)"), "]]></xhtml:p>")]: |
39 pythonfile_xml = cre.sub(repl, pythonfile_xml) |
39 pythonfile_xml = cre.sub(repl, pythonfile_xml) |
40 |
40 |
41 try: |
41 try: |
42 python_code, error = PythonParser.LoadXMLString(pythonfile_xml) |
42 python_code, error = PythonParser.LoadXMLString(pythonfile_xml) |
43 if error is None: |
43 if error is None: |
44 self.CodeFile.globals.setanyText(python_code.getanyText()) |
44 self.CodeFile.globals.setanyText(python_code.getanyText()) |
45 os.remove(filepath) |
45 os.remove(filepath) |
46 self.CreateCodeFileBuffer(False) |
46 self.CreateCodeFileBuffer(False) |
47 self.OnCTNSave() |
47 self.OnCTNSave() |
48 except Exception, exc: |
48 except Exception, exc: |
49 error = unicode(exc) |
49 error = unicode(exc) |
50 |
50 |
51 if error is not None: |
51 if error is not None: |
52 self.GetCTRoot().logger.write_error( |
52 self.GetCTRoot().logger.write_error( |
53 _("Couldn't import old %s file.") % CTNName) |
53 _("Couldn't import old %s file.") % self.CTNName()) |
54 |
54 |
55 def CodeFileName(self): |
55 def CodeFileName(self): |
56 return os.path.join(self.CTNPath(), "pyfile.xml") |
56 return os.path.join(self.CTNPath(), "pyfile.xml") |
57 |
57 |
58 def PythonFileName(self): |
58 def PythonFileName(self): |
59 return os.path.join(self.CTNPath(), "py_ext.xml") |
59 return os.path.join(self.CTNPath(), "py_ext.xml") |
60 |
60 |
61 PreSectionsTexts = {} |
61 PreSectionsTexts = {} |
62 PostSectionsTexts = {} |
62 PostSectionsTexts = {} |
63 def GetSection(self,section): |
63 def GetSection(self,section): |
64 return self.PreSectionsTexts.get(section,"") + "\n" + \ |
64 return self.PreSectionsTexts.get(section,"") + "\n" + \ |
65 getattr(self.CodeFile, section).getanyText() + "\n" + \ |
65 getattr(self.CodeFile, section).getanyText() + "\n" + \ |
66 self.PostSectionsTexts.get(section,"") |
66 self.PostSectionsTexts.get(section,"") |
67 |
|
68 |
67 |
69 def CTNGenerate_C(self, buildpath, locations): |
68 def CTNGenerate_C(self, buildpath, locations): |
70 # location string for that CTN |
69 # location string for that CTN |
71 location_str = "_".join(map(lambda x:str(x), |
70 location_str = "_".join(map(lambda x:str(x), |
72 self.GetCurrentLocation())) |
71 self.GetCurrentLocation())) |
73 configname = self.GetCTRoot().GetProjectConfigNames()[0] |
72 configname = self.GetCTRoot().GetProjectConfigNames()[0] |
74 |
73 |
75 |
74 pyextname = self.CTNName() |
|
75 varinfos = map(lambda variable : { |
|
76 "name": variable.getname(), |
|
77 "desc" : repr(variable.getdesc()), |
|
78 "onchangecode" : '"'+variable.getonchange()+\ |
|
79 "('"+variable.getname()+"')\"" \ |
|
80 if variable.getonchange() else '""', |
|
81 "onchange" : repr(variable.getonchange()) \ |
|
82 if variable.getonchange() else None, |
|
83 "opts" : repr(variable.getopts()), |
|
84 "configname" : configname.upper(), |
|
85 "uppername" : variable.getname().upper(), |
|
86 "IECtype" : variable.gettype(), |
|
87 "pyextname" :pyextname}, |
|
88 self.CodeFile.variables.variable) |
76 # python side PLC global variables access stub |
89 # python side PLC global variables access stub |
77 globalstubs = "\n".join(["""\ |
90 globalstubs = "\n".join(["""\ |
78 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\ |
91 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\ |
79 TypeTranslator["%(IECtype)s"] |
92 TypeTranslator["%(IECtype)s"] |
80 _PySafeGetPLCGlob_%(name)s = PLCBinary.__SafeGetPLCGlob_%(name)s |
93 _PySafeGetPLCGlob_%(name)s = PLCBinary.__SafeGetPLCGlob_%(name)s |
81 _PySafeGetPLCGlob_%(name)s.restype = None |
94 _PySafeGetPLCGlob_%(name)s.restype = None |
82 _PySafeGetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)] |
95 _PySafeGetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)] |
83 _PySafeSetPLCGlob_%(name)s = PLCBinary.__SafeSetPLCGlob_%(name)s |
96 _PySafeSetPLCGlob_%(name)s = PLCBinary.__SafeSetPLCGlob_%(name)s |
84 _PySafeSetPLCGlob_%(name)s.restype = None |
97 _PySafeSetPLCGlob_%(name)s.restype = None |
85 _PySafeSetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)] |
98 _PySafeSetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)] |
86 """ % { "name": variable.getname(), |
99 _%(pyextname)sGlobalsDesc.append(( |
87 "configname": configname.upper(), |
100 "%(name)s", |
88 "uppername": variable.getname().upper(), |
101 "%(IECtype)s", |
89 "IECtype": variable.gettype()} |
102 %(desc)s, |
90 for variable in self.CodeFile.variables.variable]) |
103 %(onchange)s, |
|
104 %(opts)s)) |
|
105 """ % varinfo |
|
106 for varinfo in varinfos]) |
91 |
107 |
92 # Runtime calls (start, stop, init, and cleanup) |
108 # Runtime calls (start, stop, init, and cleanup) |
93 rtcalls = "" |
109 rtcalls = "" |
94 for section in self.SECTIONS_NAMES: |
110 for section in self.SECTIONS_NAMES: |
95 if section != "globals": |
111 if section != "globals": |
99 rtcalls += ' ' + \ |
115 rtcalls += ' ' + \ |
100 sectiontext.replace('\n', '\n ')+"\n\n" |
116 sectiontext.replace('\n', '\n ')+"\n\n" |
101 else: |
117 else: |
102 rtcalls += " pass\n\n" |
118 rtcalls += " pass\n\n" |
103 |
119 |
104 globalsection = self.GetSection("globals") |
120 globalsection = self.GetSection("globals") |
105 |
121 |
106 PyFileContent = """\ |
122 PyFileContent = """\ |
107 #!/usr/bin/env python |
123 #!/usr/bin/env python |
108 # -*- coding: utf-8 -*- |
124 # -*- coding: utf-8 -*- |
109 ## Code generated by Beremiz python mixin confnode |
125 ## Code generated by Beremiz python mixin confnode |
110 ## |
126 ## |
111 |
127 |
112 ## Code for PLC global variable access |
128 ## Code for PLC global variable access |
113 from targets.typemapping import TypeTranslator |
129 from targets.typemapping import TypeTranslator |
114 import ctypes |
130 import ctypes |
|
131 _%(pyextname)sGlobalsDesc = [] |
|
132 __ext_name__ = "%(pyextname)s" |
|
133 PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc )) |
115 %(globalstubs)s |
134 %(globalstubs)s |
116 |
135 |
117 ## User code in "global" scope |
136 ## User code in "global" scope |
118 %(globalsection)s |
137 %(globalsection)s |
119 |
138 |
120 ## Beremiz python runtime calls |
139 ## Beremiz python runtime calls |
121 %(rtcalls)s |
140 %(rtcalls)s |
122 |
141 |
|
142 del __ext_name__ |
|
143 |
123 """ % locals() |
144 """ % locals() |
124 |
145 |
125 # write generated content to python file |
146 # write generated content to python file |
126 runtimefile_path = os.path.join(buildpath, |
147 runtimefile_path = os.path.join(buildpath, |
127 "runtime_%s.py"%location_str) |
148 "runtime_%s.py"%location_str) |
128 runtimefile = open(runtimefile_path, 'w') |
149 runtimefile = open(runtimefile_path, 'w') |
129 runtimefile.write(PyFileContent.encode('utf-8')) |
150 runtimefile.write(PyFileContent.encode('utf-8')) |
130 runtimefile.close() |
151 runtimefile.close() |
131 |
152 |
132 # C code for safe global variables access |
153 # C code for safe global variables access |
133 |
154 |
134 vardecfmt = """\ |
155 vardecfmt = """\ |
135 extern __IEC_%(IECtype)s_t %(configname)s__%(uppername)s; |
156 extern __IEC_%(IECtype)s_t %(configname)s__%(uppername)s; |
136 IEC_%(IECtype)s __%(name)s_rbuffer = __INIT_%(IECtype)s; |
157 IEC_%(IECtype)s __%(name)s_rbuffer = __INIT_%(IECtype)s; |
137 IEC_%(IECtype)s __%(name)s_wbuffer; |
158 IEC_%(IECtype)s __%(name)s_wbuffer; |
138 long __%(name)s_rlock = 0; |
159 long __%(name)s_rlock = 0; |
149 __%(name)s_wbuffer_written = 1; |
170 __%(name)s_wbuffer_written = 1; |
150 AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0); |
171 AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0); |
151 } |
172 } |
152 |
173 |
153 """ |
174 """ |
|
175 |
|
176 vardeconchangefmt = """\ |
|
177 PYTHON_POLL* __%(name)s_notifier; |
|
178 """ |
|
179 |
154 varretfmt = """\ |
180 varretfmt = """\ |
155 if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){ |
181 if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){ |
156 if(__%(name)s_wbuffer_written == 1){ |
182 if(__%(name)s_wbuffer_written == 1){ |
157 %(configname)s__%(uppername)s.value = __%(name)s_wbuffer; |
183 %(configname)s__%(uppername)s.value = __%(name)s_wbuffer; |
158 __%(name)s_wbuffer_written = 0; |
184 __%(name)s_wbuffer_written = 0; |
159 } |
185 } |
160 AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0); |
186 AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0); |
161 } |
187 } |
162 """ |
188 """ |
163 varpubfmt = """\ |
189 varpubfmt = """\ |
164 if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){ |
190 if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){ |
165 __%(name)s_rbuffer = %(configname)s__%(uppername)s.value; |
191 __%(name)s_rbuffer = __GET_VAR(%(configname)s__%(uppername)s); |
166 AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); |
192 AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); |
167 } |
193 } |
168 """ |
194 """ |
169 |
195 |
170 var_str = map("\n".join, zip(*[ |
196 varpubonchangefmt = """\ |
171 map(lambda f : f % varinfo, |
197 if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){ |
172 (vardecfmt, varretfmt, varpubfmt)) |
198 IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s); |
173 for varinfo in map(lambda variable : { |
199 if(__%(name)s_rbuffer != tmp){ |
174 "name": variable.getname(), |
200 __%(name)s_rbuffer = %(configname)s__%(uppername)s.value; |
175 "configname": configname.upper(), |
201 PYTHON_POLL_body__(__%(name)s_notifier); |
176 "uppername": variable.getname().upper(), |
202 } |
177 "IECtype": variable.gettype()}, |
203 AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); |
178 self.CodeFile.variables.variable)])) |
204 } |
179 if len(var_str) > 0: |
205 """ |
180 vardec, varret, varpub = var_str |
206 varinitonchangefmt = """\ |
181 else: |
207 __%(name)s_notifier = __GET_GLOBAL_ON%(uppername)sCHANGE(); |
182 vardec = varret = varpub = "" |
208 __SET_VAR(__%(name)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE)); |
183 |
209 __SET_VAR(__%(name)s_notifier->,CODE,,__STRING_LITERAL(%(onchangelen)d,%(onchangecode)s)); |
|
210 """ |
|
211 vardec = "\n".join([(vardecfmt + vardeconchangefmt |
|
212 if varinfo["onchange"] else vardecfmt)% varinfo |
|
213 for varinfo in varinfos]) |
|
214 varret = "\n".join([varretfmt % varinfo for varinfo in varinfos]) |
|
215 varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else |
|
216 varpubfmt) % varinfo |
|
217 for varinfo in varinfos]) |
|
218 varinit = "\n".join([varinitonchangefmt % dict( |
|
219 onchangelen = len(varinfo["onchangecode"]),**varinfo) |
|
220 for varinfo in varinfos if varinfo["onchange"]]) |
|
221 |
|
222 # TODO : use config name obtained from model instead of default |
|
223 # "config.h". User cannot change config name, but project imported |
|
224 # or created in older beremiz vesion could use different name. |
184 PyCFileContent = """\ |
225 PyCFileContent = """\ |
185 /* |
226 /* |
186 * Code generated by Beremiz py_ext confnode |
227 * Code generated by Beremiz py_ext confnode |
187 * for safe global variables access |
228 * for safe global variables access |
188 */ |
229 */ |
189 #include "iec_types_all.h" |
230 #include "iec_types_all.h" |
|
231 #include "POUS.h" |
|
232 #include "config.h" |
190 #include "beremiz.h" |
233 #include "beremiz.h" |
191 |
234 |
192 /* User variables reference */ |
235 /* User variables reference */ |
193 %(vardec)s |
236 %(vardec)s |
194 |
237 |
195 /* Beremiz confnode functions */ |
238 /* Beremiz confnode functions */ |
196 int __init_%(location_str)s(int argc,char **argv){ |
239 int __init_%(location_str)s(int argc,char **argv){ |
|
240 %(varinit)s |
197 return 0; |
241 return 0; |
198 } |
242 } |
199 |
243 |
200 void __cleanup_%(location_str)s(void){ |
244 void __cleanup_%(location_str)s(void){ |
201 } |
245 } |