9 from xml.dom import minidom |
9 from xml.dom import minidom |
10 import wx |
10 import wx |
11 |
11 |
12 #Quick hack to be able to find Beremiz IEC tools. Should be config params. |
12 #Quick hack to be able to find Beremiz IEC tools. Should be config params. |
13 base_folder = os.path.split(sys.path[0])[0] |
13 base_folder = os.path.split(sys.path[0])[0] |
14 sys.path.append(os.path.join(base_folder, "plcopeneditor")) |
|
15 sys.path.append(os.path.join(base_folder, "docutils")) |
|
16 |
14 |
17 from docpdf import * |
15 from docpdf import * |
18 from xmlclass import GenerateClassesFromXSDstring |
16 from xmlclass import GenerateClassesFromXSDstring |
19 from wxPopen import ProcessLogger |
17 from wxPopen import ProcessLogger |
20 |
18 |
175 if self.MandatoryParams and parts[0] == self.MandatoryParams[0]: |
173 if self.MandatoryParams and parts[0] == self.MandatoryParams[0]: |
176 self.MandatoryParams[1].setElementValue(parts[1], value) |
174 self.MandatoryParams[1].setElementValue(parts[1], value) |
177 elif self.PlugParams and parts[0] == self.PlugParams[0]: |
175 elif self.PlugParams and parts[0] == self.PlugParams[0]: |
178 self.PlugParams[1].setElementValue(parts[1], value) |
176 self.PlugParams[1].setElementValue(parts[1], value) |
179 return value, False |
177 return value, False |
|
178 |
|
179 def PlugMakeDir(self): |
|
180 os.mkdir(self.PlugPath()) |
180 |
181 |
181 def PlugRequestSave(self): |
182 def PlugRequestSave(self): |
182 # If plugin do not have corresponding directory |
183 # If plugin do not have corresponding directory |
183 plugpath = self.PlugPath() |
184 plugpath = self.PlugPath() |
184 if not os.path.isdir(plugpath): |
185 if not os.path.isdir(plugpath): |
531 _self.LoadChilds() |
532 _self.LoadChilds() |
532 #just loaded, nothing to saved |
533 #just loaded, nothing to saved |
533 _self.ChangesToSave = False |
534 _self.ChangesToSave = False |
534 else: |
535 else: |
535 # If plugin do not have corresponding file/dirs - they will be created on Save |
536 # If plugin do not have corresponding file/dirs - they will be created on Save |
536 os.mkdir(_self.PlugPath()) |
537 _self.PlugMakeDir() |
537 # Find an IEC number |
538 # Find an IEC number |
538 _self.FindNewIEC_Channel(0) |
539 _self.FindNewIEC_Channel(0) |
539 # Call the plugin real __init__ |
540 # Call the plugin real __init__ |
540 if getattr(PlugClass, "__init__", None): |
541 if getattr(PlugClass, "__init__", None): |
541 PlugClass.__init__(_self) |
542 PlugClass.__init__(_self) |
720 self.MandatoryParams = None |
721 self.MandatoryParams = None |
721 self.AppFrame = frame |
722 self.AppFrame = frame |
722 self.logger = logger |
723 self.logger = logger |
723 self._builder = None |
724 self._builder = None |
724 self._connector = None |
725 self._connector = None |
|
726 self.Deleting = False |
725 |
727 |
726 # Setup debug information |
728 # Setup debug information |
727 self.IECdebug_datas = {} |
729 self.IECdebug_datas = {} |
728 self.IECdebug_lock = Lock() |
730 self.IECdebug_lock = Lock() |
729 |
731 |
746 # Keep track of the plugin type name |
748 # Keep track of the plugin type name |
747 self.PlugType = "Beremiz" |
749 self.PlugType = "Beremiz" |
748 # After __init__ root plugin is not valid |
750 # After __init__ root plugin is not valid |
749 self.ProjectPath = None |
751 self.ProjectPath = None |
750 self.BuildPath = None |
752 self.BuildPath = None |
751 self.PLCEditor = None |
|
752 self.PLCDebug = None |
|
753 self.DebugThread = None |
753 self.DebugThread = None |
754 self.debug_break = False |
754 self.debug_break = False |
755 self.previous_plcstate = None |
755 self.previous_plcstate = None |
756 self.StatusPrint = {"Broken": self.logger.write_error, |
756 self.StatusPrint = {"Broken": self.logger.write_error, |
757 None: lambda x: None} |
757 None: lambda x: None} |
758 # copy PluginMethods so that it can be later customized |
758 # copy PluginMethods so that it can be later customized |
759 self.PluginMethods = [dic.copy() for dic in self.PluginMethods] |
759 self.PluginMethods = [dic.copy() for dic in self.PluginMethods] |
760 self.LoadSTLibrary() |
760 self.LoadSTLibrary() |
|
761 |
|
762 def __del__(self): |
|
763 self.Deleting = True |
761 |
764 |
762 def PluginLibraryFilePath(self): |
765 def PluginLibraryFilePath(self): |
763 return os.path.join(os.path.split(__file__)[0], "pous.xml") |
766 return os.path.join(os.path.split(__file__)[0], "pous.xml") |
764 |
767 |
765 def PlugTestModified(self): |
768 def PlugTestModified(self): |
862 return None |
865 return None |
863 |
866 |
864 def SaveProject(self): |
867 def SaveProject(self): |
865 if not self.SaveXMLFile(): |
868 if not self.SaveXMLFile(): |
866 self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml')) |
869 self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml')) |
867 if self.PLCEditor: |
|
868 self.PLCEditor.RefreshTitle() |
|
869 result = self.PlugRequestSave() |
870 result = self.PlugRequestSave() |
870 if result: |
871 if result: |
871 self.logger.write_error(result) |
872 self.logger.write_error(result) |
872 |
|
873 def CloseProject(self): |
|
874 if self.PLCEditor is not None: |
|
875 self.PLCEditor.Close() |
|
876 if self.PLCDebug is not None: |
|
877 self.PLCDebug.Close() |
|
878 |
|
879 |
873 |
880 # Update PLCOpenEditor Plugin Block types from loaded plugins |
874 # Update PLCOpenEditor Plugin Block types from loaded plugins |
881 def RefreshPluginsBlockLists(self): |
875 def RefreshPluginsBlockLists(self): |
882 if getattr(self, "PluggedChilds", None) is not None: |
876 if getattr(self, "PluggedChilds", None) is not None: |
883 self.ClearPluginTypes() |
877 self.ClearPluginTypes() |
884 self.AddPluginBlockList(self.PluginsBlockTypesFactory()) |
878 self.AddPluginBlockList(self.PluginsBlockTypesFactory()) |
885 if self.PLCEditor is not None: |
879 if self.AppFrame is not None: |
886 self.PLCEditor.RefreshEditor() |
880 self.AppFrame.RefreshLibraryTree() |
|
881 self.AppFrame.RefreshEditor() |
887 |
882 |
888 def PluginPath(self): |
883 def PluginPath(self): |
889 return os.path.join(os.path.split(__file__)[0], "plugins") |
884 return os.path.join(os.path.split(__file__)[0], "plugins") |
890 |
885 |
891 def PlugPath(self, PlugName=None): |
886 def PlugPath(self, PlugName=None): |
1231 |
1226 |
1232 def _build(self): |
1227 def _build(self): |
1233 """ |
1228 """ |
1234 Method called by user to (re)build SoftPLC and plugin tree |
1229 Method called by user to (re)build SoftPLC and plugin tree |
1235 """ |
1230 """ |
1236 if self.PLCEditor is not None: |
1231 if self.AppFrame is not None: |
1237 self.PLCEditor.ClearErrors() |
1232 self.AppFrame.ClearErrors() |
1238 |
1233 |
1239 buildpath = self._getBuildPath() |
1234 buildpath = self._getBuildPath() |
1240 |
1235 |
1241 # Eventually create build dir |
1236 # Eventually create build dir |
1242 if not os.path.exists(buildpath): |
1237 if not os.path.exists(buildpath): |
1332 chunk_infos = self.GetChunkInfos(from_location, to_location) |
1327 chunk_infos = self.GetChunkInfos(from_location, to_location) |
1333 self._EditPLC() |
1328 self._EditPLC() |
1334 for infos, (start_row, start_col) in chunk_infos: |
1329 for infos, (start_row, start_col) in chunk_infos: |
1335 start = (from_location[0] - start_row, from_location[1] - start_col) |
1330 start = (from_location[0] - start_row, from_location[1] - start_col) |
1336 end = (to_location[0] - start_row, to_location[1] - start_col) |
1331 end = (to_location[0] - start_row, to_location[1] - start_col) |
1337 self.PLCEditor.ShowError(infos, start, end) |
1332 self.AppFrame.ShowError(infos, start, end) |
1338 |
1333 |
1339 def _showIECcode(self): |
1334 def _showIECcode(self): |
1340 plc_file = self._getIECcodepath() |
1335 plc_file = self._getIECcodepath() |
1341 new_dialog = wx.Frame(self.AppFrame) |
1336 new_dialog = wx.Frame(self.AppFrame) |
1342 ST_viewer = TextViewer(new_dialog, "", None, None) |
1337 ST_viewer = TextViewer(new_dialog, "", None, None) |
1473 |
1468 |
1474 def ReArmDebugRegisterTimer(self): |
1469 def ReArmDebugRegisterTimer(self): |
1475 if self.DebugTimer is not None: |
1470 if self.DebugTimer is not None: |
1476 self.DebugTimer.cancel() |
1471 self.DebugTimer.cancel() |
1477 |
1472 |
1478 # Timer to prevent rapid-fire when registering many variables |
1473 if not self.Deleting: |
1479 # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead |
1474 # Timer to prevent rapid-fire when registering many variables |
1480 self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector]) |
1475 # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead |
1481 # Rearm anti-rapid-fire timer |
1476 self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector]) |
1482 self.DebugTimer.start() |
1477 # Rearm anti-rapid-fire timer |
|
1478 self.DebugTimer.start() |
1483 |
1479 |
1484 |
1480 |
1485 def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs): |
1481 def SubscribeDebugIECVariable(self, IECPath, callableobj, *args, **kwargs): |
1486 """ |
1482 """ |
1487 Dispatching use a dictionnary linking IEC variable paths |
1483 Dispatching use a dictionnary linking IEC variable paths |
1577 Start PLC (Debug Mode) |
1573 Start PLC (Debug Mode) |
1578 """ |
1574 """ |
1579 if self.GetIECProgramsAndVariables(): |
1575 if self.GetIECProgramsAndVariables(): |
1580 self._connector.StartPLC(debug=True) |
1576 self._connector.StartPLC(debug=True) |
1581 self.logger.write(_("Starting PLC (debug mode)\n")) |
1577 self.logger.write(_("Starting PLC (debug mode)\n")) |
1582 if self.PLCDebug is None: |
1578 if self.AppFrame: |
1583 self.RefreshPluginsBlockLists() |
1579 self.AppFrame.ResetGraphicViewers() |
1584 def _onclose(): |
|
1585 self.PLCDebug = None |
|
1586 self.PLCDebug = PLCOpenEditor(self.AppFrame, self, debug=True) |
|
1587 self.PLCDebug._onclose = _onclose |
|
1588 self.PLCDebug.Show() |
|
1589 else: |
|
1590 self.PLCDebug.ResetGraphicViewers() |
|
1591 self.DebugThread = Thread(target=self.DebugThreadProc) |
1580 self.DebugThread = Thread(target=self.DebugThreadProc) |
1592 self.DebugThread.start() |
1581 self.DebugThread.start() |
1593 else: |
1582 else: |
1594 self.logger.write_error(_("Couldn't start PLC debug !\n")) |
1583 self.logger.write_error(_("Couldn't start PLC debug !\n")) |
1595 self.UpdateMethodsFromPLCStatus() |
1584 self.UpdateMethodsFromPLCStatus() |
1734 builder = self.GetBuilder() |
1723 builder = self.GetBuilder() |
1735 if builder is not None: |
1724 if builder is not None: |
1736 data = builder.GetBinaryCode() |
1725 data = builder.GetBinaryCode() |
1737 if data is not None : |
1726 if data is not None : |
1738 if self._connector.NewPLC(MD5, data, extrafiles): |
1727 if self._connector.NewPLC(MD5, data, extrafiles): |
1739 if self.PLCDebug is not None: |
1728 if self.AppFrame is not None: |
1740 self.PLCDebug.Close() |
1729 self.AppFrame.CloseDebugTabs() |
1741 self.PLCDebug = None |
|
1742 self.UnsubscribeAllDebugIECVariable() |
1730 self.UnsubscribeAllDebugIECVariable() |
1743 self.ProgramTransferred() |
1731 self.ProgramTransferred() |
1744 self.logger.write(_("Transfer completed successfully.\n")) |
1732 self.logger.write(_("Transfer completed successfully.\n")) |
1745 else: |
1733 else: |
1746 self.logger.write_error(_("Transfer failed\n")) |
1734 self.logger.write_error(_("Transfer failed\n")) |
1747 else: |
1735 else: |
1748 self.logger.write_error(_("No PLC to transfer (did build success ?)\n")) |
1736 self.logger.write_error(_("No PLC to transfer (did build success ?)\n")) |
1749 self.UpdateMethodsFromPLCStatus() |
1737 self.UpdateMethodsFromPLCStatus() |
1750 |
1738 |
1751 PluginMethods = [ |
1739 PluginMethods = [ |
1752 {"bitmap" : opjimg("editPLC"), |
|
1753 "name" : _("Edit PLC"), |
|
1754 "tooltip" : _("Edit PLC program with PLCOpenEditor"), |
|
1755 "method" : "_EditPLC"}, |
|
1756 {"bitmap" : opjimg("Build"), |
1740 {"bitmap" : opjimg("Build"), |
1757 "name" : _("Build"), |
1741 "name" : _("Build"), |
1758 "tooltip" : _("Build project into build folder"), |
1742 "tooltip" : _("Build project into build folder"), |
1759 "method" : "_build"}, |
1743 "method" : "_build"}, |
1760 {"bitmap" : opjimg("Clean"), |
1744 {"bitmap" : opjimg("Clean"), |