changeset 1407 | cf3d2b53dd68 |
parent 1401 | 611fded24ce4 |
child 1413 | dd89016a5028 |
1406:82db84fe88ea | 1407:cf3d2b53dd68 |
---|---|
1 """ |
1 """ |
2 Beremiz Project Controller |
2 Beremiz Project Controller |
3 """ |
3 """ |
4 import os,sys,traceback |
4 import os,sys,traceback |
5 import time |
5 import time |
6 import features |
6 import features |
7 import shutil |
7 import shutil |
61 def GetAddMenuItems(): |
61 def GetAddMenuItems(): |
62 return ExtractMenuItemsFromCatalog(features.catalog) |
62 return ExtractMenuItemsFromCatalog(features.catalog) |
63 |
63 |
64 class ProjectController(ConfigTreeNode, PLCControler): |
64 class ProjectController(ConfigTreeNode, PLCControler): |
65 """ |
65 """ |
66 This class define Root object of the confnode tree. |
66 This class define Root object of the confnode tree. |
67 It is responsible of : |
67 It is responsible of : |
68 - Managing project directory |
68 - Managing project directory |
69 - Building project |
69 - Building project |
70 - Handling PLCOpenEditor controler and view |
70 - Handling PLCOpenEditor controler and view |
71 - Loading user confnodes and instanciante them as children |
71 - Loading user confnodes and instanciante them as children |
72 - ... |
72 - ... |
73 |
73 |
74 """ |
74 """ |
75 |
75 |
76 # For root object, available Children Types are modules of the confnode packages. |
76 # For root object, available Children Types are modules of the confnode packages. |
77 CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog) |
77 CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog) |
78 |
78 |
90 </xsd:element> |
90 </xsd:element> |
91 <xsd:element name="Libraries" minOccurs="0">"""+((""" |
91 <xsd:element name="Libraries" minOccurs="0">"""+((""" |
92 <xsd:complexType> |
92 <xsd:complexType> |
93 """+"\n".join(['<xsd:attribute name='+ |
93 """+"\n".join(['<xsd:attribute name='+ |
94 '"Enable_'+ libname + '_Library" '+ |
94 '"Enable_'+ libname + '_Library" '+ |
95 'type="xsd:boolean" use="optional" default="true"/>' |
95 'type="xsd:boolean" use="optional" default="true"/>' |
96 for libname,lib in features.libraries])+""" |
96 for libname,lib in features.libraries])+""" |
97 </xsd:complexType>""") if len(features.libraries)>0 else '<xsd:complexType/>') + """ |
97 </xsd:complexType>""") if len(features.libraries)>0 else '<xsd:complexType/>') + """ |
98 </xsd:element> |
98 </xsd:element> |
99 </xsd:sequence> |
99 </xsd:sequence> |
100 <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/> |
100 <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/> |
102 </xsd:complexType> |
102 </xsd:complexType> |
103 </xsd:element> |
103 </xsd:element> |
104 </xsd:schema> |
104 </xsd:schema> |
105 """ |
105 """ |
106 EditorType = ProjectNodeEditor |
106 EditorType = ProjectNodeEditor |
107 |
107 |
108 def __init__(self, frame, logger): |
108 def __init__(self, frame, logger): |
109 PLCControler.__init__(self) |
109 PLCControler.__init__(self) |
110 |
110 |
111 self.MandatoryParams = None |
111 self.MandatoryParams = None |
112 self._builder = None |
112 self._builder = None |
113 self._connector = None |
113 self._connector = None |
114 self.DispatchDebugValuesTimer = None |
114 self.DispatchDebugValuesTimer = None |
115 self.DebugValuesBuffers = [] |
115 self.DebugValuesBuffers = [] |
116 self.DebugTicks = [] |
116 self.DebugTicks = [] |
117 self.SetAppFrame(frame, logger) |
117 self.SetAppFrame(frame, logger) |
118 |
118 |
119 self.iec2c_path = os.path.join(base_folder, "matiec", "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else "")) |
119 self.iec2c_path = os.path.join(base_folder, "matiec", "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else "")) |
120 self.ieclib_path = os.path.join(base_folder, "matiec", "lib") |
120 self.ieclib_path = os.path.join(base_folder, "matiec", "lib") |
121 |
121 |
122 # Setup debug information |
122 # Setup debug information |
123 self.IECdebug_datas = {} |
123 self.IECdebug_datas = {} |
124 self.IECdebug_lock = Lock() |
124 self.IECdebug_lock = Lock() |
125 |
125 |
126 self.DebugTimer=None |
126 self.DebugTimer=None |
145 |
145 |
146 def __del__(self): |
146 def __del__(self): |
147 if self.DebugTimer: |
147 if self.DebugTimer: |
148 self.DebugTimer.cancel() |
148 self.DebugTimer.cancel() |
149 self.KillDebugThread() |
149 self.KillDebugThread() |
150 |
150 |
151 def LoadLibraries(self): |
151 def LoadLibraries(self): |
152 self.Libraries = [] |
152 self.Libraries = [] |
153 TypeStack=[] |
153 TypeStack=[] |
154 for libname,clsname in features.libraries: |
154 for libname,clsname in features.libraries: |
155 if self.BeremizRoot.Libraries is None or getattr(self.BeremizRoot.Libraries, "Enable_"+libname+"_Library"): |
155 if self.BeremizRoot.Libraries is None or getattr(self.BeremizRoot.Libraries, "Enable_"+libname+"_Library"): |
156 Lib = GetClassImporter(clsname)()(self, libname, TypeStack) |
156 Lib = GetClassImporter(clsname)()(self, libname, TypeStack) |
157 TypeStack.append(Lib.GetTypes()) |
157 TypeStack.append(Lib.GetTypes()) |
158 self.Libraries.append(Lib) |
158 self.Libraries.append(Lib) |
159 |
159 |
160 def SetAppFrame(self, frame, logger): |
160 def SetAppFrame(self, frame, logger): |
161 self.AppFrame = frame |
161 self.AppFrame = frame |
162 self.logger = logger |
162 self.logger = logger |
163 self.StatusTimer = None |
163 self.StatusTimer = None |
164 if self.DispatchDebugValuesTimer is not None: |
164 if self.DispatchDebugValuesTimer is not None: |
165 self.DispatchDebugValuesTimer.Stop() |
165 self.DispatchDebugValuesTimer.Stop() |
166 self.DispatchDebugValuesTimer = None |
166 self.DispatchDebugValuesTimer = None |
167 |
167 |
168 if frame is not None: |
168 if frame is not None: |
169 |
169 |
170 # Timer to pull PLC status |
170 # Timer to pull PLC status |
171 self.StatusTimer = wx.Timer(self.AppFrame, -1) |
171 self.StatusTimer = wx.Timer(self.AppFrame, -1) |
172 self.AppFrame.Bind(wx.EVT_TIMER, |
172 self.AppFrame.Bind(wx.EVT_TIMER, |
173 self.PullPLCStatusProc, self.StatusTimer) |
173 self.PullPLCStatusProc, self.StatusTimer) |
174 |
174 |
175 if self._connector is not None: |
175 if self._connector is not None: |
176 frame.LogViewer.SetLogSource(self._connector) |
176 frame.LogViewer.SetLogSource(self._connector) |
177 self.StatusTimer.Start(milliseconds=500, oneShot=False) |
177 self.StatusTimer.Start(milliseconds=500, oneShot=False) |
178 |
178 |
179 # Timer to dispatch debug values to consumers |
179 # Timer to dispatch debug values to consumers |
180 self.DispatchDebugValuesTimer = wx.Timer(self.AppFrame, -1) |
180 self.DispatchDebugValuesTimer = wx.Timer(self.AppFrame, -1) |
181 self.AppFrame.Bind(wx.EVT_TIMER, |
181 self.AppFrame.Bind(wx.EVT_TIMER, |
182 self.DispatchDebugValuesProc, self.DispatchDebugValuesTimer) |
182 self.DispatchDebugValuesProc, self.DispatchDebugValuesTimer) |
183 |
183 |
184 self.RefreshConfNodesBlockLists() |
184 self.RefreshConfNodesBlockLists() |
185 |
185 |
186 def ResetAppFrame(self, logger): |
186 def ResetAppFrame(self, logger): |
187 if self.AppFrame is not None: |
187 if self.AppFrame is not None: |
188 self.AppFrame.Unbind(wx.EVT_TIMER, self.StatusTimer) |
188 self.AppFrame.Unbind(wx.EVT_TIMER, self.StatusTimer) |
189 self.StatusTimer = None |
189 self.StatusTimer = None |
190 self.AppFrame = None |
190 self.AppFrame = None |
191 |
191 |
192 self.logger = logger |
192 self.logger = logger |
193 |
193 |
194 def CTNName(self): |
194 def CTNName(self): |
195 return "Project" |
195 return "Project" |
196 |
196 |
203 def GetCTRoot(self): |
203 def GetCTRoot(self): |
204 return self |
204 return self |
205 |
205 |
206 def GetIECLibPath(self): |
206 def GetIECLibPath(self): |
207 return self.ieclib_path |
207 return self.ieclib_path |
208 |
208 |
209 def GetIEC2cPath(self): |
209 def GetIEC2cPath(self): |
210 return self.iec2c_path |
210 return self.iec2c_path |
211 |
211 |
212 def GetCurrentLocation(self): |
212 def GetCurrentLocation(self): |
213 return () |
213 return () |
214 |
214 |
215 def GetCurrentName(self): |
215 def GetCurrentName(self): |
216 return "" |
216 return "" |
217 |
217 |
218 def _GetCurrentName(self): |
218 def _GetCurrentName(self): |
219 return "" |
219 return "" |
220 |
220 |
221 def GetProjectPath(self): |
221 def GetProjectPath(self): |
222 return self.ProjectPath |
222 return self.ProjectPath |
223 |
223 |
224 def GetProjectName(self): |
224 def GetProjectName(self): |
225 return os.path.split(self.ProjectPath)[1] |
225 return os.path.split(self.ProjectPath)[1] |
226 |
226 |
227 def GetIconName(self): |
227 def GetIconName(self): |
228 return "PROJECT" |
228 return "PROJECT" |
229 |
229 |
230 def GetDefaultTargetName(self): |
230 def GetDefaultTargetName(self): |
231 if wx.Platform == '__WXMSW__': |
231 if wx.Platform == '__WXMSW__': |
232 return "Win32" |
232 return "Win32" |
233 else: |
233 else: |
234 return "Linux" |
234 return "Linux" |
240 target = self.Parser.CreateElement("TargetType", "BeremizRoot") |
240 target = self.Parser.CreateElement("TargetType", "BeremizRoot") |
241 temp_root.setTargetType(target) |
241 temp_root.setTargetType(target) |
242 target_name = self.GetDefaultTargetName() |
242 target_name = self.GetDefaultTargetName() |
243 target.setcontent(self.Parser.CreateElement(target_name, "TargetType")) |
243 target.setcontent(self.Parser.CreateElement(target_name, "TargetType")) |
244 return target |
244 return target |
245 |
245 |
246 def GetParamsAttributes(self, path = None): |
246 def GetParamsAttributes(self, path = None): |
247 params = ConfigTreeNode.GetParamsAttributes(self, path) |
247 params = ConfigTreeNode.GetParamsAttributes(self, path) |
248 if params[0]["name"] == "BeremizRoot": |
248 if params[0]["name"] == "BeremizRoot": |
249 for child in params[0]["children"]: |
249 for child in params[0]["children"]: |
250 if child["name"] == "TargetType" and child["value"] == '': |
250 if child["name"] == "TargetType" and child["value"] == '': |
251 child.update(self.GetTarget().getElementInfos("TargetType")) |
251 child.update(self.GetTarget().getElementInfos("TargetType")) |
252 return params |
252 return params |
253 |
253 |
254 def SetParamsAttribute(self, path, value): |
254 def SetParamsAttribute(self, path, value): |
255 if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None: |
255 if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None: |
256 self.BeremizRoot.setTargetType(self.GetTarget()) |
256 self.BeremizRoot.setTargetType(self.GetTarget()) |
257 res = ConfigTreeNode.SetParamsAttribute(self, path, value) |
257 res = ConfigTreeNode.SetParamsAttribute(self, path, value) |
258 if path.startswith("BeremizRoot.Libraries."): |
258 if path.startswith("BeremizRoot.Libraries."): |
259 wx.CallAfter(self.RefreshConfNodesBlockLists) |
259 wx.CallAfter(self.RefreshConfNodesBlockLists) |
260 return res |
260 return res |
261 |
261 |
262 # helper func to check project path write permission |
262 # helper func to check project path write permission |
263 def CheckProjectPathPerm(self, dosave=True): |
263 def CheckProjectPathPerm(self, dosave=True): |
264 if CheckPathPerm(self.ProjectPath): |
264 if CheckPathPerm(self.ProjectPath): |
265 return True |
265 return True |
266 if self.AppFrame is not None: |
266 if self.AppFrame is not None: |
267 dialog = wx.MessageDialog(self.AppFrame, |
267 dialog = wx.MessageDialog(self.AppFrame, |
268 _('You must have permission to work on the project\nWork on a project copy ?'), |
268 _('You must have permission to work on the project\nWork on a project copy ?'), |
269 _('Error'), |
269 _('Error'), |
270 wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) |
270 wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) |
271 answer = dialog.ShowModal() |
271 answer = dialog.ShowModal() |
272 dialog.Destroy() |
272 dialog.Destroy() |
273 if answer == wx.ID_YES: |
273 if answer == wx.ID_YES: |
274 if self.SaveProjectAs(): |
274 if self.SaveProjectAs(): |
275 self.AppFrame.RefreshTitle() |
275 self.AppFrame.RefreshTitle() |
276 self.AppFrame.RefreshFileMenu() |
276 self.AppFrame.RefreshFileMenu() |
277 self.AppFrame.RefreshPageTitles() |
277 self.AppFrame.RefreshPageTitles() |
278 return True |
278 return True |
279 return False |
279 return False |
280 |
280 |
281 def _getProjectFilesPath(self, project_path=None): |
281 def _getProjectFilesPath(self, project_path=None): |
282 if project_path is not None: |
282 if project_path is not None: |
283 return os.path.join(project_path, "project_files") |
283 return os.path.join(project_path, "project_files") |
284 projectfiles_path = os.path.join(self.GetProjectPath(), "project_files") |
284 projectfiles_path = os.path.join(self.GetProjectPath(), "project_files") |
285 if not os.path.exists(projectfiles_path): |
285 if not os.path.exists(projectfiles_path): |
286 os.mkdir(projectfiles_path) |
286 os.mkdir(projectfiles_path) |
287 return projectfiles_path |
287 return projectfiles_path |
288 |
288 |
289 def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"): |
289 def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"): |
290 self.ProjectAddConfiguration(config_name) |
290 self.ProjectAddConfiguration(config_name) |
291 self.ProjectAddConfigurationResource(config_name, res_name) |
291 self.ProjectAddConfigurationResource(config_name, res_name) |
292 |
292 |
293 def NewProject(self, ProjectPath, BuildPath=None): |
293 def NewProject(self, ProjectPath, BuildPath=None): |
297 @param PLCParams: properties of the PLCOpen program created |
297 @param PLCParams: properties of the PLCOpen program created |
298 """ |
298 """ |
299 # Verify that chosen folder is empty |
299 # Verify that chosen folder is empty |
300 if not os.path.isdir(ProjectPath) or len(os.listdir(ProjectPath)) > 0: |
300 if not os.path.isdir(ProjectPath) or len(os.listdir(ProjectPath)) > 0: |
301 return _("Chosen folder isn't empty. You can't use it for a new project!") |
301 return _("Chosen folder isn't empty. You can't use it for a new project!") |
302 |
302 |
303 # Create PLCOpen program |
303 # Create PLCOpen program |
304 self.CreateNewProject( |
304 self.CreateNewProject( |
305 {"projectName": _("Unnamed"), |
305 {"projectName": _("Unnamed"), |
306 "productName": _("Unnamed"), |
306 "productName": _("Unnamed"), |
307 "productVersion": "1", |
307 "productVersion": "1", |
308 "companyName": _("Unknown"), |
308 "companyName": _("Unknown"), |
309 "creationDateTime": datetime(*localtime()[:6])}) |
309 "creationDateTime": datetime(*localtime()[:6])}) |
310 self.AddProjectDefaultConfiguration() |
310 self.AddProjectDefaultConfiguration() |
311 |
311 |
312 # Change XSD into class members |
312 # Change XSD into class members |
313 self._AddParamsMembers() |
313 self._AddParamsMembers() |
314 self.Children = {} |
314 self.Children = {} |
315 # Keep track of the root confnode (i.e. project path) |
315 # Keep track of the root confnode (i.e. project path) |
316 self.ProjectPath = ProjectPath |
316 self.ProjectPath = ProjectPath |
318 # get confnodes bloclist (is that usefull at project creation?) |
318 # get confnodes bloclist (is that usefull at project creation?) |
319 self.RefreshConfNodesBlockLists() |
319 self.RefreshConfNodesBlockLists() |
320 # this will create files base XML files |
320 # this will create files base XML files |
321 self.SaveProject() |
321 self.SaveProject() |
322 return None |
322 return None |
323 |
323 |
324 def LoadProject(self, ProjectPath, BuildPath=None): |
324 def LoadProject(self, ProjectPath, BuildPath=None): |
325 """ |
325 """ |
326 Load a project contained in a folder |
326 Load a project contained in a folder |
327 @param ProjectPath: path of the project folder |
327 @param ProjectPath: path of the project folder |
328 """ |
328 """ |
355 if result: |
355 if result: |
356 return result |
356 return result |
357 #Load and init all the children |
357 #Load and init all the children |
358 self.LoadChildren() |
358 self.LoadChildren() |
359 self.RefreshConfNodesBlockLists() |
359 self.RefreshConfNodesBlockLists() |
360 |
360 |
361 if os.path.exists(self._getBuildPath()): |
361 if os.path.exists(self._getBuildPath()): |
362 self.EnableMethod("_Clean", True) |
362 self.EnableMethod("_Clean", True) |
363 |
363 |
364 if os.path.isfile(self._getIECcodepath()): |
364 if os.path.isfile(self._getIECcodepath()): |
365 self.ShowMethod("_showIECcode", True) |
365 self.ShowMethod("_showIECcode", True) |
366 |
366 |
367 self.UpdateMethodsFromPLCStatus() |
367 self.UpdateMethodsFromPLCStatus() |
368 |
368 |
369 return None |
369 return None |
370 |
370 |
371 def RecursiveConfNodeInfos(self, confnode): |
371 def RecursiveConfNodeInfos(self, confnode): |
372 values = [] |
372 values = [] |
373 for CTNChild in confnode.IECSortedChildren(): |
373 for CTNChild in confnode.IECSortedChildren(): |
374 values.append( |
374 values.append( |
375 {"name": "%s: %s" % (CTNChild.GetFullIEC_Channel(), |
375 {"name": "%s: %s" % (CTNChild.GetFullIEC_Channel(), |
376 CTNChild.CTNName()), |
376 CTNChild.CTNName()), |
377 "tagname": CTNChild.CTNFullName(), |
377 "tagname": CTNChild.CTNFullName(), |
378 "type": ITEM_CONFNODE, |
378 "type": ITEM_CONFNODE, |
379 "confnode": CTNChild, |
379 "confnode": CTNChild, |
380 "icon": CTNChild.GetIconName(), |
380 "icon": CTNChild.GetIconName(), |
381 "values": self.RecursiveConfNodeInfos(CTNChild)}) |
381 "values": self.RecursiveConfNodeInfos(CTNChild)}) |
382 return values |
382 return values |
383 |
383 |
384 def GetProjectInfos(self): |
384 def GetProjectInfos(self): |
385 infos = PLCControler.GetProjectInfos(self) |
385 infos = PLCControler.GetProjectInfos(self) |
386 configurations = infos["values"].pop(-1) |
386 configurations = infos["values"].pop(-1) |
387 resources = None |
387 resources = None |
388 for config_infos in configurations["values"]: |
388 for config_infos in configurations["values"]: |
392 resources["values"].extend(config_infos["values"][0]["values"]) |
392 resources["values"].extend(config_infos["values"][0]["values"]) |
393 if resources is not None: |
393 if resources is not None: |
394 infos["values"].append(resources) |
394 infos["values"].append(resources) |
395 infos["values"].extend(self.RecursiveConfNodeInfos(self)) |
395 infos["values"].extend(self.RecursiveConfNodeInfos(self)) |
396 return infos |
396 return infos |
397 |
397 |
398 def CloseProject(self): |
398 def CloseProject(self): |
399 self.ClearChildren() |
399 self.ClearChildren() |
400 self.ResetAppFrame(None) |
400 self.ResetAppFrame(None) |
401 |
401 |
402 def SaveProject(self, from_project_path=None): |
402 def SaveProject(self, from_project_path=None): |
403 if self.CheckProjectPathPerm(False): |
403 if self.CheckProjectPathPerm(False): |
404 if from_project_path is not None: |
404 if from_project_path is not None: |
405 old_projectfiles_path = self._getProjectFilesPath(from_project_path) |
405 old_projectfiles_path = self._getProjectFilesPath(from_project_path) |
406 if os.path.isdir(old_projectfiles_path): |
406 if os.path.isdir(old_projectfiles_path): |
407 shutil.copytree(old_projectfiles_path, |
407 shutil.copytree(old_projectfiles_path, |
408 self._getProjectFilesPath(self.ProjectPath)) |
408 self._getProjectFilesPath(self.ProjectPath)) |
409 self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml')) |
409 self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml')) |
410 result = self.CTNRequestSave(from_project_path) |
410 result = self.CTNRequestSave(from_project_path) |
411 if result: |
411 if result: |
412 self.logger.write_error(result) |
412 self.logger.write_error(result) |
413 |
413 |
414 def SaveProjectAs(self): |
414 def SaveProjectAs(self): |
415 # Ask user to choose a path with write permissions |
415 # Ask user to choose a path with write permissions |
416 if wx.Platform == '__WXMSW__': |
416 if wx.Platform == '__WXMSW__': |
417 path = os.getenv("USERPROFILE") |
417 path = os.getenv("USERPROFILE") |
418 else: |
418 else: |
442 self.GetIECProgramsAndVariables() |
442 self.GetIECProgramsAndVariables() |
443 LibIECCflags = '"-I%s" -Wno-unused-function'%os.path.abspath(self.GetIECLibPath()) |
443 LibIECCflags = '"-I%s" -Wno-unused-function'%os.path.abspath(self.GetIECLibPath()) |
444 LocatedCCodeAndFlags=[] |
444 LocatedCCodeAndFlags=[] |
445 Extras=[] |
445 Extras=[] |
446 for lib in self.Libraries: |
446 for lib in self.Libraries: |
447 res=lib.Generate_C(buildpath,self._VariablesList,LibIECCflags) |
447 res=lib.Generate_C(buildpath,self._VariablesList,LibIECCflags) |
448 LocatedCCodeAndFlags.append(res[:2]) |
448 LocatedCCodeAndFlags.append(res[:2]) |
449 if len(res)>2: |
449 if len(res)>2: |
450 Extras.extend(res[2:]) |
450 Extras.extend(res[2:]) |
451 return map(list,zip(*LocatedCCodeAndFlags))+[tuple(Extras)] |
451 return map(list,zip(*LocatedCCodeAndFlags))+[tuple(Extras)] |
452 |
452 |
453 # Update PLCOpenEditor ConfNode Block types from loaded confnodes |
453 # Update PLCOpenEditor ConfNode Block types from loaded confnodes |
454 def RefreshConfNodesBlockLists(self): |
454 def RefreshConfNodesBlockLists(self): |
455 if getattr(self, "Children", None) is not None: |
455 if getattr(self, "Children", None) is not None: |
456 self.ClearConfNodeTypes() |
456 self.ClearConfNodeTypes() |
457 self.AddConfNodeTypesList(self.GetLibrariesTypes()) |
457 self.AddConfNodeTypesList(self.GetLibrariesTypes()) |
458 if self.AppFrame is not None: |
458 if self.AppFrame is not None: |
459 self.AppFrame.RefreshLibraryPanel() |
459 self.AppFrame.RefreshLibraryPanel() |
460 self.AppFrame.RefreshEditor() |
460 self.AppFrame.RefreshEditor() |
461 |
461 |
462 # Update a PLCOpenEditor Pou variable location |
462 # Update a PLCOpenEditor Pou variable location |
463 def UpdateProjectVariableLocation(self, old_leading, new_leading): |
463 def UpdateProjectVariableLocation(self, old_leading, new_leading): |
464 self.Project.updateElementAddress(old_leading, new_leading) |
464 self.Project.updateElementAddress(old_leading, new_leading) |
465 self.BufferProject() |
465 self.BufferProject() |
466 if self.AppFrame is not None: |
466 if self.AppFrame is not None: |
467 self.AppFrame.RefreshTitle() |
467 self.AppFrame.RefreshTitle() |
468 self.AppFrame.RefreshPouInstanceVariablesPanel() |
468 self.AppFrame.RefreshPouInstanceVariablesPanel() |
469 self.AppFrame.RefreshFileMenu() |
469 self.AppFrame.RefreshFileMenu() |
470 self.AppFrame.RefreshEditMenu() |
470 self.AppFrame.RefreshEditMenu() |
471 wx.CallAfter(self.AppFrame.RefreshEditor) |
471 wx.CallAfter(self.AppFrame.RefreshEditor) |
472 |
472 |
473 def GetVariableLocationTree(self): |
473 def GetVariableLocationTree(self): |
474 ''' |
474 ''' |
475 This function is meant to be overridden by confnodes. |
475 This function is meant to be overridden by confnodes. |
476 |
476 |
477 It should returns an list of dictionaries |
477 It should returns an list of dictionaries |
478 |
478 |
479 - IEC_type is an IEC type like BOOL/BYTE/SINT/... |
479 - IEC_type is an IEC type like BOOL/BYTE/SINT/... |
480 - location is a string of this variable's location, like "%IX0.0.0" |
480 - location is a string of this variable's location, like "%IX0.0.0" |
481 ''' |
481 ''' |
482 children = [] |
482 children = [] |
483 for child in self.IECSortedChildren(): |
483 for child in self.IECSortedChildren(): |
484 children.append(child.GetVariableLocationTree()) |
484 children.append(child.GetVariableLocationTree()) |
485 return children |
485 return children |
486 |
486 |
487 def ConfNodePath(self): |
487 def ConfNodePath(self): |
488 return os.path.split(__file__)[0] |
488 return os.path.split(__file__)[0] |
489 |
489 |
490 def CTNPath(self, CTNName=None): |
490 def CTNPath(self, CTNName=None): |
491 return self.ProjectPath |
491 return self.ProjectPath |
492 |
492 |
493 def ConfNodeXmlFilePath(self, CTNName=None): |
493 def ConfNodeXmlFilePath(self, CTNName=None): |
494 return os.path.join(self.CTNPath(CTNName), "beremiz.xml") |
494 return os.path.join(self.CTNPath(CTNName), "beremiz.xml") |
495 |
495 |
496 def ParentsTypesFactory(self): |
496 def ParentsTypesFactory(self): |
497 return self.ConfNodeTypesFactory() |
497 return self.ConfNodeTypesFactory() |
513 if CheckPathPerm(self.ProjectPath): |
513 if CheckPathPerm(self.ProjectPath): |
514 self.DefaultBuildPath = os.path.join(self.ProjectPath, "build") |
514 self.DefaultBuildPath = os.path.join(self.ProjectPath, "build") |
515 # Create a build path in temp folder |
515 # Create a build path in temp folder |
516 else: |
516 else: |
517 self.DefaultBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build") |
517 self.DefaultBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build") |
518 |
518 |
519 if not os.path.exists(self.DefaultBuildPath): |
519 if not os.path.exists(self.DefaultBuildPath): |
520 os.makedirs(self.DefaultBuildPath) |
520 os.makedirs(self.DefaultBuildPath) |
521 return self.DefaultBuildPath |
521 return self.DefaultBuildPath |
522 |
522 |
523 def _getExtraFilesPath(self): |
523 def _getExtraFilesPath(self): |
524 return os.path.join(self._getBuildPath(), "extra_files") |
524 return os.path.join(self._getBuildPath(), "extra_files") |
525 |
525 |
526 def _getIECcodepath(self): |
526 def _getIECcodepath(self): |
527 # define name for IEC code file |
527 # define name for IEC code file |
528 return os.path.join(self._getBuildPath(), "plc.st") |
528 return os.path.join(self._getBuildPath(), "plc.st") |
529 |
529 |
530 def _getIECgeneratedcodepath(self): |
530 def _getIECgeneratedcodepath(self): |
531 # define name for IEC generated code file |
531 # define name for IEC generated code file |
532 return os.path.join(self._getBuildPath(), "generated_plc.st") |
532 return os.path.join(self._getBuildPath(), "generated_plc.st") |
533 |
533 |
534 def _getIECrawcodepath(self): |
534 def _getIECrawcodepath(self): |
535 # define name for IEC raw code file |
535 # define name for IEC raw code file |
536 return os.path.join(self.CTNPath(), "raw_plc.st") |
536 return os.path.join(self.CTNPath(), "raw_plc.st") |
537 |
537 |
538 def GetLocations(self): |
538 def GetLocations(self): |
539 locations = [] |
539 locations = [] |
540 filepath = os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h") |
540 filepath = os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h") |
541 if os.path.isfile(filepath): |
541 if os.path.isfile(filepath): |
542 # IEC2C compiler generate a list of located variables : LOCATED_VARIABLES.h |
542 # IEC2C compiler generate a list of located variables : LOCATED_VARIABLES.h |
544 # each line of LOCATED_VARIABLES.h declares a located variable |
544 # each line of LOCATED_VARIABLES.h declares a located variable |
545 lines = [line.strip() for line in location_file.readlines()] |
545 lines = [line.strip() for line in location_file.readlines()] |
546 # This regular expression parses the lines genereated by IEC2C |
546 # This regular expression parses the lines genereated by IEC2C |
547 LOCATED_MODEL = re.compile("__LOCATED_VAR\((?P<IEC_TYPE>[A-Z]*),(?P<NAME>[_A-Za-z0-9]*),(?P<DIR>[QMI])(?:,(?P<SIZE>[XBWDL]))?,(?P<LOC>[,0-9]*)\)") |
547 LOCATED_MODEL = re.compile("__LOCATED_VAR\((?P<IEC_TYPE>[A-Z]*),(?P<NAME>[_A-Za-z0-9]*),(?P<DIR>[QMI])(?:,(?P<SIZE>[XBWDL]))?,(?P<LOC>[,0-9]*)\)") |
548 for line in lines: |
548 for line in lines: |
549 # If line match RE, |
549 # If line match RE, |
550 result = LOCATED_MODEL.match(line) |
550 result = LOCATED_MODEL.match(line) |
551 if result: |
551 if result: |
552 # Get the resulting dict |
552 # Get the resulting dict |
553 resdict = result.groupdict() |
553 resdict = result.groupdict() |
554 # rewrite string for variadic location as a tuple of integers |
554 # rewrite string for variadic location as a tuple of integers |
555 resdict['LOC'] = tuple(map(int,resdict['LOC'].split(','))) |
555 resdict['LOC'] = tuple(map(int,resdict['LOC'].split(','))) |
556 # set located size to 'X' if not given |
556 # set located size to 'X' if not given |
557 if not resdict['SIZE']: |
557 if not resdict['SIZE']: |
558 resdict['SIZE'] = 'X' |
558 resdict['SIZE'] = 'X' |
559 # finally store into located variable list |
559 # finally store into located variable list |
560 locations.append(resdict) |
560 locations.append(resdict) |
561 return locations |
561 return locations |
562 |
562 |
563 def GetConfNodeGlobalInstances(self): |
563 def GetConfNodeGlobalInstances(self): |
564 return self._GlobalInstances() |
564 return self._GlobalInstances() |
565 |
565 |
566 def _Generate_SoftPLC(self): |
566 def _Generate_SoftPLC(self): |
567 if self._Generate_PLC_ST(): |
|
568 return self._Compile_ST_to_SoftPLC() |
|
569 return False |
|
570 |
|
571 def _Generate_PLC_ST(self): |
|
567 """ |
572 """ |
568 Generate SoftPLC ST/IL/SFC code out of PLCOpenEditor controller, and compile it with IEC2C |
573 Generate SoftPLC ST/IL/SFC code out of PLCOpenEditor controller, and compile it with IEC2C |
569 @param buildpath: path where files should be created |
574 @param buildpath: path where files should be created |
570 """ |
575 """ |
571 |
576 |
572 # Update PLCOpenEditor ConfNode Block types before generate ST code |
577 # Update PLCOpenEditor ConfNode Block types before generate ST code |
573 self.RefreshConfNodesBlockLists() |
578 self.RefreshConfNodesBlockLists() |
574 |
579 |
575 self.logger.write(_("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n")) |
580 self.logger.write(_("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n")) |
576 buildpath = self._getBuildPath() |
|
577 # ask PLCOpenEditor controller to write ST/IL/SFC code file |
581 # ask PLCOpenEditor controller to write ST/IL/SFC code file |
578 program, errors, warnings = self.GenerateProgram(self._getIECgeneratedcodepath()) |
582 program, errors, warnings = self.GenerateProgram(self._getIECgeneratedcodepath()) |
579 if len(warnings) > 0: |
583 if len(warnings) > 0: |
580 self.logger.write_warning(_("Warnings in ST/IL/SFC code generator :\n")) |
584 self.logger.write_warning(_("Warnings in ST/IL/SFC code generator :\n")) |
581 for warning in warnings: |
585 for warning in warnings: |
597 self.ProgramOffset += 1 |
601 self.ProgramOffset += 1 |
598 plc_file.close() |
602 plc_file.close() |
599 plc_file = open(self._getIECcodepath(), "a") |
603 plc_file = open(self._getIECcodepath(), "a") |
600 plc_file.write(open(self._getIECgeneratedcodepath(), "r").read()) |
604 plc_file.write(open(self._getIECgeneratedcodepath(), "r").read()) |
601 plc_file.close() |
605 plc_file.close() |
602 |
606 return True |
607 |
|
608 def _Compile_ST_to_SoftPLC(self): |
|
603 self.logger.write(_("Compiling IEC Program into C code...\n")) |
609 self.logger.write(_("Compiling IEC Program into C code...\n")) |
610 buildpath = self._getBuildPath() |
|
604 |
611 |
605 # Now compile IEC code into many C files |
612 # Now compile IEC code into many C files |
606 # files are listed to stdout, and errors to stderr. |
613 # files are listed to stdout, and errors to stderr. |
607 status, result, err_result = ProcessLogger( |
614 status, result, err_result = ProcessLogger( |
608 self.logger, |
615 self.logger, |
609 "\"%s\" -f -I \"%s\" -T \"%s\" \"%s\""%( |
616 "\"%s\" -f -I \"%s\" -T \"%s\" \"%s\""%( |
610 self.iec2c_path, |
617 self.iec2c_path, |
611 self.ieclib_path, |
618 self.ieclib_path, |
612 buildpath, |
619 buildpath, |
613 self._getIECcodepath()), |
620 self._getIECcodepath()), |
614 no_stdout=True, no_stderr=True).spin() |
621 no_stdout=True, no_stderr=True).spin() |
615 if status: |
622 if status: |
616 # Failed ! |
623 # Failed ! |
617 |
624 |
618 # parse iec2c's error message. if it contains a line number, |
625 # parse iec2c's error message. if it contains a line number, |
619 # then print those lines from the generated IEC file. |
626 # then print those lines from the generated IEC file. |
620 for err_line in err_result.split('\n'): |
627 for err_line in err_result.split('\n'): |
621 self.logger.write_warning(err_line + "\n") |
628 self.logger.write_warning(err_line + "\n") |
622 |
629 |
623 m_result = MATIEC_ERROR_MODEL.match(err_line) |
630 m_result = MATIEC_ERROR_MODEL.match(err_line) |
624 if m_result is not None: |
631 if m_result is not None: |
625 first_line, first_column, last_line, last_column, error = m_result.groups() |
632 first_line, first_column, last_line, last_column, error = m_result.groups() |
626 first_line, last_line = int(first_line), int(last_line) |
633 first_line, last_line = int(first_line), int(last_line) |
627 |
634 |
628 last_section = None |
635 last_section = None |
629 f = open(self._getIECcodepath()) |
636 f = open(self._getIECcodepath()) |
630 |
637 |
631 for i, line in enumerate(f.readlines()): |
638 for i, line in enumerate(f.readlines()): |
632 i = i + 1 |
639 i = i + 1 |
638 self.logger.write_warning("In section: " + last_section) |
645 self.logger.write_warning("In section: " + last_section) |
639 last_section = None # only write section once |
646 last_section = None # only write section once |
640 self.logger.write_warning("%04d: %s" % (i, line)) |
647 self.logger.write_warning("%04d: %s" % (i, line)) |
641 |
648 |
642 f.close() |
649 f.close() |
643 |
650 |
644 self.logger.write_error(_("Error : IEC to C compiler returned %d\n")%status) |
651 self.logger.write_error(_("Error : IEC to C compiler returned %d\n")%status) |
645 return False |
652 return False |
646 |
653 |
647 # Now extract C files of stdout |
654 # Now extract C files of stdout |
648 C_files = [ fname for fname in result.splitlines() if fname[-2:]==".c" or fname[-2:]==".C" ] |
655 C_files = [ fname for fname in result.splitlines() if fname[-2:]==".c" or fname[-2:]==".C" ] |
649 # remove those that are not to be compiled because included by others |
656 # remove those that are not to be compiled because included by others |
650 C_files.remove("POUS.c") |
657 C_files.remove("POUS.c") |
651 if not C_files: |
658 if not C_files: |
677 """ |
684 """ |
678 # Get target, module and class name |
685 # Get target, module and class name |
679 targetname = self.GetTarget().getcontent().getLocalTag() |
686 targetname = self.GetTarget().getcontent().getLocalTag() |
680 targetclass = targets.GetBuilder(targetname) |
687 targetclass = targets.GetBuilder(targetname) |
681 |
688 |
682 # if target already |
689 # if target already |
683 if self._builder is None or not isinstance(self._builder,targetclass): |
690 if self._builder is None or not isinstance(self._builder,targetclass): |
684 # Get classname instance |
691 # Get classname instance |
685 self._builder = targetclass(self) |
692 self._builder = targetclass(self) |
686 return self._builder |
693 return self._builder |
687 |
694 |
701 ####################################################################### |
708 ####################################################################### |
702 # |
709 # |
703 # C CODE GENERATION METHODS |
710 # C CODE GENERATION METHODS |
704 # |
711 # |
705 ####################################################################### |
712 ####################################################################### |
706 |
713 |
707 def CTNGenerate_C(self, buildpath, locations): |
714 def CTNGenerate_C(self, buildpath, locations): |
708 """ |
715 """ |
709 Return C code generated by iec2c compiler |
716 Return C code generated by iec2c compiler |
710 when _generate_softPLC have been called |
717 when _generate_softPLC have been called |
711 @param locations: ignored |
718 @param locations: ignored |
712 @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND |
719 @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND |
713 """ |
720 """ |
714 |
721 |
715 return ([(C_file_name, self.plcCFLAGS) |
722 return ([(C_file_name, self.plcCFLAGS) |
716 for C_file_name in self.PLCGeneratedCFiles ], |
723 for C_file_name in self.PLCGeneratedCFiles ], |
717 "", # no ldflags |
724 "", # no ldflags |
718 False) # do not expose retreive/publish calls |
725 False) # do not expose retreive/publish calls |
719 |
726 |
720 def ResetIECProgramsAndVariables(self): |
727 def ResetIECProgramsAndVariables(self): |
721 """ |
728 """ |
722 Reset variable and program list that are parsed from |
729 Reset variable and program list that are parsed from |
723 CSV file generated by IEC2C compiler. |
730 CSV file generated by IEC2C compiler. |
724 """ |
731 """ |
741 ProgramsListAttributeName = ["num", "C_path", "type"] |
748 ProgramsListAttributeName = ["num", "C_path", "type"] |
742 VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"] |
749 VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"] |
743 self._ProgramList = [] |
750 self._ProgramList = [] |
744 self._VariablesList = [] |
751 self._VariablesList = [] |
745 self._IECPathToIdx = {} |
752 self._IECPathToIdx = {} |
746 |
753 |
747 # Separate sections |
754 # Separate sections |
748 ListGroup = [] |
755 ListGroup = [] |
749 for line in open(csvfile,'r').xreadlines(): |
756 for line in open(csvfile,'r').xreadlines(): |
750 strippedline = line.strip() |
757 strippedline = line.strip() |
751 if strippedline.startswith("//"): |
758 if strippedline.startswith("//"): |
752 # Start new section |
759 # Start new section |
753 ListGroup.append([]) |
760 ListGroup.append([]) |
754 elif len(strippedline) > 0 and len(ListGroup) > 0: |
761 elif len(strippedline) > 0 and len(ListGroup) > 0: |
755 # append to this section |
762 # append to this section |
756 ListGroup[-1].append(strippedline) |
763 ListGroup[-1].append(strippedline) |
757 |
764 |
758 # first section contains programs |
765 # first section contains programs |
759 for line in ListGroup[0]: |
766 for line in ListGroup[0]: |
760 # Split and Maps each field to dictionnary entries |
767 # Split and Maps each field to dictionnary entries |
761 attrs = dict(zip(ProgramsListAttributeName,line.strip().split(';'))) |
768 attrs = dict(zip(ProgramsListAttributeName,line.strip().split(';'))) |
762 # Truncate "C_path" to remove conf an ressources names |
769 # Truncate "C_path" to remove conf an ressources names |
763 attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:]) |
770 attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:]) |
764 # Push this dictionnary into result. |
771 # Push this dictionnary into result. |
765 self._ProgramList.append(attrs) |
772 self._ProgramList.append(attrs) |
766 |
773 |
767 # second section contains all variables |
774 # second section contains all variables |
768 config_FBs = {} |
775 config_FBs = {} |
769 for line in ListGroup[1]: |
776 for line in ListGroup[1]: |
770 # Split and Maps each field to dictionnary entries |
777 # Split and Maps each field to dictionnary entries |
771 attrs = dict(zip(VariablesListAttributeName,line.strip().split(';'))) |
778 attrs = dict(zip(VariablesListAttributeName,line.strip().split(';'))) |
774 if len(parts) > 2: |
781 if len(parts) > 2: |
775 config_FB = config_FBs.get(tuple(parts[:2])) |
782 config_FB = config_FBs.get(tuple(parts[:2])) |
776 if config_FB: |
783 if config_FB: |
777 parts = [config_FB] + parts[2:] |
784 parts = [config_FB] + parts[2:] |
778 attrs["C_path"] = '.'.join(parts) |
785 attrs["C_path"] = '.'.join(parts) |
779 else: |
786 else: |
780 attrs["C_path"] = '__'.join(parts[1:]) |
787 attrs["C_path"] = '__'.join(parts[1:]) |
781 else: |
788 else: |
782 attrs["C_path"] = '__'.join(parts) |
789 attrs["C_path"] = '__'.join(parts) |
783 if attrs["vartype"] == "FB": |
790 if attrs["vartype"] == "FB": |
784 config_FBs[tuple(parts)] = attrs["C_path"] |
791 config_FBs[tuple(parts)] = attrs["C_path"] |
786 self._VariablesList.append(attrs) |
793 self._VariablesList.append(attrs) |
787 # Fill in IEC<->C translation dicts |
794 # Fill in IEC<->C translation dicts |
788 IEC_path=attrs["IEC_path"] |
795 IEC_path=attrs["IEC_path"] |
789 Idx=int(attrs["num"]) |
796 Idx=int(attrs["num"]) |
790 self._IECPathToIdx[IEC_path]=(Idx, attrs["type"]) |
797 self._IECPathToIdx[IEC_path]=(Idx, attrs["type"]) |
791 |
798 |
792 # third section contains ticktime |
799 # third section contains ticktime |
793 if len(ListGroup) > 2: |
800 if len(ListGroup) > 2: |
794 self._Ticktime = int(ListGroup[2][0]) |
801 self._Ticktime = int(ListGroup[2][0]) |
795 |
802 |
796 except Exception,e: |
803 except Exception,e: |
797 self.logger.write_error(_("Cannot open/parse VARIABLES.csv!\n")) |
804 self.logger.write_error(_("Cannot open/parse VARIABLES.csv!\n")) |
798 self.logger.write_error(traceback.format_exc()) |
805 self.logger.write_error(traceback.format_exc()) |
799 self.ResetIECProgramsAndVariables() |
806 self.ResetIECProgramsAndVariables() |
800 return False |
807 return False |
816 {"EXT":"extern __IEC_%(type)s_p %(C_path)s;", |
823 {"EXT":"extern __IEC_%(type)s_p %(C_path)s;", |
817 "IN":"extern __IEC_%(type)s_p %(C_path)s;", |
824 "IN":"extern __IEC_%(type)s_p %(C_path)s;", |
818 "MEM":"extern __IEC_%(type)s_p %(C_path)s;", |
825 "MEM":"extern __IEC_%(type)s_p %(C_path)s;", |
819 "OUT":"extern __IEC_%(type)s_p %(C_path)s;", |
826 "OUT":"extern __IEC_%(type)s_p %(C_path)s;", |
820 "VAR":"extern __IEC_%(type)s_t %(C_path)s;", |
827 "VAR":"extern __IEC_%(type)s_t %(C_path)s;", |
821 "FB":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v |
828 "FB":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v |
822 for v in self._VariablesList if v["C_path"].find('.')<0]), |
829 for v in self._VariablesList if v["C_path"].find('.')<0]), |
823 "for_each_variable_do_code":"\n".join([ |
830 "for_each_variable_do_code":"\n".join([ |
824 {"EXT":" (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n", |
831 {"EXT":" (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n", |
825 "IN":" (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n", |
832 "IN":" (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n", |
826 "MEM":" (*fp)((void*)&(%(C_path)s),%(type)s_O_ENUM);\n", |
833 "MEM":" (*fp)((void*)&(%(C_path)s),%(type)s_O_ENUM);\n", |
834 "IN":" return %(type)s_P_ENUM;\n", |
841 "IN":" return %(type)s_P_ENUM;\n", |
835 "MEM":" return %(type)s_O_ENUM;\n", |
842 "MEM":" return %(type)s_O_ENUM;\n", |
836 "OUT":" return %(type)s_O_ENUM;\n", |
843 "OUT":" return %(type)s_O_ENUM;\n", |
837 "VAR":" return %(type)s_ENUM;\n"}[v["vartype"]]%v |
844 "VAR":" return %(type)s_ENUM;\n"}[v["vartype"]]%v |
838 for v in self._VariablesList if v["vartype"] != "FB" and v["type"] in DebugTypesSize ])} |
845 for v in self._VariablesList if v["vartype"] != "FB" and v["type"] in DebugTypesSize ])} |
839 |
846 |
840 return debug_code |
847 return debug_code |
841 |
848 |
842 def Generate_plc_main(self): |
849 def Generate_plc_main(self): |
843 """ |
850 """ |
844 Use confnodes layout given in LocationCFilesAndCFLAGS to |
851 Use confnodes layout given in LocationCFilesAndCFLAGS to |
845 generate glue code that dispatch calls to all confnodes |
852 generate glue code that dispatch calls to all confnodes |
846 """ |
853 """ |
847 # filter location that are related to code that will be called |
854 # filter location that are related to code that will be called |
848 # in retreive, publish, init, cleanup |
855 # in retreive, publish, init, cleanup |
849 locstrs = map(lambda x:"_".join(map(str,x)), |
856 locstrs = map(lambda x:"_".join(map(str,x)), |
850 [loc for loc,Cfiles,DoCalls in self.LocationCFilesAndCFLAGS if loc and DoCalls]) |
857 [loc for loc,Cfiles,DoCalls in self.LocationCFilesAndCFLAGS if loc and DoCalls]) |
851 |
858 |
852 # Generate main, based on template |
859 # Generate main, based on template |
853 if not self.BeremizRoot.getDisable_Extensions(): |
860 if not self.BeremizRoot.getDisable_Extensions(): |
854 plc_main_code = targets.GetCode("plc_main_head") % { |
861 plc_main_code = targets.GetCode("plc_main_head") % { |
855 "calls_prototypes":"\n".join([( |
862 "calls_prototypes":"\n".join([( |
856 "int __init_%(s)s(int argc,char **argv);\n"+ |
863 "int __init_%(s)s(int argc,char **argv);\n"+ |
880 } |
887 } |
881 plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent().getLocalTag()) |
888 plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent().getLocalTag()) |
882 plc_main_code += targets.GetCode("plc_main_tail") |
889 plc_main_code += targets.GetCode("plc_main_tail") |
883 return plc_main_code |
890 return plc_main_code |
884 |
891 |
885 |
892 |
886 def _Build(self): |
893 def _Build(self): |
887 """ |
894 """ |
888 Method called by user to (re)build SoftPLC and confnode tree |
895 Method called by user to (re)build SoftPLC and confnode tree |
889 """ |
896 """ |
890 if self.AppFrame is not None: |
897 if self.AppFrame is not None: |
891 self.AppFrame.ClearErrors() |
898 self.AppFrame.ClearErrors() |
892 self._CloseView(self._IECCodeView) |
899 self._CloseView(self._IECCodeView) |
893 |
900 |
894 buildpath = self._getBuildPath() |
901 buildpath = self._getBuildPath() |
895 |
902 |
896 # Eventually create build dir |
903 # Eventually create build dir |
897 if not os.path.exists(buildpath): |
904 if not os.path.exists(buildpath): |
898 os.mkdir(buildpath) |
905 os.mkdir(buildpath) |
906 IECGenRes = self._Generate_SoftPLC() |
913 IECGenRes = self._Generate_SoftPLC() |
907 self.ShowMethod("_showIECcode", True) |
914 self.ShowMethod("_showIECcode", True) |
908 |
915 |
909 # If IEC code gen fail, bail out. |
916 # If IEC code gen fail, bail out. |
910 if not IECGenRes: |
917 if not IECGenRes: |
911 self.logger.write_error(_("IEC-61131-3 code generation failed !\n")) |
918 self.logger.write_error(_("PLC code generation failed !\n")) |
912 self.ResetBuildMD5() |
919 self.ResetBuildMD5() |
913 return False |
920 return False |
914 |
921 |
915 # Reset variable and program list that are parsed from |
922 # Reset variable and program list that are parsed from |
916 # CSV file generated by IEC2C compiler. |
923 # CSV file generated by IEC2C compiler. |
917 self.ResetIECProgramsAndVariables() |
924 self.ResetIECProgramsAndVariables() |
918 |
925 |
926 # Collect platform specific C code |
|
927 # Code and other files from extension |
|
928 if not self._Generate_runtime(): |
|
929 return False |
|
930 |
|
931 # Get current or fresh builder |
|
932 builder = self.GetBuilder() |
|
933 if builder is None: |
|
934 self.logger.write_error(_("Fatal : cannot get builder.\n")) |
|
935 self.ResetBuildMD5() |
|
936 return False |
|
937 |
|
938 # Build |
|
939 try: |
|
940 if not builder.build() : |
|
941 self.logger.write_error(_("C Build failed.\n")) |
|
942 return False |
|
943 except Exception, exc: |
|
944 self.logger.write_error(_("C Build crashed !\n")) |
|
945 self.logger.write_error(traceback.format_exc()) |
|
946 self.ResetBuildMD5() |
|
947 return False |
|
948 |
|
949 self.logger.write(_("Successfully built.\n")) |
|
950 # Update GUI status about need for transfer |
|
951 self.CompareLocalAndRemotePLC() |
|
952 return True |
|
953 |
|
954 def _Generate_runtime(self): |
|
955 buildpath = self._getBuildPath() |
|
956 |
|
919 # Generate C code and compilation params from confnode hierarchy |
957 # Generate C code and compilation params from confnode hierarchy |
920 try: |
958 try: |
921 CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C( |
959 CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C( |
922 buildpath, |
960 buildpath, |
923 self.PLCGeneratedLocatedVars) |
961 self.PLCGeneratedLocatedVars) |
924 except Exception, exc: |
962 except Exception, exc: |
925 self.logger.write_error(_("Runtime extensions C code generation failed !\n")) |
963 self.logger.write_error(_("Runtime IO extensions C code generation failed !\n")) |
926 self.logger.write_error(traceback.format_exc()) |
964 self.logger.write_error(traceback.format_exc()) |
927 self.ResetBuildMD5() |
965 self.ResetBuildMD5() |
928 return False |
966 return False |
929 |
967 |
930 # Generate C code and compilation params from liraries |
968 # Generate C code and compilation params from liraries |
931 try: |
969 try: |
932 LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(buildpath) |
970 LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(buildpath) |
933 except Exception, exc: |
971 except Exception, exc: |
934 self.logger.write_error(_("Runtime extensions C code generation failed !\n")) |
972 self.logger.write_error(_("Runtime library extensions C code generation failed !\n")) |
935 self.logger.write_error(traceback.format_exc()) |
973 self.logger.write_error(traceback.format_exc()) |
936 self.ResetBuildMD5() |
974 self.ResetBuildMD5() |
937 return False |
975 return False |
938 |
976 |
939 self.LocationCFilesAndCFLAGS = CTNLocationCFilesAndCFLAGS + LibCFilesAndCFLAGS |
977 self.LocationCFilesAndCFLAGS = CTNLocationCFilesAndCFLAGS + LibCFilesAndCFLAGS |
940 self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS |
978 self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS |
941 ExtraFiles = CTNExtraFiles + LibExtraFiles |
979 ExtraFiles = CTNExtraFiles + LibExtraFiles |
942 |
980 |
943 # Get temporary directory path |
981 # Get temporary directory path |
944 extrafilespath = self._getExtraFilesPath() |
982 extrafilespath = self._getExtraFilesPath() |
945 # Remove old directory |
983 # Remove old directory |
946 if os.path.exists(extrafilespath): |
984 if os.path.exists(extrafilespath): |
947 shutil.rmtree(extrafilespath) |
985 shutil.rmtree(extrafilespath) |
951 for fname,fobject in ExtraFiles: |
989 for fname,fobject in ExtraFiles: |
952 fpath = os.path.join(extrafilespath,fname) |
990 fpath = os.path.join(extrafilespath,fname) |
953 open(fpath, "wb").write(fobject.read()) |
991 open(fpath, "wb").write(fobject.read()) |
954 # Now we can forget ExtraFiles (will close files object) |
992 # Now we can forget ExtraFiles (will close files object) |
955 del ExtraFiles |
993 del ExtraFiles |
956 |
994 |
957 # Header file for extensions |
995 # Header file for extensions |
958 open(os.path.join(buildpath,"beremiz.h"), "w").write(targets.GetHeader()) |
996 open(os.path.join(buildpath,"beremiz.h"), "w").write(targets.GetHeader()) |
959 |
997 |
960 # Template based part of C code generation |
998 # Template based part of C code generation |
961 # files are stacked at the beginning, as files of confnode tree root |
999 # files are stacked at the beginning, as files of confnode tree root |
976 except Exception, exc: |
1014 except Exception, exc: |
977 self.logger.write_error(name+_(" generation failed !\n")) |
1015 self.logger.write_error(name+_(" generation failed !\n")) |
978 self.logger.write_error(traceback.format_exc()) |
1016 self.logger.write_error(traceback.format_exc()) |
979 self.ResetBuildMD5() |
1017 self.ResetBuildMD5() |
980 return False |
1018 return False |
981 |
|
982 self.logger.write(_("C code generated successfully.\n")) |
1019 self.logger.write(_("C code generated successfully.\n")) |
983 |
|
984 # Get current or fresh builder |
|
985 builder = self.GetBuilder() |
|
986 if builder is None: |
|
987 self.logger.write_error(_("Fatal : cannot get builder.\n")) |
|
988 self.ResetBuildMD5() |
|
989 return False |
|
990 |
|
991 # Build |
|
992 try: |
|
993 if not builder.build() : |
|
994 self.logger.write_error(_("C Build failed.\n")) |
|
995 return False |
|
996 except Exception, exc: |
|
997 self.logger.write_error(_("C Build crashed !\n")) |
|
998 self.logger.write_error(traceback.format_exc()) |
|
999 self.ResetBuildMD5() |
|
1000 return False |
|
1001 |
|
1002 self.logger.write(_("Successfully built.\n")) |
|
1003 # Update GUI status about need for transfer |
|
1004 self.CompareLocalAndRemotePLC() |
|
1005 return True |
1020 return True |
1006 |
1021 |
1007 def ShowError(self, logger, from_location, to_location): |
1022 def ShowError(self, logger, from_location, to_location): |
1008 chunk_infos = self.GetChunkInfos(from_location, to_location) |
1023 chunk_infos = self.GetChunkInfos(from_location, to_location) |
1009 for infos, (start_row, start_col) in chunk_infos: |
1024 for infos, (start_row, start_col) in chunk_infos: |
1010 start = (from_location[0] - start_row, from_location[1] - start_col) |
1025 start = (from_location[0] - start_row, from_location[1] - start_col) |
1011 end = (to_location[0] - start_row, to_location[1] - start_col) |
1026 end = (to_location[0] - start_row, to_location[1] - start_col) |
1012 if self.AppFrame is not None: |
1027 if self.AppFrame is not None: |
1013 self.AppFrame.ShowError(infos, start, end) |
1028 self.AppFrame.ShowError(infos, start, end) |
1014 |
1029 |
1015 _IECCodeView = None |
1030 _IECCodeView = None |
1016 def _showIECcode(self): |
1031 def _showIECcode(self): |
1017 self._OpenView("IEC code") |
1032 self._OpenView("IEC code") |
1018 |
1033 |
1019 _IECRawCodeView = None |
1034 _IECRawCodeView = None |
1020 def _editIECrawcode(self): |
1035 def _editIECrawcode(self): |
1021 self._OpenView("IEC raw code") |
1036 self._OpenView("IEC raw code") |
1022 |
1037 |
1023 _ProjectFilesView = None |
1038 _ProjectFilesView = None |
1024 def _OpenProjectFiles(self): |
1039 def _OpenProjectFiles(self): |
1025 self._OpenView("Project Files") |
1040 self._OpenView("Project Files") |
1026 |
1041 |
1027 _FileEditors = {} |
1042 _FileEditors = {} |
1028 def _OpenFileEditor(self, filepath): |
1043 def _OpenFileEditor(self, filepath): |
1029 self._OpenView(filepath) |
1044 self._OpenView(filepath) |
1030 |
1045 |
1031 def _OpenView(self, name=None, onlyopened=False): |
1046 def _OpenView(self, name=None, onlyopened=False): |
1032 if name == "IEC code": |
1047 if name == "IEC code": |
1033 if self._IECCodeView is None: |
1048 if self._IECCodeView is None: |
1034 plc_file = self._getIECcodepath() |
1049 plc_file = self._getIECcodepath() |
1035 |
1050 |
1036 self._IECCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name) |
1051 self._IECCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name) |
1037 self._IECCodeView.SetTextSyntax("ALL") |
1052 self._IECCodeView.SetTextSyntax("ALL") |
1038 self._IECCodeView.SetKeywords(IEC_KEYWORDS) |
1053 self._IECCodeView.SetKeywords(IEC_KEYWORDS) |
1039 try: |
1054 try: |
1040 text = file(plc_file).read() |
1055 text = file(plc_file).read() |
1041 except: |
1056 except: |
1042 text = '(* No IEC code have been generated at that time ! *)' |
1057 text = '(* No IEC code have been generated at that time ! *)' |
1043 self._IECCodeView.SetText(text = text) |
1058 self._IECCodeView.SetText(text = text) |
1044 self._IECCodeView.SetIcon(GetBitmap("ST")) |
1059 self._IECCodeView.SetIcon(GetBitmap("ST")) |
1045 setattr(self._IECCodeView, "_OnClose", self.OnCloseEditor) |
1060 setattr(self._IECCodeView, "_OnClose", self.OnCloseEditor) |
1046 |
1061 |
1047 if self._IECCodeView is not None: |
1062 if self._IECCodeView is not None: |
1048 self.AppFrame.EditProjectElement(self._IECCodeView, name) |
1063 self.AppFrame.EditProjectElement(self._IECCodeView, name) |
1049 |
1064 |
1050 return self._IECCodeView |
1065 return self._IECCodeView |
1051 |
1066 |
1052 elif name == "IEC raw code": |
1067 elif name == "IEC raw code": |
1053 if self._IECRawCodeView is None: |
1068 if self._IECRawCodeView is None: |
1054 controler = MiniTextControler(self._getIECrawcodepath(), self) |
1069 controler = MiniTextControler(self._getIECrawcodepath(), self) |
1055 |
1070 |
1056 self._IECRawCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name) |
1071 self._IECRawCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name) |
1057 self._IECRawCodeView.SetTextSyntax("ALL") |
1072 self._IECRawCodeView.SetTextSyntax("ALL") |
1058 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS) |
1073 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS) |
1059 self._IECRawCodeView.RefreshView() |
1074 self._IECRawCodeView.RefreshView() |
1060 self._IECRawCodeView.SetIcon(GetBitmap("ST")) |
1075 self._IECRawCodeView.SetIcon(GetBitmap("ST")) |
1061 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor) |
1076 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor) |
1062 |
1077 |
1063 if self._IECRawCodeView is not None: |
1078 if self._IECRawCodeView is not None: |
1064 self.AppFrame.EditProjectElement(self._IECRawCodeView, name) |
1079 self.AppFrame.EditProjectElement(self._IECRawCodeView, name) |
1065 |
1080 |
1066 return self._IECRawCodeView |
1081 return self._IECRawCodeView |
1067 |
1082 |
1068 elif name == "Project Files": |
1083 elif name == "Project Files": |
1069 if self._ProjectFilesView is None: |
1084 if self._ProjectFilesView is None: |
1070 self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True) |
1085 self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True) |
1071 |
1086 |
1072 extensions = [] |
1087 extensions = [] |
1073 for extension, name, editor in features.file_editors: |
1088 for extension, name, editor in features.file_editors: |
1074 if extension not in extensions: |
1089 if extension not in extensions: |
1075 extensions.append(extension) |
1090 extensions.append(extension) |
1076 self._ProjectFilesView.SetEditableFileExtensions(extensions) |
1091 self._ProjectFilesView.SetEditableFileExtensions(extensions) |
1077 |
1092 |
1078 if self._ProjectFilesView is not None: |
1093 if self._ProjectFilesView is not None: |
1079 self.AppFrame.EditProjectElement(self._ProjectFilesView, name) |
1094 self.AppFrame.EditProjectElement(self._ProjectFilesView, name) |
1080 |
1095 |
1081 return self._ProjectFilesView |
1096 return self._ProjectFilesView |
1082 |
1097 |
1083 elif name is not None and name.find("::") != -1: |
1098 elif name is not None and name.find("::") != -1: |
1084 filepath, editor_name = name.split("::") |
1099 filepath, editor_name = name.split("::") |
1085 if not self._FileEditors.has_key(filepath): |
1100 if not self._FileEditors.has_key(filepath): |
1086 if os.path.isfile(filepath): |
1101 if os.path.isfile(filepath): |
1087 file_extension = os.path.splitext(filepath)[1] |
1102 file_extension = os.path.splitext(filepath)[1] |
1088 |
1103 |
1089 editors = dict([(edit_name, edit_class) |
1104 editors = dict([(edit_name, edit_class) |
1090 for extension, edit_name, edit_class in features.file_editors |
1105 for extension, edit_name, edit_class in features.file_editors |
1091 if extension == file_extension]) |
1106 if extension == file_extension]) |
1092 |
1107 |
1093 if editor_name == "": |
1108 if editor_name == "": |
1094 if len(editors) == 1: |
1109 if len(editors) == 1: |
1095 editor_name = editors.keys()[0] |
1110 editor_name = editors.keys()[0] |
1096 elif len(editors) > 0: |
1111 elif len(editors) > 0: |
1097 names = editors.keys() |
1112 names = editors.keys() |
1098 dialog = wx.SingleChoiceDialog(self.AppFrame, |
1113 dialog = wx.SingleChoiceDialog(self.AppFrame, |
1099 _("Select an editor:"), _("Editor selection"), |
1114 _("Select an editor:"), _("Editor selection"), |
1100 names, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) |
1115 names, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) |
1101 if dialog.ShowModal() == wx.ID_OK: |
1116 if dialog.ShowModal() == wx.ID_OK: |
1102 editor_name = names[dialog.GetSelection()] |
1117 editor_name = names[dialog.GetSelection()] |
1103 dialog.Destroy() |
1118 dialog.Destroy() |
1104 |
1119 |
1105 if editor_name != "": |
1120 if editor_name != "": |
1106 name = "::".join([filepath, editor_name]) |
1121 name = "::".join([filepath, editor_name]) |
1107 |
1122 |
1108 editor = editors[editor_name]() |
1123 editor = editors[editor_name]() |
1109 self._FileEditors[filepath] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame) |
1124 self._FileEditors[filepath] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame) |
1110 self._FileEditors[filepath].SetIcon(GetBitmap("FILE")) |
1125 self._FileEditors[filepath].SetIcon(GetBitmap("FILE")) |
1111 if isinstance(self._FileEditors[filepath], DebugViewer): |
1126 if isinstance(self._FileEditors[filepath], DebugViewer): |
1112 self._FileEditors[filepath].SetDataProducer(self) |
1127 self._FileEditors[filepath].SetDataProducer(self) |
1113 |
1128 |
1114 if self._FileEditors.has_key(filepath): |
1129 if self._FileEditors.has_key(filepath): |
1115 editor = self._FileEditors[filepath] |
1130 editor = self._FileEditors[filepath] |
1116 self.AppFrame.EditProjectElement(editor, editor.GetTagName()) |
1131 self.AppFrame.EditProjectElement(editor, editor.GetTagName()) |
1117 |
1132 |
1118 return self._FileEditors.get(filepath) |
1133 return self._FileEditors.get(filepath) |
1119 else: |
1134 else: |
1120 return ConfigTreeNode._OpenView(self, self.CTNName(), onlyopened) |
1135 return ConfigTreeNode._OpenView(self, self.CTNName(), onlyopened) |
1121 |
1136 |
1122 def OnCloseEditor(self, view): |
1137 def OnCloseEditor(self, view): |
1145 |
1160 |
1146 def UpdatePLCLog(self, log_count): |
1161 def UpdatePLCLog(self, log_count): |
1147 if log_count: |
1162 if log_count: |
1148 if self.AppFrame is not None: |
1163 if self.AppFrame is not None: |
1149 self.AppFrame.LogViewer.SetLogCounters(log_count) |
1164 self.AppFrame.LogViewer.SetLogCounters(log_count) |
1150 |
1165 |
1151 def UpdateMethodsFromPLCStatus(self): |
1166 def UpdateMethodsFromPLCStatus(self): |
1152 status = None |
1167 status = None |
1153 if self._connector is not None: |
1168 if self._connector is not None: |
1154 PLCstatus = self._connector.GetPLCstatus() |
1169 PLCstatus = self._connector.GetPLCstatus() |
1155 if PLCstatus is not None: |
1170 if PLCstatus is not None: |
1182 self.AppFrame.ConnectionStatusBar.SetStatusText('', 2) |
1197 self.AppFrame.ConnectionStatusBar.SetStatusText('', 2) |
1183 else: |
1198 else: |
1184 self.AppFrame.ConnectionStatusBar.SetStatusText( |
1199 self.AppFrame.ConnectionStatusBar.SetStatusText( |
1185 _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1) |
1200 _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1) |
1186 self.AppFrame.ConnectionStatusBar.SetStatusText(_(status), 2) |
1201 self.AppFrame.ConnectionStatusBar.SetStatusText(_(status), 2) |
1187 |
1202 |
1188 def PullPLCStatusProc(self, event): |
1203 def PullPLCStatusProc(self, event): |
1189 self.UpdateMethodsFromPLCStatus() |
1204 self.UpdateMethodsFromPLCStatus() |
1190 |
1205 |
1191 def SnapshotAndResetDebugValuesBuffers(self): |
1206 def SnapshotAndResetDebugValuesBuffers(self): |
1192 buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers, |
1207 buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers, |
1193 [list() for iec_path in self.TracedIECPath]) |
1208 [list() for iec_path in self.TracedIECPath]) |
1194 ticks, self.DebugTicks = self.DebugTicks, [] |
1209 ticks, self.DebugTicks = self.DebugTicks, [] |
1195 return ticks, buffers |
1210 return ticks, buffers |
1196 |
1211 |
1197 def RegisterDebugVarToConnector(self): |
1212 def RegisterDebugVarToConnector(self): |
1198 self.DebugTimer=None |
1213 self.DebugTimer=None |
1199 Idxs = [] |
1214 Idxs = [] |
1200 self.TracedIECPath = [] |
1215 self.TracedIECPath = [] |
1201 if self._connector is not None: |
1216 if self._connector is not None: |
1206 if len(WeakCallableDict) == 0: |
1221 if len(WeakCallableDict) == 0: |
1207 # Callable Dict is empty. |
1222 # Callable Dict is empty. |
1208 # This variable is not needed anymore! |
1223 # This variable is not needed anymore! |
1209 IECPathsToPop.append(IECPath) |
1224 IECPathsToPop.append(IECPath) |
1210 elif IECPath != "__tick__": |
1225 elif IECPath != "__tick__": |
1211 # Convert |
1226 # Convert |
1212 Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) |
1227 Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) |
1213 if Idx is not None: |
1228 if Idx is not None: |
1214 if IEC_Type in DebugTypesSize: |
1229 if IEC_Type in DebugTypesSize: |
1215 Idxs.append((Idx, IEC_Type, fvalue, IECPath)) |
1230 Idxs.append((Idx, IEC_Type, fvalue, IECPath)) |
1216 else: |
1231 else: |
1217 self.logger.write_warning(_("Debug: Unsupported type to debug '%s'\n")%IEC_Type) |
1232 self.logger.write_warning(_("Debug: Unsupported type to debug '%s'\n")%IEC_Type) |
1218 else: |
1233 else: |
1219 self.logger.write_warning(_("Debug: Unknown variable '%s'\n")%IECPath) |
1234 self.logger.write_warning(_("Debug: Unknown variable '%s'\n")%IECPath) |
1227 else: |
1242 else: |
1228 self.TracedIECPath = [] |
1243 self.TracedIECPath = [] |
1229 self._connector.SetTraceVariablesList([]) |
1244 self._connector.SetTraceVariablesList([]) |
1230 self.SnapshotAndResetDebugValuesBuffers() |
1245 self.SnapshotAndResetDebugValuesBuffers() |
1231 self.IECdebug_lock.release() |
1246 self.IECdebug_lock.release() |
1232 |
1247 |
1233 def IsPLCStarted(self): |
1248 def IsPLCStarted(self): |
1234 return self.previous_plcstate == "Started" |
1249 return self.previous_plcstate == "Started" |
1235 |
1250 |
1236 def ReArmDebugRegisterTimer(self): |
1251 def ReArmDebugRegisterTimer(self): |
1237 if self.DebugTimer is not None: |
1252 if self.DebugTimer is not None: |
1238 self.DebugTimer.cancel() |
1253 self.DebugTimer.cancel() |
1239 |
1254 |
1240 # Prevent to call RegisterDebugVarToConnector when PLC is not started |
1255 # Prevent to call RegisterDebugVarToConnector when PLC is not started |
1241 # If an output location var is forced it's leads to segmentation fault in runtime |
1256 # If an output location var is forced it's leads to segmentation fault in runtime |
1242 # Links between PLC located variables and real variables are not ready |
1257 # Links between PLC located variables and real variables are not ready |
1243 if self.IsPLCStarted(): |
1258 if self.IsPLCStarted(): |
1244 # Timer to prevent rapid-fire when registering many variables |
1259 # Timer to prevent rapid-fire when registering many variables |
1248 self.DebugTimer.start() |
1263 self.DebugTimer.start() |
1249 |
1264 |
1250 def GetDebugIECVariableType(self, IECPath): |
1265 def GetDebugIECVariableType(self, IECPath): |
1251 Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) |
1266 Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) |
1252 return IEC_Type |
1267 return IEC_Type |
1253 |
1268 |
1254 def SubscribeDebugIECVariable(self, IECPath, callableobj, buffer_list=False, *args, **kwargs): |
1269 def SubscribeDebugIECVariable(self, IECPath, callableobj, buffer_list=False, *args, **kwargs): |
1255 """ |
1270 """ |
1256 Dispatching use a dictionnary linking IEC variable paths |
1271 Dispatching use a dictionnary linking IEC variable paths |
1257 to a WeakKeyDictionary linking |
1272 to a WeakKeyDictionary linking |
1258 weakly referenced callables to optionnal args |
1273 weakly referenced callables to optionnal args |
1259 """ |
1274 """ |
1260 if IECPath != "__tick__" and not self._IECPathToIdx.has_key(IECPath): |
1275 if IECPath != "__tick__" and not self._IECPathToIdx.has_key(IECPath): |
1261 return None |
1276 return None |
1262 |
1277 |
1263 self.IECdebug_lock.acquire() |
1278 self.IECdebug_lock.acquire() |
1264 # If no entry exist, create a new one with a fresh WeakKeyDictionary |
1279 # If no entry exist, create a new one with a fresh WeakKeyDictionary |
1265 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1280 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1266 if IECdebug_data is None: |
1281 if IECdebug_data is None: |
1267 IECdebug_data = [ |
1282 IECdebug_data = [ |
1271 None, |
1286 None, |
1272 buffer_list] # Forced value |
1287 buffer_list] # Forced value |
1273 self.IECdebug_datas[IECPath] = IECdebug_data |
1288 self.IECdebug_datas[IECPath] = IECdebug_data |
1274 else: |
1289 else: |
1275 IECdebug_data[4] |= buffer_list |
1290 IECdebug_data[4] |= buffer_list |
1276 |
1291 |
1277 IECdebug_data[0][callableobj]=(buffer_list, args, kwargs) |
1292 IECdebug_data[0][callableobj]=(buffer_list, args, kwargs) |
1278 |
1293 |
1279 self.IECdebug_lock.release() |
1294 self.IECdebug_lock.release() |
1280 |
1295 |
1281 self.ReArmDebugRegisterTimer() |
1296 self.ReArmDebugRegisterTimer() |
1282 |
1297 |
1283 return IECdebug_data[1] |
1298 return IECdebug_data[1] |
1284 |
1299 |
1285 def UnsubscribeDebugIECVariable(self, IECPath, callableobj): |
1300 def UnsubscribeDebugIECVariable(self, IECPath, callableobj): |
1286 self.IECdebug_lock.acquire() |
1301 self.IECdebug_lock.acquire() |
1287 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1302 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1290 if len(IECdebug_data[0]) == 0: |
1305 if len(IECdebug_data[0]) == 0: |
1291 self.IECdebug_datas.pop(IECPath) |
1306 self.IECdebug_datas.pop(IECPath) |
1292 else: |
1307 else: |
1293 IECdebug_data[4] = reduce( |
1308 IECdebug_data[4] = reduce( |
1294 lambda x, y: x|y, |
1309 lambda x, y: x|y, |
1295 [buffer_list for buffer_list,args,kwargs |
1310 [buffer_list for buffer_list,args,kwargs |
1296 in IECdebug_data[0].itervalues()], |
1311 in IECdebug_data[0].itervalues()], |
1297 False) |
1312 False) |
1298 self.IECdebug_lock.release() |
1313 self.IECdebug_lock.release() |
1299 |
1314 |
1300 self.ReArmDebugRegisterTimer() |
1315 self.ReArmDebugRegisterTimer() |
1307 self.ReArmDebugRegisterTimer() |
1322 self.ReArmDebugRegisterTimer() |
1308 |
1323 |
1309 def ForceDebugIECVariable(self, IECPath, fvalue): |
1324 def ForceDebugIECVariable(self, IECPath, fvalue): |
1310 if not self.IECdebug_datas.has_key(IECPath): |
1325 if not self.IECdebug_datas.has_key(IECPath): |
1311 return |
1326 return |
1312 |
1327 |
1313 self.IECdebug_lock.acquire() |
1328 self.IECdebug_lock.acquire() |
1314 |
1329 |
1315 # If no entry exist, create a new one with a fresh WeakKeyDictionary |
1330 # If no entry exist, create a new one with a fresh WeakKeyDictionary |
1316 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1331 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1317 IECdebug_data[2] = "Forced" |
1332 IECdebug_data[2] = "Forced" |
1318 IECdebug_data[3] = fvalue |
1333 IECdebug_data[3] = fvalue |
1319 |
1334 |
1320 self.IECdebug_lock.release() |
1335 self.IECdebug_lock.release() |
1321 |
1336 |
1322 self.ReArmDebugRegisterTimer() |
1337 self.ReArmDebugRegisterTimer() |
1323 |
1338 |
1324 def ReleaseDebugIECVariable(self, IECPath): |
1339 def ReleaseDebugIECVariable(self, IECPath): |
1325 if not self.IECdebug_datas.has_key(IECPath): |
1340 if not self.IECdebug_datas.has_key(IECPath): |
1326 return |
1341 return |
1327 |
1342 |
1328 self.IECdebug_lock.acquire() |
1343 self.IECdebug_lock.acquire() |
1329 |
1344 |
1330 # If no entry exist, create a new one with a fresh WeakKeyDictionary |
1345 # If no entry exist, create a new one with a fresh WeakKeyDictionary |
1331 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1346 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1332 IECdebug_data[2] = "Registered" |
1347 IECdebug_data[2] = "Registered" |
1333 IECdebug_data[3] = None |
1348 IECdebug_data[3] = None |
1334 |
1349 |
1335 self.IECdebug_lock.release() |
1350 self.IECdebug_lock.release() |
1336 |
1351 |
1337 self.ReArmDebugRegisterTimer() |
1352 self.ReArmDebugRegisterTimer() |
1338 |
1353 |
1339 def CallWeakcallables(self, IECPath, function_name, *cargs): |
1354 def CallWeakcallables(self, IECPath, function_name, *cargs): |
1340 data_tuple = self.IECdebug_datas.get(IECPath, None) |
1355 data_tuple = self.IECdebug_datas.get(IECPath, None) |
1341 if data_tuple is not None: |
1356 if data_tuple is not None: |
1342 WeakCallableDict, data_log, status, fvalue, buffer_list = data_tuple |
1357 WeakCallableDict, data_log, status, fvalue, buffer_list = data_tuple |
1343 #data_log.append((debug_tick, value)) |
1358 #data_log.append((debug_tick, value)) |
1413 for IECPath, values in zip(self.TracedIECPath, buffers): |
1428 for IECPath, values in zip(self.TracedIECPath, buffers): |
1414 if len(values) > 0: |
1429 if len(values) > 0: |
1415 self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values) |
1430 self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values) |
1416 if len(debug_ticks) > 0: |
1431 if len(debug_ticks) > 0: |
1417 self.CallWeakcallables("__tick__", "NewDataAvailable", debug_ticks) |
1432 self.CallWeakcallables("__tick__", "NewDataAvailable", debug_ticks) |
1418 |
1433 |
1419 delay = time.time() - start_time |
1434 delay = time.time() - start_time |
1420 next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay) |
1435 next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay) |
1421 if self.DispatchDebugValuesTimer is not None and self.DebugThread is not None: |
1436 if self.DispatchDebugValuesTimer is not None and self.DebugThread is not None: |
1422 self.DispatchDebugValuesTimer.Start( |
1437 self.DispatchDebugValuesTimer.Start( |
1423 int(next_refresh * 1000), oneShot=True) |
1438 int(next_refresh * 1000), oneShot=True) |
1435 self.logger.write(_("Debugger stopped.\n")) |
1450 self.logger.write(_("Debugger stopped.\n")) |
1436 self.DebugThread = None |
1451 self.DebugThread = None |
1437 if self.DispatchDebugValuesTimer is not None: |
1452 if self.DispatchDebugValuesTimer is not None: |
1438 self.DispatchDebugValuesTimer.Stop() |
1453 self.DispatchDebugValuesTimer.Stop() |
1439 |
1454 |
1440 def _connect_debug(self): |
1455 def _connect_debug(self): |
1441 self.previous_plcstate = None |
1456 self.previous_plcstate = None |
1442 if self.AppFrame: |
1457 if self.AppFrame: |
1443 self.AppFrame.ResetGraphicViewers() |
1458 self.AppFrame.ResetGraphicViewers() |
1444 self.RegisterDebugVarToConnector() |
1459 self.RegisterDebugVarToConnector() |
1445 if self.DispatchDebugValuesTimer is not None: |
1460 if self.DispatchDebugValuesTimer is not None: |
1446 self.DispatchDebugValuesTimer.Start( |
1461 self.DispatchDebugValuesTimer.Start( |
1447 int(REFRESH_PERIOD * 1000), oneShot=True) |
1462 int(REFRESH_PERIOD * 1000), oneShot=True) |
1448 if self.DebugThread is None: |
1463 if self.DebugThread is None: |
1449 self.DebugThread = Thread(target=self.DebugThreadProc) |
1464 self.DebugThread = Thread(target=self.DebugThreadProc) |
1450 self.DebugThread.start() |
1465 self.DebugThread.start() |
1451 |
1466 |
1452 def _Run(self): |
1467 def _Run(self): |
1453 """ |
1468 """ |
1454 Start PLC |
1469 Start PLC |
1455 """ |
1470 """ |
1456 if self.GetIECProgramsAndVariables(): |
1471 if self.GetIECProgramsAndVariables(): |
1458 self.logger.write(_("Starting PLC\n")) |
1473 self.logger.write(_("Starting PLC\n")) |
1459 self._connect_debug() |
1474 self._connect_debug() |
1460 else: |
1475 else: |
1461 self.logger.write_error(_("Couldn't start PLC !\n")) |
1476 self.logger.write_error(_("Couldn't start PLC !\n")) |
1462 wx.CallAfter(self.UpdateMethodsFromPLCStatus) |
1477 wx.CallAfter(self.UpdateMethodsFromPLCStatus) |
1463 |
1478 |
1464 def _Stop(self): |
1479 def _Stop(self): |
1465 """ |
1480 """ |
1466 Stop PLC |
1481 Stop PLC |
1467 """ |
1482 """ |
1468 if self._connector is not None and not self._connector.StopPLC(): |
1483 if self._connector is not None and not self._connector.StopPLC(): |
1469 self.logger.write_error(_("Couldn't stop PLC !\n")) |
1484 self.logger.write_error(_("Couldn't stop PLC !\n")) |
1470 |
1485 |
1471 # debugthread should die on his own |
1486 # debugthread should die on his own |
1472 #self.KillDebugThread() |
1487 #self.KillDebugThread() |
1473 |
1488 |
1474 wx.CallAfter(self.UpdateMethodsFromPLCStatus) |
1489 wx.CallAfter(self.UpdateMethodsFromPLCStatus) |
1475 |
1490 |
1476 def _SetConnector(self, connector, update_status=True): |
1491 def _SetConnector(self, connector, update_status=True): |
1477 self._connector = connector |
1492 self._connector = connector |
1478 if self.AppFrame is not None: |
1493 if self.AppFrame is not None: |
1491 def _Connect(self): |
1506 def _Connect(self): |
1492 # don't accept re-connetion if already connected |
1507 # don't accept re-connetion if already connected |
1493 if self._connector is not None: |
1508 if self._connector is not None: |
1494 self.logger.write_error(_("Already connected. Please disconnect\n")) |
1509 self.logger.write_error(_("Already connected. Please disconnect\n")) |
1495 return |
1510 return |
1496 |
1511 |
1497 # Get connector uri |
1512 # Get connector uri |
1498 uri = self.\ |
1513 uri = self.\ |
1499 BeremizRoot.\ |
1514 BeremizRoot.\ |
1500 getURI_location().\ |
1515 getURI_location().\ |
1501 strip() |
1516 strip() |
1510 dialog.Destroy() |
1525 dialog.Destroy() |
1511 except: |
1526 except: |
1512 self.logger.write_error(_("Local service discovery failed!\n")) |
1527 self.logger.write_error(_("Local service discovery failed!\n")) |
1513 self.logger.write_error(traceback.format_exc()) |
1528 self.logger.write_error(traceback.format_exc()) |
1514 uri = None |
1529 uri = None |
1515 |
1530 |
1516 # Nothing choosed or cancel button |
1531 # Nothing choosed or cancel button |
1517 if uri is None or answer == wx.ID_CANCEL: |
1532 if uri is None or answer == wx.ID_CANCEL: |
1518 self.logger.write_error(_("Connection canceled!\n")) |
1533 self.logger.write_error(_("Connection canceled!\n")) |
1519 return |
1534 return |
1520 else: |
1535 else: |
1527 if self.AppFrame is not None: |
1542 if self.AppFrame is not None: |
1528 self.AppFrame.RefreshTitle() |
1543 self.AppFrame.RefreshTitle() |
1529 self.AppFrame.RefreshFileMenu() |
1544 self.AppFrame.RefreshFileMenu() |
1530 self.AppFrame.RefreshEditMenu() |
1545 self.AppFrame.RefreshEditMenu() |
1531 self.AppFrame.RefreshPageTitles() |
1546 self.AppFrame.RefreshPageTitles() |
1532 |
1547 |
1533 # Get connector from uri |
1548 # Get connector from uri |
1534 try: |
1549 try: |
1535 self._SetConnector(connectors.ConnectorFactory(uri, self)) |
1550 self._SetConnector(connectors.ConnectorFactory(uri, self)) |
1536 except Exception, msg: |
1551 except Exception, msg: |
1537 self.logger.write_error(_("Exception while connecting %s!\n")%uri) |
1552 self.logger.write_error(_("Exception while connecting %s!\n")%uri) |
1545 self.ShowMethod("_Connect", False) |
1560 self.ShowMethod("_Connect", False) |
1546 self.ShowMethod("_Disconnect", True) |
1561 self.ShowMethod("_Disconnect", True) |
1547 self.ShowMethod("_Transfer", True) |
1562 self.ShowMethod("_Transfer", True) |
1548 |
1563 |
1549 self.CompareLocalAndRemotePLC() |
1564 self.CompareLocalAndRemotePLC() |
1550 |
1565 |
1551 # Init with actual PLC status and print it |
1566 # Init with actual PLC status and print it |
1552 self.UpdateMethodsFromPLCStatus() |
1567 self.UpdateMethodsFromPLCStatus() |
1553 if self.previous_plcstate is not None: |
1568 if self.previous_plcstate is not None: |
1554 status = _(self.previous_plcstate) |
1569 status = _(self.previous_plcstate) |
1555 else: |
1570 else: |
1556 status = "" |
1571 status = "" |
1557 |
1572 |
1558 #self.logger.write(_("PLC is %s\n")%status) |
1573 #self.logger.write(_("PLC is %s\n")%status) |
1559 |
1574 |
1560 if self.previous_plcstate in ["Started","Stopped"]: |
1575 if self.previous_plcstate in ["Started","Stopped"]: |
1561 if self.DebugAvailable() and self.GetIECProgramsAndVariables(): |
1576 if self.DebugAvailable() and self.GetIECProgramsAndVariables(): |
1562 self.logger.write(_("Debugger ready\n")) |
1577 self.logger.write(_("Debugger ready\n")) |
1563 self._connect_debug() |
1578 self._connect_debug() |
1564 else: |
1579 else: |
1588 self.EnableMethod("_Transfer", False) |
1603 self.EnableMethod("_Transfer", False) |
1589 |
1604 |
1590 |
1605 |
1591 def _Disconnect(self): |
1606 def _Disconnect(self): |
1592 self._SetConnector(None) |
1607 self._SetConnector(None) |
1593 |
1608 |
1594 def _Transfer(self): |
1609 def _Transfer(self): |
1595 # Get the last build PLC's |
1610 # Get the last build PLC's |
1596 MD5 = self.GetLastBuildMD5() |
1611 MD5 = self.GetLastBuildMD5() |
1597 |
1612 |
1598 # Check if md5 file is empty : ask user to build PLC |
1613 # Check if md5 file is empty : ask user to build PLC |
1599 if MD5 is None : |
1614 if MD5 is None : |
1600 self.logger.write_error(_("Failed : Must build before transfer.\n")) |
1615 self.logger.write_error(_("Failed : Must build before transfer.\n")) |
1601 return False |
1616 return False |
1602 |
1617 |
1603 # Compare PLC project with PLC on target |
1618 # Compare PLC project with PLC on target |
1607 |
1622 |
1608 # Get temprary directory path |
1623 # Get temprary directory path |
1609 extrafiles = [] |
1624 extrafiles = [] |
1610 for extrafilespath in [self._getExtraFilesPath(), |
1625 for extrafilespath in [self._getExtraFilesPath(), |
1611 self._getProjectFilesPath()]: |
1626 self._getProjectFilesPath()]: |
1612 |
1627 |
1613 extrafiles.extend( |
1628 extrafiles.extend( |
1614 [(name, open(os.path.join(extrafilespath, name), |
1629 [(name, open(os.path.join(extrafilespath, name), |
1615 'rb').read()) \ |
1630 'rb').read()) \ |
1616 for name in os.listdir(extrafilespath)]) |
1631 for name in os.listdir(extrafilespath)]) |
1617 |
1632 |
1618 # Send PLC on target |
1633 # Send PLC on target |
1619 builder = self.GetBuilder() |
1634 builder = self.GetBuilder() |
1620 if builder is not None: |
1635 if builder is not None: |
1621 data = builder.GetBinaryCode() |
1636 data = builder.GetBinaryCode() |
1622 if data is not None : |
1637 if data is not None : |