12 from datetime import datetime |
12 from datetime import datetime |
13 from weakref import WeakKeyDictionary |
13 from weakref import WeakKeyDictionary |
14 |
14 |
15 import targets |
15 import targets |
16 import connectors |
16 import connectors |
17 from util.misc import CheckPathPerm, GetClassImporter, IECCodeViewer |
17 from util.misc import CheckPathPerm, GetClassImporter |
18 from util.MiniTextControler import MiniTextControler |
18 from util.MiniTextControler import MiniTextControler |
19 from util.ProcessLogger import ProcessLogger |
19 from util.ProcessLogger import ProcessLogger |
20 from util.FileManagementPanel import FileManagementPanel |
20 from util.BitmapLibrary import GetBitmap |
|
21 from editors.FileManagementPanel import FileManagementPanel |
|
22 from editors.ProjectNodeEditor import ProjectNodeEditor |
|
23 from editors.IECCodeViewer import IECCodeViewer |
|
24 from graphics import DebugViewer |
|
25 from dialogs import DiscoveryDialog |
21 from PLCControler import PLCControler |
26 from PLCControler import PLCControler |
22 from TextViewer import TextViewer |
|
23 from plcopen.structures import IEC_KEYWORDS |
27 from plcopen.structures import IEC_KEYWORDS |
24 from targets.typemapping import DebugTypesSize |
28 from targets.typemapping import DebugTypesSize, LogLevelsCount, LogLevels |
25 from util.discovery import DiscoveryDialog |
|
26 from ConfigTreeNode import ConfigTreeNode |
29 from ConfigTreeNode import ConfigTreeNode |
27 from ProjectNodeEditor import ProjectNodeEditor |
|
28 from utils.BitmapLibrary import GetBitmap |
|
29 |
30 |
30 base_folder = os.path.split(sys.path[0])[0] |
31 base_folder = os.path.split(sys.path[0])[0] |
31 |
32 |
32 MATIEC_ERROR_MODEL = re.compile(".*\.st:(\d+)-(\d+)\.\.(\d+)-(\d+): error : (.*)$") |
33 MATIEC_ERROR_MODEL = re.compile(".*\.st:(\d+)-(\d+)\.\.(\d+)-(\d+): (?:error)|(?:warning) : (.*)$") |
33 |
34 |
34 DEBUG_RETRIES_WARN = 3 |
35 DEBUG_RETRIES_WARN = 3 |
35 DEBUG_RETRIES_REREGISTER = 4 |
36 DEBUG_RETRIES_REREGISTER = 4 |
36 |
37 |
37 ITEM_CONFNODE = 25 |
38 ITEM_CONFNODE = 25 |
110 self.ProjectPath = None |
111 self.ProjectPath = None |
111 self._setBuildPath(None) |
112 self._setBuildPath(None) |
112 self.DebugThread = None |
113 self.DebugThread = None |
113 self.debug_break = False |
114 self.debug_break = False |
114 self.previous_plcstate = None |
115 self.previous_plcstate = None |
|
116 self.previous_log_count = [None]*LogLevelsCount |
115 # copy ConfNodeMethods so that it can be later customized |
117 # copy ConfNodeMethods so that it can be later customized |
116 self.StatusMethods = [dic.copy() for dic in self.StatusMethods] |
118 self.StatusMethods = [dic.copy() for dic in self.StatusMethods] |
117 |
119 |
118 def LoadLibraries(self): |
120 def LoadLibraries(self): |
119 self.Libraries = [] |
121 self.Libraries = [] |
235 projectfiles_path = os.path.join(self.GetProjectPath(), "project_files") |
237 projectfiles_path = os.path.join(self.GetProjectPath(), "project_files") |
236 if not os.path.exists(projectfiles_path): |
238 if not os.path.exists(projectfiles_path): |
237 os.mkdir(projectfiles_path) |
239 os.mkdir(projectfiles_path) |
238 return projectfiles_path |
240 return projectfiles_path |
239 |
241 |
|
242 def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"): |
|
243 self.ProjectAddConfiguration(config_name) |
|
244 self.ProjectAddConfigurationResource(config_name, res_name) |
|
245 |
240 def NewProject(self, ProjectPath, BuildPath=None): |
246 def NewProject(self, ProjectPath, BuildPath=None): |
241 """ |
247 """ |
242 Create a new project in an empty folder |
248 Create a new project in an empty folder |
243 @param ProjectPath: path of the folder where project have to be created |
249 @param ProjectPath: path of the folder where project have to be created |
244 @param PLCParams: properties of the PLCOpen program created |
250 @param PLCParams: properties of the PLCOpen program created |
252 {"projectName": _("Unnamed"), |
258 {"projectName": _("Unnamed"), |
253 "productName": _("Unnamed"), |
259 "productName": _("Unnamed"), |
254 "productVersion": "1", |
260 "productVersion": "1", |
255 "companyName": _("Unknown"), |
261 "companyName": _("Unknown"), |
256 "creationDateTime": datetime(*localtime()[:6])}) |
262 "creationDateTime": datetime(*localtime()[:6])}) |
257 self.ProjectAddConfiguration("config") |
263 self.AddProjectDefaultConfiguration() |
258 self.ProjectAddConfigurationResource("config", "resource1") |
|
259 |
264 |
260 # Change XSD into class members |
265 # Change XSD into class members |
261 self._AddParamsMembers() |
266 self._AddParamsMembers() |
262 self.Children = {} |
267 self.Children = {} |
263 # Keep track of the root confnode (i.e. project path) |
268 # Keep track of the root confnode (i.e. project path) |
282 return _("Chosen folder doesn't contain a program. It's not a valid project!") |
287 return _("Chosen folder doesn't contain a program. It's not a valid project!") |
283 # Load PLCOpen file |
288 # Load PLCOpen file |
284 result = self.OpenXMLFile(plc_file) |
289 result = self.OpenXMLFile(plc_file) |
285 if result: |
290 if result: |
286 return result |
291 return result |
|
292 if len(self.GetProjectConfigNames()) == 0: |
|
293 self.AddProjectDefaultConfiguration() |
287 # Change XSD into class members |
294 # Change XSD into class members |
288 self._AddParamsMembers() |
295 self._AddParamsMembers() |
289 self.Children = {} |
296 self.Children = {} |
290 # Keep track of the root confnode (i.e. project path) |
297 # Keep track of the root confnode (i.e. project path) |
291 self.ProjectPath = ProjectPath |
298 self.ProjectPath = ProjectPath |
380 Extras=[] |
387 Extras=[] |
381 for lib in self.Libraries: |
388 for lib in self.Libraries: |
382 res=lib.Generate_C(buildpath,self._VariablesList,LibIECCflags) |
389 res=lib.Generate_C(buildpath,self._VariablesList,LibIECCflags) |
383 LocatedCCodeAndFlags.append(res[:2]) |
390 LocatedCCodeAndFlags.append(res[:2]) |
384 if len(res)>2: |
391 if len(res)>2: |
385 Extras.append(res[2:]) |
392 Extras.extend(res[2:]) |
386 return map(list,zip(*LocatedCCodeAndFlags))+[tuple(*Extras)] |
393 return map(list,zip(*LocatedCCodeAndFlags))+[tuple(Extras)] |
387 |
394 |
388 # Update PLCOpenEditor ConfNode Block types from loaded confnodes |
395 # Update PLCOpenEditor ConfNode Block types from loaded confnodes |
389 def RefreshConfNodesBlockLists(self): |
396 def RefreshConfNodesBlockLists(self): |
390 if getattr(self, "Children", None) is not None: |
397 if getattr(self, "Children", None) is not None: |
391 self.ClearConfNodeTypes() |
398 self.ClearConfNodeTypes() |
401 if self.AppFrame is not None: |
408 if self.AppFrame is not None: |
402 self.AppFrame.RefreshTitle() |
409 self.AppFrame.RefreshTitle() |
403 self.AppFrame.RefreshPouInstanceVariablesPanel() |
410 self.AppFrame.RefreshPouInstanceVariablesPanel() |
404 self.AppFrame.RefreshFileMenu() |
411 self.AppFrame.RefreshFileMenu() |
405 self.AppFrame.RefreshEditMenu() |
412 self.AppFrame.RefreshEditMenu() |
406 self.AppFrame.RefreshEditor() |
413 wx.CallAfter(self.AppFrame.RefreshEditor) |
407 |
414 |
408 def GetVariableLocationTree(self): |
415 def GetVariableLocationTree(self): |
409 ''' |
416 ''' |
410 This function is meant to be overridden by confnodes. |
417 This function is meant to be overridden by confnodes. |
411 |
418 |
496 if not resdict['SIZE']: |
503 if not resdict['SIZE']: |
497 resdict['SIZE'] = 'X' |
504 resdict['SIZE'] = 'X' |
498 # finally store into located variable list |
505 # finally store into located variable list |
499 locations.append(resdict) |
506 locations.append(resdict) |
500 return locations |
507 return locations |
501 |
508 |
|
509 def GetConfNodeGlobalInstances(self): |
|
510 return self._GlobalInstances() |
|
511 |
502 def _Generate_SoftPLC(self): |
512 def _Generate_SoftPLC(self): |
503 """ |
513 """ |
504 Generate SoftPLC ST/IL/SFC code out of PLCOpenEditor controller, and compile it with IEC2C |
514 Generate SoftPLC ST/IL/SFC code out of PLCOpenEditor controller, and compile it with IEC2C |
505 @param buildpath: path where files should be created |
515 @param buildpath: path where files should be created |
506 """ |
516 """ |
690 attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:]) |
700 attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:]) |
691 # Push this dictionnary into result. |
701 # Push this dictionnary into result. |
692 self._ProgramList.append(attrs) |
702 self._ProgramList.append(attrs) |
693 |
703 |
694 # second section contains all variables |
704 # second section contains all variables |
|
705 config_FBs = {} |
695 for line in ListGroup[1]: |
706 for line in ListGroup[1]: |
696 # Split and Maps each field to dictionnary entries |
707 # Split and Maps each field to dictionnary entries |
697 attrs = dict(zip(VariablesListAttributeName,line.strip().split(';'))) |
708 attrs = dict(zip(VariablesListAttributeName,line.strip().split(';'))) |
698 # Truncate "C_path" to remove conf an ressources names |
709 # Truncate "C_path" to remove conf an ressources names |
699 parts = attrs["C_path"].split(".",2) |
710 parts = attrs["C_path"].split(".",2) |
700 if len(parts) > 2: |
711 if len(parts) > 2: |
701 attrs["C_path"] = '__'.join(parts[1:]) |
712 config_FB = config_FBs.get(tuple(parts[:2])) |
|
713 if config_FB: |
|
714 parts = [config_FB] + parts[2:] |
|
715 attrs["C_path"] = '.'.join(parts) |
|
716 else: |
|
717 attrs["C_path"] = '__'.join(parts[1:]) |
702 else: |
718 else: |
703 attrs["C_path"] = '__'.join(parts) |
719 attrs["C_path"] = '__'.join(parts) |
|
720 if attrs["vartype"] == "FB": |
|
721 config_FBs[tuple(parts)] = attrs["C_path"] |
704 # Push this dictionnary into result. |
722 # Push this dictionnary into result. |
705 self._VariablesList.append(attrs) |
723 self._VariablesList.append(attrs) |
706 # Fill in IEC<->C translation dicts |
724 # Fill in IEC<->C translation dicts |
707 IEC_path=attrs["IEC_path"] |
725 IEC_path=attrs["IEC_path"] |
708 Idx=int(attrs["num"]) |
726 Idx=int(attrs["num"]) |
734 "extern_variables_declarations":"\n".join([ |
752 "extern_variables_declarations":"\n".join([ |
735 {"EXT":"extern __IEC_%(type)s_p %(C_path)s;", |
753 {"EXT":"extern __IEC_%(type)s_p %(C_path)s;", |
736 "IN":"extern __IEC_%(type)s_p %(C_path)s;", |
754 "IN":"extern __IEC_%(type)s_p %(C_path)s;", |
737 "MEM":"extern __IEC_%(type)s_p %(C_path)s;", |
755 "MEM":"extern __IEC_%(type)s_p %(C_path)s;", |
738 "OUT":"extern __IEC_%(type)s_p %(C_path)s;", |
756 "OUT":"extern __IEC_%(type)s_p %(C_path)s;", |
739 "VAR":"extern __IEC_%(type)s_t %(C_path)s;"}[v["vartype"]]%v |
757 "VAR":"extern __IEC_%(type)s_t %(C_path)s;", |
740 for v in self._VariablesList if v["vartype"] != "FB" and v["C_path"].find('.')<0]), |
758 "FB":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v |
|
759 for v in self._VariablesList if v["C_path"].find('.')<0]), |
741 "for_each_variable_do_code":"\n".join([ |
760 "for_each_variable_do_code":"\n".join([ |
742 {"EXT":" (*fp)((void*)&%(C_path)s,%(type)s_P_ENUM);\n", |
761 {"EXT":" (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n", |
743 "IN":" (*fp)((void*)&%(C_path)s,%(type)s_P_ENUM);\n", |
762 "IN":" (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n", |
744 "MEM":" (*fp)((void*)&%(C_path)s,%(type)s_O_ENUM);\n", |
763 "MEM":" (*fp)((void*)&(%(C_path)s),%(type)s_O_ENUM);\n", |
745 "OUT":" (*fp)((void*)&%(C_path)s,%(type)s_O_ENUM);\n", |
764 "OUT":" (*fp)((void*)&(%(C_path)s),%(type)s_O_ENUM);\n", |
746 "VAR":" (*fp)((void*)&%(C_path)s,%(type)s_ENUM);\n"}[v["vartype"]]%v |
765 "VAR":" (*fp)((void*)&(%(C_path)s),%(type)s_ENUM);\n"}[v["vartype"]]%v |
747 for v in self._VariablesList if v["vartype"] != "FB" and v["type"] in DebugTypesSize ]), |
766 for v in self._VariablesList if v["vartype"] != "FB" and v["type"] in DebugTypesSize ]), |
748 "find_variable_case_code":"\n".join([ |
767 "find_variable_case_code":"\n".join([ |
749 " case %(num)s:\n"%v+ |
768 " case %(num)s:\n"%v+ |
750 " *varp = (void*)&%(C_path)s;\n"%v+ |
769 " *varp = (void*)&(%(C_path)s);\n"%v+ |
751 {"EXT":" return %(type)s_P_ENUM;\n", |
770 {"EXT":" return %(type)s_P_ENUM;\n", |
752 "IN":" return %(type)s_P_ENUM;\n", |
771 "IN":" return %(type)s_P_ENUM;\n", |
753 "MEM":" return %(type)s_O_ENUM;\n", |
772 "MEM":" return %(type)s_O_ENUM;\n", |
754 "OUT":" return %(type)s_O_ENUM;\n", |
773 "OUT":" return %(type)s_O_ENUM;\n", |
755 "VAR":" return %(type)s_ENUM;\n"}[v["vartype"]]%v |
774 "VAR":" return %(type)s_ENUM;\n"}[v["vartype"]]%v |
854 return False |
873 return False |
855 |
874 |
856 self.LocationCFilesAndCFLAGS = CTNLocationCFilesAndCFLAGS + LibCFilesAndCFLAGS |
875 self.LocationCFilesAndCFLAGS = CTNLocationCFilesAndCFLAGS + LibCFilesAndCFLAGS |
857 self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS |
876 self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS |
858 ExtraFiles = CTNExtraFiles + LibExtraFiles |
877 ExtraFiles = CTNExtraFiles + LibExtraFiles |
859 |
878 |
860 # Get temporary directory path |
879 # Get temporary directory path |
861 extrafilespath = self._getExtraFilesPath() |
880 extrafilespath = self._getExtraFilesPath() |
862 # Remove old directory |
881 # Remove old directory |
863 if os.path.exists(extrafilespath): |
882 if os.path.exists(extrafilespath): |
864 shutil.rmtree(extrafilespath) |
883 shutil.rmtree(extrafilespath) |
921 def ShowError(self, logger, from_location, to_location): |
940 def ShowError(self, logger, from_location, to_location): |
922 chunk_infos = self.GetChunkInfos(from_location, to_location) |
941 chunk_infos = self.GetChunkInfos(from_location, to_location) |
923 for infos, (start_row, start_col) in chunk_infos: |
942 for infos, (start_row, start_col) in chunk_infos: |
924 start = (from_location[0] - start_row, from_location[1] - start_col) |
943 start = (from_location[0] - start_row, from_location[1] - start_col) |
925 end = (to_location[0] - start_row, to_location[1] - start_col) |
944 end = (to_location[0] - start_row, to_location[1] - start_col) |
926 #print from_location, to_location, start_row, start_col, start, end |
|
927 if self.AppFrame is not None: |
945 if self.AppFrame is not None: |
928 self.AppFrame.ShowError(infos, start, end) |
946 self.AppFrame.ShowError(infos, start, end) |
929 |
947 |
930 _IECCodeView = None |
948 _IECCodeView = None |
931 def _showIECcode(self): |
949 def _showIECcode(self): |
935 def _editIECrawcode(self): |
953 def _editIECrawcode(self): |
936 self._OpenView("IEC raw code") |
954 self._OpenView("IEC raw code") |
937 |
955 |
938 _ProjectFilesView = None |
956 _ProjectFilesView = None |
939 def _OpenProjectFiles(self): |
957 def _OpenProjectFiles(self): |
940 self._OpenView("Project files") |
958 self._OpenView("Project Files") |
941 |
959 |
942 _FileEditors = {} |
960 _FileEditors = {} |
943 def _OpenFileEditor(self, filepath): |
961 def _OpenFileEditor(self, filepath): |
944 self._OpenView(filepath) |
962 self._OpenView(filepath) |
945 |
963 |
978 if self._IECRawCodeView is not None: |
996 if self._IECRawCodeView is not None: |
979 self.AppFrame.EditProjectElement(self._IECRawCodeView, name) |
997 self.AppFrame.EditProjectElement(self._IECRawCodeView, name) |
980 |
998 |
981 return self._IECRawCodeView |
999 return self._IECRawCodeView |
982 |
1000 |
983 elif name == "Project files": |
1001 elif name == "Project Files": |
984 if self._ProjectFilesView is None: |
1002 if self._ProjectFilesView is None: |
985 self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True) |
1003 self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True) |
986 |
1004 |
987 extensions = [] |
1005 extensions = [] |
988 for extension, name, editor in features.file_editors: |
1006 for extension, name, editor in features.file_editors: |
989 if extension not in extensions: |
1007 if extension not in extensions: |
990 extensions.append(extension) |
1008 extensions.append(extension) |
991 self._ProjectFilesView.SetEditableFileExtensions(extensions) |
1009 self._ProjectFilesView.SetEditableFileExtensions(extensions) |
992 |
1010 |
993 if self._ProjectFilesView is not None: |
1011 if self._ProjectFilesView is not None: |
994 self.AppFrame.EditProjectElement(self._ProjectFilesView, name) |
1012 self.AppFrame.EditProjectElement(self._ProjectFilesView, name) |
995 |
1013 |
996 return self._ProjectFilesView |
1014 return self._ProjectFilesView |
1021 name = "::".join([filepath, editor_name]) |
1039 name = "::".join([filepath, editor_name]) |
1022 |
1040 |
1023 editor = editors[editor_name]() |
1041 editor = editors[editor_name]() |
1024 self._FileEditors[filepath] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame) |
1042 self._FileEditors[filepath] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame) |
1025 self._FileEditors[filepath].SetIcon(GetBitmap("FILE")) |
1043 self._FileEditors[filepath].SetIcon(GetBitmap("FILE")) |
|
1044 if isinstance(self._FileEditors[filepath], DebugViewer): |
|
1045 self._FileEditors[filepath].SetDataProducer(self) |
1026 |
1046 |
1027 if self._FileEditors.has_key(filepath): |
1047 if self._FileEditors.has_key(filepath): |
1028 editor = self._FileEditors[filepath] |
1048 editor = self._FileEditors[filepath] |
1029 self.AppFrame.EditProjectElement(editor, editor.GetTagName()) |
1049 self.AppFrame.EditProjectElement(editor, editor.GetTagName()) |
1030 |
1050 |
1054 self.EnableMethod("_Clean", False) |
1074 self.EnableMethod("_Clean", False) |
1055 # kill the builder |
1075 # kill the builder |
1056 self._builder = None |
1076 self._builder = None |
1057 self.CompareLocalAndRemotePLC() |
1077 self.CompareLocalAndRemotePLC() |
1058 |
1078 |
1059 ############# Real PLC object access ############# |
1079 def UpdatePLCLog(self, log_count): |
|
1080 if log_count : |
|
1081 to_console = [] |
|
1082 for level, count, prev in zip(xrange(LogLevelsCount), log_count,self.previous_log_count): |
|
1083 if count is not None and prev != count: |
|
1084 # XXX replace dump to console with dedicated log panel. |
|
1085 dump_end = max( # request message sent after the last one we already got |
|
1086 prev - 1 if prev is not None else -1, |
|
1087 count - 100) # 100 is purely arbitrary number |
|
1088 # dedicated panel should only ask for a small range, |
|
1089 # depending on how user navigate in the panel |
|
1090 # and only ask for last one in follow mode |
|
1091 for msgidx in xrange(count-1, dump_end,-1): |
|
1092 answer = self._connector.GetLogMessage(level, msgidx) |
|
1093 if answer is not None : |
|
1094 msg, tick, tv_sec, tv_nsec = answer |
|
1095 to_console.insert(0,( |
|
1096 (tv_sec, tv_nsec), |
|
1097 '%d|%s.%9.9d|%s(%s)'%( |
|
1098 int(tick), |
|
1099 str(datetime.fromtimestamp(tv_sec)), |
|
1100 tv_nsec, |
|
1101 msg, |
|
1102 LogLevels[level]))) |
|
1103 else: |
|
1104 break; |
|
1105 self.previous_log_count[level] = count |
|
1106 if to_console: |
|
1107 to_console.sort() |
|
1108 self.logger.write("\n".join(zip(*to_console)[1]+('',))) |
|
1109 |
1060 def UpdateMethodsFromPLCStatus(self): |
1110 def UpdateMethodsFromPLCStatus(self): |
1061 # Get PLC state : Running or Stopped |
|
1062 # TODO : use explicit status instead of boolean |
|
1063 status = None |
1111 status = None |
1064 if self._connector is not None: |
1112 if self._connector is not None: |
1065 status = self._connector.GetPLCstatus() |
1113 PLCstatus = self._connector.GetPLCstatus() |
|
1114 if PLCstatus is not None: |
|
1115 status, log_count = PLCstatus |
|
1116 self.UpdatePLCLog(log_count) |
1066 if status is None: |
1117 if status is None: |
1067 self._connector = None |
1118 self._connector = None |
1068 status = "Disconnected" |
1119 status = "Disconnected" |
1069 if(self.previous_plcstate != status): |
1120 if(self.previous_plcstate != status): |
1070 for args in { |
1121 for args in { |
1080 ("_Transfer", False), |
1131 ("_Transfer", False), |
1081 ("_Connect", True), |
1132 ("_Connect", True), |
1082 ("_Disconnect", False)], |
1133 ("_Disconnect", False)], |
1083 }.get(status,[]): |
1134 }.get(status,[]): |
1084 self.ShowMethod(*args) |
1135 self.ShowMethod(*args) |
|
1136 {"Broken": self.logger.write_error, |
|
1137 None: lambda x: None}.get( |
|
1138 status, self.logger.write)(_("PLC state is \"%s\"\n")%_(status)) |
1085 self.previous_plcstate = status |
1139 self.previous_plcstate = status |
1086 return True |
1140 if self.AppFrame is not None: |
1087 return False |
1141 self.AppFrame.RefreshStatusToolBar() |
1088 |
1142 |
1089 def PullPLCStatusProc(self, event): |
1143 def PullPLCStatusProc(self, event): |
1090 if self._connector is None: |
1144 if self._connector is None: |
1091 self.StatusTimer.Stop() |
1145 self.StatusTimer.Stop() |
1092 if self.UpdateMethodsFromPLCStatus(): |
1146 self.UpdateMethodsFromPLCStatus() |
1093 |
|
1094 status = _(self.previous_plcstate) |
|
1095 {"Broken": self.logger.write_error, |
|
1096 None: lambda x: None}.get( |
|
1097 self.previous_plcstate, self.logger.write)(_("PLC is %s\n")%status) |
|
1098 self.AppFrame.RefreshStatusToolBar() |
|
1099 |
1147 |
1100 def RegisterDebugVarToConnector(self): |
1148 def RegisterDebugVarToConnector(self): |
1101 self.DebugTimer=None |
1149 self.DebugTimer=None |
1102 Idxs = [] |
1150 Idxs = [] |
1103 self.TracedIECPath = [] |
1151 self.TracedIECPath = [] |
1107 for IECPath,data_tuple in self.IECdebug_datas.iteritems(): |
1155 for IECPath,data_tuple in self.IECdebug_datas.iteritems(): |
1108 WeakCallableDict, data_log, status, fvalue = data_tuple |
1156 WeakCallableDict, data_log, status, fvalue = data_tuple |
1109 if len(WeakCallableDict) == 0: |
1157 if len(WeakCallableDict) == 0: |
1110 # Callable Dict is empty. |
1158 # Callable Dict is empty. |
1111 # This variable is not needed anymore! |
1159 # This variable is not needed anymore! |
1112 #print "Unused : " + IECPath |
|
1113 IECPathsToPop.append(IECPath) |
1160 IECPathsToPop.append(IECPath) |
1114 elif IECPath != "__tick__": |
1161 elif IECPath != "__tick__": |
1115 # Convert |
1162 # Convert |
1116 Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) |
1163 Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) |
1117 if Idx is not None: |
1164 if Idx is not None: |
1130 self._connector.SetTraceVariablesList(zip(*zip(*Idxs)[0:3])) |
1177 self._connector.SetTraceVariablesList(zip(*zip(*Idxs)[0:3])) |
1131 else: |
1178 else: |
1132 self.TracedIECPath = [] |
1179 self.TracedIECPath = [] |
1133 self._connector.SetTraceVariablesList([]) |
1180 self._connector.SetTraceVariablesList([]) |
1134 self.IECdebug_lock.release() |
1181 self.IECdebug_lock.release() |
1135 |
|
1136 #for IEC_path, IECdebug_data in self.IECdebug_datas.iteritems(): |
|
1137 # print IEC_path, IECdebug_data[0].keys() |
|
1138 |
1182 |
1139 def ReArmDebugRegisterTimer(self): |
1183 def ReArmDebugRegisterTimer(self): |
1140 if self.DebugTimer is not None: |
1184 if self.DebugTimer is not None: |
1141 self.DebugTimer.cancel() |
1185 self.DebugTimer.cancel() |
1142 |
1186 |
1177 self.ReArmDebugRegisterTimer() |
1221 self.ReArmDebugRegisterTimer() |
1178 |
1222 |
1179 return IECdebug_data[1] |
1223 return IECdebug_data[1] |
1180 |
1224 |
1181 def UnsubscribeDebugIECVariable(self, IECPath, callableobj): |
1225 def UnsubscribeDebugIECVariable(self, IECPath, callableobj): |
1182 #print "Unsubscribe", IECPath, callableobj |
|
1183 self.IECdebug_lock.acquire() |
1226 self.IECdebug_lock.acquire() |
1184 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1227 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1185 if IECdebug_data is not None: |
1228 if IECdebug_data is not None: |
1186 IECdebug_data[0].pop(callableobj,None) |
1229 IECdebug_data[0].pop(callableobj,None) |
1187 self.IECdebug_lock.release() |
1230 self.IECdebug_lock.release() |
1229 data_tuple = self.IECdebug_datas.get(IECPath, None) |
1272 data_tuple = self.IECdebug_datas.get(IECPath, None) |
1230 if data_tuple is not None: |
1273 if data_tuple is not None: |
1231 WeakCallableDict, data_log, status, fvalue = data_tuple |
1274 WeakCallableDict, data_log, status, fvalue = data_tuple |
1232 #data_log.append((debug_tick, value)) |
1275 #data_log.append((debug_tick, value)) |
1233 for weakcallable,(args,kwargs) in WeakCallableDict.iteritems(): |
1276 for weakcallable,(args,kwargs) in WeakCallableDict.iteritems(): |
1234 #print weakcallable, value, args, kwargs |
|
1235 function = getattr(weakcallable, function_name, None) |
1277 function = getattr(weakcallable, function_name, None) |
1236 if function is not None: |
1278 if function is not None: |
1237 if status == "Forced" and cargs[1] == fvalue: |
1279 if status == "Forced" and cargs[1] == fvalue: |
1238 function(*(cargs + (True,) + args), **kwargs) |
1280 function(*(cargs + (True,) + args), **kwargs) |
1239 else: |
1281 else: |
1259 if(Trace): |
1301 if(Trace): |
1260 plc_status, debug_tick, debug_vars = Trace |
1302 plc_status, debug_tick, debug_vars = Trace |
1261 else: |
1303 else: |
1262 plc_status = None |
1304 plc_status = None |
1263 debug_getvar_retry += 1 |
1305 debug_getvar_retry += 1 |
1264 #print debug_tick, debug_vars |
|
1265 if plc_status == "Started": |
1306 if plc_status == "Started": |
1266 self.IECdebug_lock.acquire() |
1307 self.IECdebug_lock.acquire() |
1267 if len(debug_vars) == len(self.TracedIECPath): |
1308 if len(debug_vars) == len(self.TracedIECPath): |
1268 if debug_getvar_retry > DEBUG_RETRIES_WARN: |
1309 if debug_getvar_retry > DEBUG_RETRIES_WARN: |
1269 self.logger.write(_("... debugger recovered\n")) |
1310 self.logger.write(_("... debugger recovered\n")) |
1270 debug_getvar_retry = 0 |
1311 debug_getvar_retry = 0 |
1271 for IECPath,value in zip(self.TracedIECPath, debug_vars): |
1312 for IECPath,value in zip(self.TracedIECPath, debug_vars): |
1272 if value is not None: |
1313 if value is not None: |
1273 self.CallWeakcallables(IECPath, "NewValue", debug_tick, value) |
1314 self.CallWeakcallables(IECPath, "NewValue", debug_tick, value) |
1274 self.CallWeakcallables("__tick__", "NewDataAvailable") |
1315 self.CallWeakcallables("__tick__", "NewDataAvailable", debug_tick) |
1275 self.IECdebug_lock.release() |
1316 self.IECdebug_lock.release() |
1276 if debug_getvar_retry == DEBUG_RETRIES_WARN: |
1317 if debug_getvar_retry == DEBUG_RETRIES_WARN: |
1277 self.logger.write(_("Waiting debugger to recover...\n")) |
1318 self.logger.write(_("Waiting debugger to recover...\n")) |
1278 if debug_getvar_retry == DEBUG_RETRIES_REREGISTER: |
1319 if debug_getvar_retry == DEBUG_RETRIES_REREGISTER: |
1279 # re-register debug registry to PLC |
1320 # re-register debug registry to PLC |
1297 else: |
1338 else: |
1298 self.logger.write(_("Debugger stopped.\n")) |
1339 self.logger.write(_("Debugger stopped.\n")) |
1299 self.DebugThread = None |
1340 self.DebugThread = None |
1300 |
1341 |
1301 def _connect_debug(self): |
1342 def _connect_debug(self): |
|
1343 self.previous_plcstate = None |
|
1344 self.previous_log_count = [None]*LogLevelsCount |
1302 if self.AppFrame: |
1345 if self.AppFrame: |
1303 self.AppFrame.ResetGraphicViewers() |
1346 self.AppFrame.ResetGraphicViewers() |
1304 self.RegisterDebugVarToConnector() |
1347 self.RegisterDebugVarToConnector() |
1305 if self.DebugThread is None: |
1348 if self.DebugThread is None: |
1306 self.DebugThread = Thread(target=self.DebugThreadProc) |
1349 self.DebugThread = Thread(target=self.DebugThreadProc) |
1394 self.UpdateMethodsFromPLCStatus() |
1437 self.UpdateMethodsFromPLCStatus() |
1395 if self.previous_plcstate is not None: |
1438 if self.previous_plcstate is not None: |
1396 status = _(self.previous_plcstate) |
1439 status = _(self.previous_plcstate) |
1397 else: |
1440 else: |
1398 status = "" |
1441 status = "" |
1399 self.logger.write(_("PLC is %s\n")%status) |
1442 |
|
1443 #self.logger.write(_("PLC is %s\n")%status) |
1400 |
1444 |
1401 # Start the status Timer |
1445 # Start the status Timer |
1402 self.StatusTimer.Start(milliseconds=500, oneShot=False) |
1446 self.StatusTimer.Start(milliseconds=500, oneShot=False) |
1403 |
1447 |
1404 if self.previous_plcstate=="Started": |
1448 if self.previous_plcstate in ["Started","Stopped"]: |
1405 if self.DebugAvailable() and self.GetIECProgramsAndVariables(): |
1449 if self.DebugAvailable() and self.GetIECProgramsAndVariables(): |
1406 self.logger.write(_("Debug connect matching running PLC\n")) |
1450 self.logger.write(_("Debugger ready\n")) |
1407 self._connect_debug() |
1451 self._connect_debug() |
1408 else: |
1452 else: |
1409 self.logger.write_warning(_("Debug do not match PLC - stop/transfert/start to re-enable\n")) |
1453 self.logger.write_warning(_("Debug does not match PLC - stop/transfert/start to re-enable\n")) |
1410 |
1454 |
1411 def CompareLocalAndRemotePLC(self): |
1455 def CompareLocalAndRemotePLC(self): |
1412 if self._connector is None: |
1456 if self._connector is None: |
1413 return |
1457 return |
1414 # We are now connected. Update button status |
1458 # We are now connected. Update button status |