1 import os |
1 import os |
2 from PLCControler import UndoBuffer |
2 from PLCControler import UndoBuffer |
3 from PythonEditor import PythonEditor |
3 from PythonEditor import PythonEditor |
4 |
4 |
5 from xml.dom import minidom |
5 from xml.dom import minidom |
6 from xmlclass import * |
6 from xmlclass import GenerateClassesFromXSD |
7 import cPickle |
7 import cPickle |
8 |
8 |
|
9 from CodeFileTreeNode import CodeFile |
|
10 |
9 PythonClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "py_ext_xsd.xsd")) |
11 PythonClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "py_ext_xsd.xsd")) |
10 |
12 |
11 class PythonFileCTNMixin: |
13 class PythonFileCTNMixin(CodeFile): |
12 |
14 |
|
15 CODEFILE_NAME = "PyFile" |
|
16 SECTIONS_NAMES = [ |
|
17 "globals", |
|
18 "init", |
|
19 "cleanup", |
|
20 "start", |
|
21 "stop"] |
13 EditorType = PythonEditor |
22 EditorType = PythonEditor |
14 |
23 |
15 def __init__(self): |
24 def __init__(self): |
|
25 CodeFile.__init__(self) |
16 |
26 |
17 filepath = self.PythonFileName() |
27 filepath = self.PythonFileName() |
18 |
28 |
19 self.PythonCode = PythonClasses["Python"]() |
29 python_code = PythonClasses["Python"]() |
20 if os.path.isfile(filepath): |
30 if os.path.isfile(filepath): |
21 xmlfile = open(filepath, 'r') |
31 xmlfile = open(filepath, 'r') |
22 tree = minidom.parse(xmlfile) |
32 tree = minidom.parse(xmlfile) |
23 xmlfile.close() |
33 xmlfile.close() |
24 |
34 |
25 for child in tree.childNodes: |
35 for child in tree.childNodes: |
26 if child.nodeType == tree.ELEMENT_NODE and child.nodeName == "Python": |
36 if child.nodeType == tree.ELEMENT_NODE and child.nodeName == "Python": |
27 self.PythonCode.loadXMLTree(child, ["xmlns", "xmlns:xsi", "xsi:schemaLocation"]) |
37 python_code.loadXMLTree(child, ["xmlns", "xmlns:xsi", "xsi:schemaLocation"]) |
28 self.CreatePythonBuffer(True) |
38 self.CodeFile.globals.settext(python_code.gettext()) |
29 else: |
39 os.remove(filepath) |
30 self.CreatePythonBuffer(False) |
40 self.CreateCodeFileBuffer(False) |
31 self.OnCTNSave() |
41 self.OnCTNSave() |
32 |
42 |
|
43 def CodeFileName(self): |
|
44 return os.path.join(self.CTNPath(), "pyfile.xml") |
|
45 |
33 def PythonFileName(self): |
46 def PythonFileName(self): |
34 return os.path.join(self.CTNPath(), "py_ext.xml") |
47 return os.path.join(self.CTNPath(), "py_ext.xml") |
35 |
48 |
36 def GetFilename(self): |
49 PreSectionsTexts = {} |
37 if self.PythonBuffer.IsCurrentSaved(): |
50 PostSectionsTexts = {} |
38 return "py_ext" |
51 def GetSection(self,section): |
|
52 return self.PreSectionsTexts.get(section,"") + "\n" + \ |
|
53 getattr(self.CodeFile, section).gettext() + "\n" + \ |
|
54 self.PostSectionsTexts.get(section,"") |
|
55 |
|
56 |
|
57 def CTNGenerate_C(self, buildpath, locations): |
|
58 # location string for that CTN |
|
59 location_str = "_".join(map(lambda x:str(x), |
|
60 self.GetCurrentLocation())) |
|
61 configname = self.GetCTRoot().GetProjectConfigNames()[0] |
|
62 |
|
63 |
|
64 # python side PLC global variables access stub |
|
65 globalstubs = "\n".join(["""\ |
|
66 _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\ |
|
67 TypeTranslator["%(IECtype)s"] |
|
68 _PySafeGetPLCGlob_%(name)s = PLCBinary.__SafeGetPLCGlob_%(name)s |
|
69 _PySafeGetPLCGlob_%(name)s.restype = None |
|
70 _PySafeGetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)] |
|
71 _PySafeSetPLCGlob_%(name)s = PLCBinary.__SafeSetPLCGlob_%(name)s |
|
72 _PySafeSetPLCGlob_%(name)s.restype = None |
|
73 _PySafeSetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)] |
|
74 """ % { "name": variable.getname(), |
|
75 "configname": configname.upper(), |
|
76 "uppername": variable.getname().upper(), |
|
77 "IECtype": variable.gettype()} |
|
78 for variable in self.CodeFile.variables.variable]) |
|
79 |
|
80 # Runtime calls (start, stop, init, and cleanup) |
|
81 rtcalls = "" |
|
82 for section in self.SECTIONS_NAMES: |
|
83 if section != "globals": |
|
84 rtcalls += "def _runtime_%s_%s():\n" % (location_str, section) |
|
85 sectiontext = self.GetSection(section).strip() |
|
86 if sectiontext: |
|
87 rtcalls += ' ' + \ |
|
88 sectiontext.replace('\n', '\n ')+"\n\n" |
|
89 else: |
|
90 rtcalls += " pass\n\n" |
|
91 |
|
92 globalsection = self.GetSection("globals") |
|
93 |
|
94 PyFileContent = """\ |
|
95 #!/usr/bin/env python |
|
96 # -*- coding: utf-8 -*- |
|
97 ## Code generated by Beremiz python mixin confnode |
|
98 ## |
|
99 |
|
100 ## Code for PLC global variable access |
|
101 from targets.typemapping import TypeTranslator |
|
102 import ctypes |
|
103 %(globalstubs)s |
|
104 |
|
105 ## User code in "global" scope |
|
106 %(globalsection)s |
|
107 |
|
108 ## Beremiz python runtime calls |
|
109 %(rtcalls)s |
|
110 |
|
111 """ % locals() |
|
112 |
|
113 # write generated content to python file |
|
114 runtimefile_path = os.path.join(buildpath, |
|
115 "runtime_%s.py"%location_str) |
|
116 runtimefile = open(runtimefile_path, 'w') |
|
117 runtimefile.write(PyFileContent.encode('utf-8')) |
|
118 runtimefile.close() |
|
119 |
|
120 # C code for safe global variables access |
|
121 |
|
122 vardecfmt = """\ |
|
123 extern __IEC_%(IECtype)s_t %(configname)s__%(uppername)s; |
|
124 IEC_%(IECtype)s __%(name)s_rbuffer = __INIT_%(IECtype)s; |
|
125 IEC_%(IECtype)s __%(name)s_wbuffer; |
|
126 long __%(name)s_rlock = 0; |
|
127 long __%(name)s_wlock = 0; |
|
128 int __%(name)s_wbuffer_written = 0; |
|
129 void __SafeGetPLCGlob_%(name)s(IEC_%(IECtype)s *pvalue){ |
|
130 while(AtomicCompareExchange(&__%(name)s_rlock, 0, 1)); |
|
131 *pvalue = __%(name)s_rbuffer; |
|
132 AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); |
|
133 } |
|
134 void __SafeSetPLCGlob_%(name)s(IEC_%(IECtype)s *value){ |
|
135 while(AtomicCompareExchange(&__%(name)s_wlock, 0, 1)); |
|
136 __%(name)s_wbuffer = *value; |
|
137 __%(name)s_wbuffer_written = 1; |
|
138 AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0); |
|
139 } |
|
140 |
|
141 """ |
|
142 varretfmt = """\ |
|
143 if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){ |
|
144 if(__%(name)s_wbuffer_written == 1){ |
|
145 %(configname)s__%(uppername)s.value = __%(name)s_wbuffer; |
|
146 __%(name)s_wbuffer_written = 0; |
|
147 } |
|
148 AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0); |
|
149 } |
|
150 """ |
|
151 varpubfmt = """\ |
|
152 if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){ |
|
153 __%(name)s_rbuffer = %(configname)s__%(uppername)s.value; |
|
154 AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0); |
|
155 } |
|
156 """ |
|
157 |
|
158 var_str = map("\n".join, zip(*[ |
|
159 map(lambda f : f % varinfo, |
|
160 (vardecfmt, varretfmt, varpubfmt)) |
|
161 for varinfo in map(lambda variable : { |
|
162 "name": variable.getname(), |
|
163 "configname": configname.upper(), |
|
164 "uppername": variable.getname().upper(), |
|
165 "IECtype": variable.gettype()}, |
|
166 self.CodeFile.variables.variable)])) |
|
167 if len(var_str) > 0: |
|
168 vardec, varret, varpub = var_str |
39 else: |
169 else: |
40 return "~py_ext~" |
170 vardec = varret = varpub = "" |
41 |
171 |
42 def SetPythonCode(self, text): |
172 PyCFileContent = """\ |
43 self.PythonCode.settext(text) |
173 /* |
44 |
174 * Code generated by Beremiz py_ext confnode |
45 def GetPythonCode(self): |
175 * for safe global variables access |
46 return self.PythonCode.gettext() |
176 */ |
47 |
177 #include "iec_types_all.h" |
48 def CTNTestModified(self): |
178 #include "beremiz.h" |
49 return self.ChangesToSave or not self.PythonIsSaved() |
179 |
50 |
180 /* User variables reference */ |
51 def OnCTNSave(self): |
181 %(vardec)s |
52 filepath = self.PythonFileName() |
182 |
53 |
183 /* Beremiz confnode functions */ |
54 text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" |
184 int __init_%(location_str)s(int argc,char **argv){ |
55 extras = {"xmlns":"http://www.w3.org/2001/XMLSchema", |
185 return 0; |
56 "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", |
186 } |
57 "xsi:schemaLocation" : "py_ext_xsd.xsd"} |
187 |
58 text += self.PythonCode.generateXMLText("Python", 0, extras) |
188 void __cleanup_%(location_str)s(void){ |
59 |
189 } |
60 xmlfile = open(filepath,"w") |
190 |
61 xmlfile.write(text.encode("utf-8")) |
191 void __retrieve_%(location_str)s(void){ |
62 xmlfile.close() |
192 %(varret)s |
63 |
193 } |
64 self.MarkPythonAsSaved() |
194 |
65 return True |
195 void __publish_%(location_str)s(void){ |
66 |
196 %(varpub)s |
67 #------------------------------------------------------------------------------- |
197 } |
68 # Current Buffering Management Functions |
198 """ % locals() |
69 #------------------------------------------------------------------------------- |
199 |
70 |
200 Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c"%location_str) |
71 """ |
201 pycfile = open(Gen_PyCfile_path,'w') |
72 Return a copy of the project |
202 pycfile.write(PyCFileContent) |
73 """ |
203 pycfile.close() |
74 def Copy(self, model): |
204 |
75 return cPickle.loads(cPickle.dumps(model)) |
205 matiec_flags = '"-I%s"'%os.path.abspath( |
76 |
206 self.GetCTRoot().GetIECLibPath()) |
77 def CreatePythonBuffer(self, saved): |
207 |
78 self.Buffering = False |
208 return ([(Gen_PyCfile_path, matiec_flags)], |
79 self.PythonBuffer = UndoBuffer(cPickle.dumps(self.PythonCode), saved) |
209 "", |
80 |
210 True, |
81 def BufferPython(self): |
211 ("runtime_%s.py"%location_str, file(runtimefile_path,"rb"))) |
82 self.PythonBuffer.Buffering(cPickle.dumps(self.PythonCode)) |
212 |
83 |
|
84 def StartBuffering(self): |
|
85 self.Buffering = True |
|
86 |
|
87 def EndBuffering(self): |
|
88 if self.Buffering: |
|
89 self.PythonBuffer.Buffering(cPickle.dumps(self.PythonCode)) |
|
90 self.Buffering = False |
|
91 |
|
92 def MarkPythonAsSaved(self): |
|
93 self.EndBuffering() |
|
94 self.PythonBuffer.CurrentSaved() |
|
95 |
|
96 def PythonIsSaved(self): |
|
97 return self.PythonBuffer.IsCurrentSaved() and not self.Buffering |
|
98 |
|
99 def LoadPrevious(self): |
|
100 self.EndBuffering() |
|
101 self.PythonCode = cPickle.loads(self.PythonBuffer.Previous()) |
|
102 |
|
103 def LoadNext(self): |
|
104 self.PythonCode = cPickle.loads(self.PythonBuffer.Next()) |
|
105 |
|
106 def GetBufferState(self): |
|
107 first = self.PythonBuffer.IsFirst() and not self.Buffering |
|
108 last = self.PythonBuffer.IsLast() |
|
109 return not first, not last |
|
110 |
|