1 import wx |
1 import wx |
2 import wx.stc as stc |
2 import os |
3 import os, sys, shutil |
|
4 from CppSTC import CppSTC |
|
5 from plugger import PlugTemplate |
3 from plugger import PlugTemplate |
6 import tempfile |
4 from CFileEditor import CFileEditor |
|
5 |
|
6 from xml.dom import minidom |
|
7 from xmlclass import * |
|
8 import cPickle |
|
9 |
|
10 CFileClasses = GenerateClassesFromXSD(os.path.join(os.path.dirname(__file__), "cext_xsd.xsd")) |
|
11 |
|
12 #------------------------------------------------------------------------------- |
|
13 # Undo Buffer for CFile |
|
14 #------------------------------------------------------------------------------- |
|
15 |
|
16 # Length of the buffer |
|
17 UNDO_BUFFER_LENGTH = 20 |
|
18 |
|
19 """ |
|
20 Class implementing a buffer of changes made on the current editing model |
|
21 """ |
|
22 class UndoBuffer: |
|
23 |
|
24 # Constructor initialising buffer |
|
25 def __init__(self, currentstate, issaved = False): |
|
26 self.Buffer = [] |
|
27 self.CurrentIndex = -1 |
|
28 self.MinIndex = -1 |
|
29 self.MaxIndex = -1 |
|
30 # if current state is defined |
|
31 if currentstate: |
|
32 self.CurrentIndex = 0 |
|
33 self.MinIndex = 0 |
|
34 self.MaxIndex = 0 |
|
35 # Initialising buffer with currentstate at the first place |
|
36 for i in xrange(UNDO_BUFFER_LENGTH): |
|
37 if i == 0: |
|
38 self.Buffer.append(currentstate) |
|
39 else: |
|
40 self.Buffer.append(None) |
|
41 # Initialising index of state saved |
|
42 if issaved: |
|
43 self.LastSave = 0 |
|
44 else: |
|
45 self.LastSave = -1 |
|
46 |
|
47 # Add a new state in buffer |
|
48 def Buffering(self, currentstate): |
|
49 self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH |
|
50 self.Buffer[self.CurrentIndex] = currentstate |
|
51 # Actualising buffer limits |
|
52 self.MaxIndex = self.CurrentIndex |
|
53 if self.MinIndex == self.CurrentIndex: |
|
54 # If the removed state was the state saved, there is no state saved in the buffer |
|
55 if self.LastSave == self.MinIndex: |
|
56 self.LastSave = -1 |
|
57 self.MinIndex = (self.MinIndex + 1) % UNDO_BUFFER_LENGTH |
|
58 self.MinIndex = max(self.MinIndex, 0) |
|
59 |
|
60 # Return current state of buffer |
|
61 def Current(self): |
|
62 return self.Buffer[self.CurrentIndex] |
|
63 |
|
64 # Change current state to previous in buffer and return new current state |
|
65 def Previous(self): |
|
66 if self.CurrentIndex != self.MinIndex: |
|
67 self.CurrentIndex = (self.CurrentIndex - 1) % UNDO_BUFFER_LENGTH |
|
68 return self.Buffer[self.CurrentIndex] |
|
69 return None |
|
70 |
|
71 # Change current state to next in buffer and return new current state |
|
72 def Next(self): |
|
73 if self.CurrentIndex != self.MaxIndex: |
|
74 self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH |
|
75 return self.Buffer[self.CurrentIndex] |
|
76 return None |
|
77 |
|
78 # Return True if current state is the first in buffer |
|
79 def IsFirst(self): |
|
80 return self.CurrentIndex == self.MinIndex |
|
81 |
|
82 # Return True if current state is the last in buffer |
|
83 def IsLast(self): |
|
84 return self.CurrentIndex == self.MaxIndex |
|
85 |
|
86 # Note that current state is saved |
|
87 def CurrentSaved(self): |
|
88 self.LastSave = self.CurrentIndex |
|
89 |
|
90 # Return True if current state is saved |
|
91 def IsCurrentSaved(self): |
|
92 return self.LastSave == self.CurrentIndex |
|
93 |
|
94 |
|
95 TYPECONVERSION = {"BOOL" : "X", "SINT" : "B", "INT" : "W", "DINT" : "D", "LINT" : "L", |
|
96 "USINT" : "B", "UINT" : "W", "UDINT" : "D", "ULINT" : "L", "REAL" : "D", "LREAL" : "L", |
|
97 "STRING" : "B", "BYTE" : "B", "WORD" : "W", "DWORD" : "D", "LWORD" : "L", "WSTRING" : "W"} |
7 |
98 |
8 class _Cfile: |
99 class _Cfile: |
9 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
100 XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?> |
10 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
101 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
11 <xsd:element name="C_Extension"> |
102 <xsd:element name="CExtension"> |
12 <xsd:complexType> |
103 <xsd:complexType> |
13 <xsd:attribute name="C_Files" type="xsd:string" use="optional" default="myfile.c"/> |
|
14 <xsd:attribute name="CFLAGS" type="xsd:string" use="required"/> |
104 <xsd:attribute name="CFLAGS" type="xsd:string" use="required"/> |
15 <xsd:attribute name="LDFLAGS" type="xsd:string" use="required"/> |
105 <xsd:attribute name="LDFLAGS" type="xsd:string" use="required"/> |
16 </xsd:complexType> |
106 </xsd:complexType> |
17 </xsd:element> |
107 </xsd:element> |
18 </xsd:schema> |
108 </xsd:schema> |
19 """ |
109 """ |
20 def __init__(self): |
110 def __init__(self): |
21 self.CheckCFilesExist() |
111 filepath = self.CFileName() |
22 |
112 |
23 def CheckCFilesExist(self): |
113 self.Buffering = False |
24 for CFile in self.CFileNames(): |
114 self.CFile = CFileClasses["CFile"]() |
25 if not os.path.isfile(CFile): |
115 self.CFileBuffer = UndoBuffer(self.Copy(self.CFile), False) |
26 f = open(CFile, 'w') |
116 if os.path.isfile(filepath): |
27 f.write("/*Empty*/") |
117 xmlfile = open(filepath, 'r') |
28 f.close() |
118 tree = minidom.parse(xmlfile) |
29 |
119 xmlfile.close() |
30 def CFileBaseNames(self): |
120 |
31 """ |
121 for child in tree.childNodes: |
32 Returns list of C files base names, out of C_Extension.C_Files, coma separated list |
122 if child.nodeType == tree.ELEMENT_NODE and child.nodeName == "CFile": |
33 """ |
123 self.CFile.loadXMLTree(child, ["xmlns", "xmlns:xsi", "xsi:schemaLocation"]) |
34 return map(str.strip,str(self.C_Extension.getC_Files()).split(',')) |
124 self.CFileBuffer = UndoBuffer(self.Copy(self.CFile), True) |
35 |
125 else: |
36 def CFileName(self, fn): |
126 self.OnPlugSave() |
37 return os.path.join(self.PlugPath(),fn) |
127 |
38 |
128 def CFileName(self): |
39 def CFileNames(self): |
129 return os.path.join(self.PlugPath(), "cfile.xml") |
40 """ |
130 |
41 Returns list of full C files paths, out of C_Extension.C_Files, coma separated list |
131 def GetFilename(self): |
42 """ |
132 if self.CFileBuffer.IsCurrentSaved(): |
43 return map(self.CFileName, self.CFileBaseNames()) |
133 return "cfile" |
44 |
134 else: |
45 def SetParamsAttribute(self, path, value, logger): |
135 return "~cfile~" |
46 """ |
136 |
47 Take actions if C_Files changed |
137 def GetBaseTypes(self): |
48 """ |
138 return self.GetPlugRoot().GetBaseTypes() |
49 # Get a C files list before changes |
139 |
50 oldnames = self.CFileNames() |
140 def GetDataTypes(self, basetypes = False): |
51 # Apply changes |
141 return self.GetPlugRoot().GetDataTypes(basetypes = basetypes) |
52 res = PlugTemplate.SetParamsAttribute(self, path, value, logger) |
142 |
53 # If changes was about C files, |
143 def GetSizeOfType(self, type): |
54 if path == "C_Extension.C_Files": |
144 return TYPECONVERSION[self.GetPlugRoot().GetBaseType(type)] |
55 # Create files if did not exist |
145 |
56 self.CheckCFilesExist() |
146 def SetVariables(self, variables): |
57 # Get new list |
147 self.CFile.variables.setvariable([]) |
58 newnames = self.CFileNames() |
148 for var in variables: |
59 # Move unused files into trash (temporary directory) |
149 variable = CFileClasses["variables_variable"]() |
60 for oldfile in oldnames: |
150 variable.setname(var["Name"]) |
61 if oldfile not in newnames: |
151 variable.settype(var["Type"]) |
62 # define new "trash" name |
152 variable.setclass(var["Class"]) |
63 trashname = os.path.join(tempfile.gettempdir(),os.path.basename(oldfile)) |
153 self.CFile.variables.appendvariable(variable) |
64 # move the file |
154 |
65 shutil.move(oldfile, trashname) |
155 def GetVariables(self): |
66 # warn user |
156 datas = [] |
67 logger.write_warning("\"%s\" moved to \"%s\"\n"%(oldfile, trashname)) |
157 for var in self.CFile.variables.getvariable(): |
68 return value, False |
158 datas.append({"Name" : var.getname(), "Type" : var.gettype(), "Class" : var.getclass()}) |
69 return res |
159 return datas |
70 |
160 |
71 _Views = {} |
161 def SetPartText(self, name, text): |
|
162 if name == "Includes": |
|
163 self.CFile.includes.settext(text) |
|
164 elif name == "Globals": |
|
165 self.CFile.globals.settext(text) |
|
166 elif name == "Init": |
|
167 self.CFile.initFunction.settext(text) |
|
168 elif name == "CleanUp": |
|
169 self.CFile.cleanUpFunction.settext(text) |
|
170 elif name == "Retrieve": |
|
171 self.CFile.retrieveFunction.settext(text) |
|
172 elif name == "Publish": |
|
173 self.CFile.publishFunction.settext(text) |
|
174 |
|
175 def GetPartText(self, name): |
|
176 if name == "Includes": |
|
177 return self.CFile.includes.gettext() |
|
178 elif name == "Globals": |
|
179 return self.CFile.globals.gettext() |
|
180 elif name == "Init": |
|
181 return self.CFile.initFunction.gettext() |
|
182 elif name == "CleanUp": |
|
183 return self.CFile.cleanUpFunction.gettext() |
|
184 elif name == "Retrieve": |
|
185 return self.CFile.retrieveFunction.gettext() |
|
186 elif name == "Publish": |
|
187 return self.CFile.publishFunction.gettext() |
|
188 return "" |
|
189 |
|
190 _View = None |
72 def _OpenView(self, logger): |
191 def _OpenView(self, logger): |
73 lst = self.CFileBaseNames() |
192 if not self._View: |
74 |
193 def _onclose(): |
75 dlg = wx.MultiChoiceDialog( self.GetPlugRoot().AppFrame, |
194 self._View = None |
76 "Choose C files to Edit :", |
195 def _onsave(): |
77 "Edit", lst) |
196 self.GetPlugRoot().SaveProject() |
78 |
197 self._View = CFileEditor(self.GetPlugRoot().AppFrame, self) |
79 if (dlg.ShowModal() == wx.ID_OK): |
198 self._View._onclose = _onclose |
80 selections = dlg.GetSelections() |
199 self._View._onsave = _onsave |
81 for selected in [lst[x] for x in selections]: |
200 self._View.Show() |
82 if selected not in self._Views: |
|
83 # keep track of selected name as static for later close |
|
84 def _onclose(evt, sel = selected): |
|
85 self.SaveCView(sel) |
|
86 self._Views.pop(sel) |
|
87 evt.Skip() |
|
88 New_View = wx.Frame(self.GetPlugRoot().AppFrame,-1,selected) |
|
89 New_View.Bind(wx.EVT_CLOSE, _onclose) |
|
90 ed = CppSTC(New_View, wx.NewId()) |
|
91 ed.SetText(open(self.CFileName(selected)).read()) |
|
92 ed.EmptyUndoBuffer() |
|
93 ed.Colourise(0, -1) |
|
94 ed.SetMarginType(1, stc.STC_MARGIN_NUMBER) |
|
95 ed.SetMarginWidth(1, 25) |
|
96 New_View.ed = ed |
|
97 New_View.Show() |
|
98 self._Views[selected] = New_View |
|
99 |
|
100 dlg.Destroy() |
|
101 |
|
102 |
201 |
103 PluginMethods = [ |
202 PluginMethods = [ |
104 {"name" : "Edit C File", |
203 {"name" : "Edit C File", |
105 "tooltip" : "Edit C File", |
204 "tooltip" : "Edit C File", |
106 "method" : "_OpenView"}, |
205 "method" : "_OpenView"}, |
107 {"name" : "Import C File", |
|
108 "tooltip" : "Import C File", |
|
109 "method" : "_OpenView"} |
|
110 ] |
206 ] |
111 |
207 |
112 def SaveCView(self, name): |
|
113 f = open(self.CFileName(name),'w') |
|
114 f.write(self._Views[name].ed.GetText()) |
|
115 f.close() |
|
116 |
|
117 def OnPlugSave(self): |
208 def OnPlugSave(self): |
118 for name in self._Views: |
209 filepath = self.CFileName() |
119 self.SaveCView(name) |
210 |
|
211 text = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" |
|
212 extras = {"xmlns":"http://www.w3.org/2001/XMLSchema", |
|
213 "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance", |
|
214 "xsi:schemaLocation" : "cext_xsd.xsd"} |
|
215 text += self.CFile.generateXMLText("CFile", 0, extras) |
|
216 |
|
217 xmlfile = open(filepath,"w") |
|
218 xmlfile.write(text) |
|
219 xmlfile.close() |
|
220 |
|
221 self.CFileBuffer.CurrentSaved() |
120 return True |
222 return True |
121 |
223 |
122 def PlugGenerate_C(self, buildpath, locations, logger): |
224 def PlugGenerate_C(self, buildpath, locations, logger): |
123 """ |
225 """ |
124 Generate C code |
226 Generate C code |
133 @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND |
235 @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND |
134 """ |
236 """ |
135 current_location = self.GetCurrentLocation() |
237 current_location = self.GetCurrentLocation() |
136 # define a unique name for the generated C file |
238 # define a unique name for the generated C file |
137 location_str = "_".join(map(lambda x:str(x), current_location)) |
239 location_str = "_".join(map(lambda x:str(x), current_location)) |
138 res = [] |
240 |
139 for CFile in self.CFileBaseNames(): |
241 text = "/* Code generated by Beremiz c_ext plugin */\n\n" |
140 Gen_Cfile_path = os.path.join(buildpath, "CFile_%s_%s.c"%(location_str, os.path.splitext(CFile)[0])) |
242 |
141 f = open(Gen_Cfile_path,'w') |
243 # Adding includes |
142 f.write("/* Header generated by Beremiz c_ext plugin */\n") |
244 text += "/* User includes */\n" |
143 f.write("#include \"iec_std_lib.h\"\n") |
245 text += self.CFile.includes.gettext() |
144 f.write("#define EXT_C_INIT __init_%s\n"%location_str) |
246 text += "\n" |
145 f.write("#define EXT_C_CLEANUP __init_%s\n"%location_str) |
247 |
146 f.write("#define EXT_C_PUBLISH __init_%s\n"%location_str) |
248 text += """/* Beremiz c_ext plugin includes */ |
147 f.write("#define EXT_C_RETRIEVE __init_%s\n"%location_str) |
249 #ifdef _WINDOWS_H |
148 for loc in locations: |
250 #include "iec_types.h" |
149 f.write(loc["IEC_TYPE"]+" "+loc["NAME"]+";\n") |
251 #else |
150 f.write("/* End of header generated by Beremiz c_ext plugin */\n\n") |
252 #include "iec_std_lib.h" |
151 src_file = open(self.CFileName(CFile),'r') |
253 #endif |
152 f.write(src_file.read()) |
254 |
153 src_file.close() |
255 """ |
154 f.close() |
256 |
155 res.append((Gen_Cfile_path,str(self.C_Extension.getCFLAGS()))) |
257 # Adding variables |
156 return res,str(self.C_Extension.getLDFLAGS()),True |
258 vars = [] |
157 |
259 inputs = outputs = 0 |
|
260 for variable in self.CFile.variables.variable: |
|
261 var = {"Name" : variable.getname(), "Type" : variable.gettype()} |
|
262 if variable.getclass() == "input": |
|
263 var["location"] = "__I%s%s_%d"%(self.GetSizeOfType(var["Type"]), location_str, inputs) |
|
264 inputs += 1 |
|
265 else: |
|
266 var["location"] = "__Q%s%s_%d"%(self.GetSizeOfType(var["Type"]), location_str, outputs) |
|
267 outputs += 1 |
|
268 vars.append(var) |
|
269 text += "/* Beremiz c_ext plugin user variables definition */\n" |
|
270 text += "#ifdef _WINDOWS_H\n" |
|
271 base_types = self.GetPlugRoot().GetBaseTypes() |
|
272 for var in vars: |
|
273 if var["Type"] in base_types: |
|
274 prefix = "IEC_" |
|
275 else: |
|
276 prefix = "" |
|
277 text += "%s%s %s;\n"%(prefix, var["Type"], var["location"]) |
|
278 text += "#else\n" |
|
279 for var in vars: |
|
280 text += "%s %s;\n"%(var["Type"], var["location"]) |
|
281 text += "#endif\n\n" |
|
282 text += "/* User variables reference */\n" |
|
283 for var in vars: |
|
284 text += "#define %s %s;\n"%(var["Name"], var["location"]) |
|
285 text += "\n" |
|
286 |
|
287 # Adding user global variables and routines |
|
288 text += "/* User internal user variables and routines */\n" |
|
289 text += self.CFile.globals.gettext() |
|
290 |
|
291 # Adding Beremiz plugin functions |
|
292 text += "/* Beremiz plugin functions */\n" |
|
293 text += "int __init_%s(int argc,char **argv)\n{\n"%location_str |
|
294 text += self.CFile.initFunction.gettext() |
|
295 text += "\n}\n\n" |
|
296 |
|
297 text += "void __cleanup_%s()\n{\n"%location_str |
|
298 text += self.CFile.cleanUpFunction.gettext() |
|
299 text += "\n}\n\n" |
|
300 |
|
301 text += "void __retrieve_%s()\n{\n"%location_str |
|
302 text += self.CFile.retrieveFunction.gettext() |
|
303 text += "\n}\n\n" |
|
304 |
|
305 text += "void __publish_%s()\n{\n"%location_str |
|
306 text += self.CFile.publishFunction.gettext() |
|
307 text += "\n}\n\n" |
|
308 |
|
309 Gen_Cfile_path = os.path.join(buildpath, "CFile_%s.c"%location_str) |
|
310 cfile = open(Gen_Cfile_path,'w') |
|
311 cfile.write(text) |
|
312 cfile.close() |
|
313 |
|
314 if wx.Platform == '__WXMSW__': |
|
315 matiec_flags = " -I../../matiec/lib" |
|
316 else: |
|
317 matiec_flags = " -I../matiec/lib" |
|
318 |
|
319 return [(Gen_Cfile_path, str(self.CExtension.getCFLAGS() + matiec_flags))],str(self.CExtension.getLDFLAGS()),True |
|
320 |
|
321 #------------------------------------------------------------------------------- |
|
322 # Current Buffering Management Functions |
|
323 #------------------------------------------------------------------------------- |
|
324 |
|
325 """ |
|
326 Return a copy of the project |
|
327 """ |
|
328 def Copy(self, model): |
|
329 return cPickle.loads(cPickle.dumps(model)) |
|
330 |
|
331 def BufferCFile(self): |
|
332 self.CFileBuffer.Buffering(self.Copy(self.CFile)) |
|
333 |
|
334 def StartBuffering(self): |
|
335 self.CFileBuffer.Buffering(self.CFile) |
|
336 self.Buffering = True |
|
337 |
|
338 def EndBuffering(self): |
|
339 if self.Buffering: |
|
340 self.CFile = self.Copy(self.CFile) |
|
341 self.Buffering = False |
|
342 |
|
343 def CFileIsSaved(self): |
|
344 if self.CFileBuffer: |
|
345 return self.CFileBuffer.IsCurrentSaved() |
|
346 else: |
|
347 return True |
|
348 |
|
349 def LoadPrevious(self): |
|
350 self.CFile = self.Copy(self.CFileBuffer.Previous()) |
|
351 |
|
352 def LoadNext(self): |
|
353 self.CFile = self.Copy(self.CFileBuffer.Next()) |
|
354 |
|
355 def GetBufferState(self): |
|
356 first = self.CFileBuffer.IsFirst() |
|
357 last = self.CFileBuffer.IsLast() |
|
358 return not first, not last |
|
359 |
158 class RootClass: |
360 class RootClass: |
159 |
361 |
160 PlugChildsTypes = [("C_File",_Cfile, "C file")] |
362 PlugChildsTypes = [("C_File",_Cfile, "C file")] |
161 |
363 |
162 def PlugGenerate_C(self, buildpath, locations, logger): |
364 def PlugGenerate_C(self, buildpath, locations, logger): |