19 from util.ProcessLogger import ProcessLogger |
19 from util.ProcessLogger import ProcessLogger |
20 from util.BitmapLibrary import GetBitmap |
20 from util.BitmapLibrary import GetBitmap |
21 from editors.FileManagementPanel import FileManagementPanel |
21 from editors.FileManagementPanel import FileManagementPanel |
22 from editors.ProjectNodeEditor import ProjectNodeEditor |
22 from editors.ProjectNodeEditor import ProjectNodeEditor |
23 from editors.IECCodeViewer import IECCodeViewer |
23 from editors.IECCodeViewer import IECCodeViewer |
24 from graphics import DebugViewer |
24 from editors.DebugViewer import DebugViewer |
25 from dialogs import DiscoveryDialog |
25 from dialogs import DiscoveryDialog |
26 from PLCControler import PLCControler |
26 from PLCControler import PLCControler |
27 from plcopen.structures import IEC_KEYWORDS |
27 from plcopen.structures import IEC_KEYWORDS |
28 from targets.typemapping import DebugTypesSize, LogLevelsCount, LogLevels |
28 from targets.typemapping import DebugTypesSize, LogLevelsCount, LogLevels |
29 from ConfigTreeNode import ConfigTreeNode |
29 from ConfigTreeNode import ConfigTreeNode |
83 |
83 |
84 def __init__(self, frame, logger): |
84 def __init__(self, frame, logger): |
85 PLCControler.__init__(self) |
85 PLCControler.__init__(self) |
86 |
86 |
87 self.MandatoryParams = None |
87 self.MandatoryParams = None |
88 self.SetAppFrame(frame, logger) |
|
89 self._builder = None |
88 self._builder = None |
90 self._connector = None |
89 self._connector = None |
|
90 self.SetAppFrame(frame, logger) |
91 |
91 |
92 self.iec2c_path = os.path.join(base_folder, "matiec", "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else "")) |
92 self.iec2c_path = os.path.join(base_folder, "matiec", "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else "")) |
93 self.ieclib_path = os.path.join(base_folder, "matiec", "lib") |
93 self.ieclib_path = os.path.join(base_folder, "matiec", "lib") |
94 |
94 |
95 # Setup debug information |
95 # Setup debug information |
211 return params |
212 return params |
212 |
213 |
213 def SetParamsAttribute(self, path, value): |
214 def SetParamsAttribute(self, path, value): |
214 if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None: |
215 if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None: |
215 self.BeremizRoot.setTargetType(self.GetTarget()) |
216 self.BeremizRoot.setTargetType(self.GetTarget()) |
216 return ConfigTreeNode.SetParamsAttribute(self, path, value) |
217 res = ConfigTreeNode.SetParamsAttribute(self, path, value) |
|
218 if path.startswith("BeremizRoot.Libraries."): |
|
219 wx.CallAfter(self.RefreshConfNodesBlockLists) |
|
220 return res |
217 |
221 |
218 # helper func to check project path write permission |
222 # helper func to check project path write permission |
219 def CheckProjectPathPerm(self, dosave=True): |
223 def CheckProjectPathPerm(self, dosave=True): |
220 if CheckPathPerm(self.ProjectPath): |
224 if CheckPathPerm(self.ProjectPath): |
221 return True |
225 return True |
222 dialog = wx.MessageDialog(self.AppFrame, |
226 if self.AppFrame is not None: |
223 _('You must have permission to work on the project\nWork on a project copy ?'), |
227 dialog = wx.MessageDialog(self.AppFrame, |
224 _('Error'), |
228 _('You must have permission to work on the project\nWork on a project copy ?'), |
225 wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) |
229 _('Error'), |
226 answer = dialog.ShowModal() |
230 wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) |
227 dialog.Destroy() |
231 answer = dialog.ShowModal() |
228 if answer == wx.ID_YES: |
232 dialog.Destroy() |
229 if self.SaveProjectAs(): |
233 if answer == wx.ID_YES: |
230 self.AppFrame.RefreshTitle() |
234 if self.SaveProjectAs(): |
231 self.AppFrame.RefreshFileMenu() |
235 self.AppFrame.RefreshTitle() |
232 self.AppFrame.RefreshPageTitles() |
236 self.AppFrame.RefreshFileMenu() |
233 return True |
237 self.AppFrame.RefreshPageTitles() |
|
238 return True |
234 return False |
239 return False |
235 |
240 |
236 def _getProjectFilesPath(self): |
241 def _getProjectFilesPath(self, project_path=None): |
|
242 if project_path is not None: |
|
243 return os.path.join(project_path, "project_files") |
237 projectfiles_path = os.path.join(self.GetProjectPath(), "project_files") |
244 projectfiles_path = os.path.join(self.GetProjectPath(), "project_files") |
238 if not os.path.exists(projectfiles_path): |
245 if not os.path.exists(projectfiles_path): |
239 os.mkdir(projectfiles_path) |
246 os.mkdir(projectfiles_path) |
240 return projectfiles_path |
247 return projectfiles_path |
241 |
248 |
279 Load a project contained in a folder |
286 Load a project contained in a folder |
280 @param ProjectPath: path of the project folder |
287 @param ProjectPath: path of the project folder |
281 """ |
288 """ |
282 if os.path.basename(ProjectPath) == "": |
289 if os.path.basename(ProjectPath) == "": |
283 ProjectPath = os.path.dirname(ProjectPath) |
290 ProjectPath = os.path.dirname(ProjectPath) |
284 # Verify that project contains a PLCOpen program |
291 # Verify that project contains a PLCOpen program |
285 plc_file = os.path.join(ProjectPath, "plc.xml") |
292 plc_file = os.path.join(ProjectPath, "plc.xml") |
286 if not os.path.isfile(plc_file): |
293 if not os.path.isfile(plc_file): |
287 return _("Chosen folder doesn't contain a program. It's not a valid project!") |
294 return _("Chosen folder doesn't contain a program. It's not a valid project!") |
288 # Load PLCOpen file |
295 # Load PLCOpen file |
289 result = self.OpenXMLFile(plc_file) |
296 result = self.OpenXMLFile(plc_file) |
308 self.RefreshConfNodesBlockLists() |
315 self.RefreshConfNodesBlockLists() |
309 |
316 |
310 if os.path.exists(self._getBuildPath()): |
317 if os.path.exists(self._getBuildPath()): |
311 self.EnableMethod("_Clean", True) |
318 self.EnableMethod("_Clean", True) |
312 |
319 |
313 if os.path.isfile(self._getIECrawcodepath()): |
320 if os.path.isfile(self._getIECcodepath()): |
314 self.ShowMethod("_showIECcode", True) |
321 self.ShowMethod("_showIECcode", True) |
315 |
322 |
|
323 self.UpdateMethodsFromPLCStatus() |
|
324 |
316 return None |
325 return None |
317 |
326 |
318 def RecursiveConfNodeInfos(self, confnode): |
327 def RecursiveConfNodeInfos(self, confnode): |
319 values = [] |
328 values = [] |
320 for CTNChild in confnode.IECSortedChildren(): |
329 for CTNChild in confnode.IECSortedChildren(): |
321 values.append( |
330 values.append( |
322 {"name": "%s: %s" % (CTNChild.GetFullIEC_Channel(), |
331 {"name": "%s: %s" % (CTNChild.GetFullIEC_Channel(), |
323 CTNChild.CTNName()), |
332 CTNChild.CTNName()), |
|
333 "tagname": CTNChild.CTNFullName(), |
324 "type": ITEM_CONFNODE, |
334 "type": ITEM_CONFNODE, |
325 "confnode": CTNChild, |
335 "confnode": CTNChild, |
326 "icon": CTNChild.GetIconName(), |
336 "icon": CTNChild.GetIconName(), |
327 "values": self.RecursiveConfNodeInfos(CTNChild)}) |
337 "values": self.RecursiveConfNodeInfos(CTNChild)}) |
328 return values |
338 return values |
343 |
353 |
344 def CloseProject(self): |
354 def CloseProject(self): |
345 self.ClearChildren() |
355 self.ClearChildren() |
346 self.ResetAppFrame(None) |
356 self.ResetAppFrame(None) |
347 |
357 |
348 def SaveProject(self): |
358 def SaveProject(self, from_project_path=None): |
349 if self.CheckProjectPathPerm(False): |
359 if self.CheckProjectPathPerm(False): |
|
360 if from_project_path is not None: |
|
361 old_projectfiles_path = self._getProjectFilesPath(from_project_path) |
|
362 if os.path.isdir(old_projectfiles_path): |
|
363 shutil.copytree(old_projectfiles_path, |
|
364 self._getProjectFilesPath(self.ProjectPath)) |
350 self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml')) |
365 self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml')) |
351 result = self.CTNRequestSave() |
366 result = self.CTNRequestSave(from_project_path) |
352 if result: |
367 if result: |
353 self.logger.write_error(result) |
368 self.logger.write_error(result) |
354 |
369 |
355 def SaveProjectAs(self, dosave=True): |
370 def SaveProjectAs(self): |
356 # Ask user to choose a path with write permissions |
371 # Ask user to choose a path with write permissions |
357 if wx.Platform == '__WXMSW__': |
372 if wx.Platform == '__WXMSW__': |
358 path = os.getenv("USERPROFILE") |
373 path = os.getenv("USERPROFILE") |
359 else: |
374 else: |
360 path = os.getenv("HOME") |
375 path = os.getenv("HOME") |
597 if not C_files: |
611 if not C_files: |
598 self.logger.write_error(_("Error : At least one configuration and one resource must be declared in PLC !\n")) |
612 self.logger.write_error(_("Error : At least one configuration and one resource must be declared in PLC !\n")) |
599 return False |
613 return False |
600 # transform those base names to full names with path |
614 # transform those base names to full names with path |
601 C_files = map(lambda filename:os.path.join(buildpath, filename), C_files) |
615 C_files = map(lambda filename:os.path.join(buildpath, filename), C_files) |
|
616 |
|
617 # prepend beremiz include to configuration header |
|
618 H_files = [ fname for fname in result.splitlines() if fname[-2:]==".h" or fname[-2:]==".H" ] |
|
619 H_files.remove("LOCATED_VARIABLES.h") |
|
620 H_files = map(lambda filename:os.path.join(buildpath, filename), H_files) |
|
621 for H_file in H_files: |
|
622 with file(H_file, 'r') as original: data = original.read() |
|
623 with file(H_file, 'w') as modified: modified.write('#include "beremiz.h"\n' + data) |
|
624 |
602 self.logger.write(_("Extracting Located Variables...\n")) |
625 self.logger.write(_("Extracting Located Variables...\n")) |
603 # Keep track of generated located variables for later use by self._Generate_C |
626 # Keep track of generated located variables for later use by self._Generate_C |
604 self.PLCGeneratedLocatedVars = self.GetLocations() |
627 self.PLCGeneratedLocatedVars = self.GetLocations() |
605 # Keep track of generated C files for later use by self.CTNGenerate_C |
628 # Keep track of generated C files for later use by self.CTNGenerate_C |
606 self.PLCGeneratedCFiles = C_files |
629 self.PLCGeneratedCFiles = C_files |
774 "VAR":" return %(type)s_ENUM;\n"}[v["vartype"]]%v |
797 "VAR":" return %(type)s_ENUM;\n"}[v["vartype"]]%v |
775 for v in self._VariablesList if v["vartype"] != "FB" and v["type"] in DebugTypesSize ])} |
798 for v in self._VariablesList if v["vartype"] != "FB" and v["type"] in DebugTypesSize ])} |
776 |
799 |
777 return debug_code |
800 return debug_code |
778 |
801 |
779 def Generate_plc_common_main(self): |
802 def Generate_plc_main(self): |
780 """ |
803 """ |
781 Use confnodes layout given in LocationCFilesAndCFLAGS to |
804 Use confnodes layout given in LocationCFilesAndCFLAGS to |
782 generate glue code that dispatch calls to all confnodes |
805 generate glue code that dispatch calls to all confnodes |
783 """ |
806 """ |
784 # filter location that are related to code that will be called |
807 # filter location that are related to code that will be called |
785 # in retreive, publish, init, cleanup |
808 # in retreive, publish, init, cleanup |
786 locstrs = map(lambda x:"_".join(map(str,x)), |
809 locstrs = map(lambda x:"_".join(map(str,x)), |
787 [loc for loc,Cfiles,DoCalls in self.LocationCFilesAndCFLAGS if loc and DoCalls]) |
810 [loc for loc,Cfiles,DoCalls in self.LocationCFilesAndCFLAGS if loc and DoCalls]) |
788 |
811 |
789 # Generate main, based on template |
812 # Generate main, based on template |
790 if not self.BeremizRoot.getDisable_Extensions(): |
813 if not self.BeremizRoot.getDisable_Extensions(): |
791 plc_main_code = targets.GetCode("plc_common_main") % { |
814 plc_main_code = targets.GetCode("plc_main_head") % { |
792 "calls_prototypes":"\n".join([( |
815 "calls_prototypes":"\n".join([( |
793 "int __init_%(s)s(int argc,char **argv);\n"+ |
816 "int __init_%(s)s(int argc,char **argv);\n"+ |
794 "void __cleanup_%(s)s(void);\n"+ |
817 "void __cleanup_%(s)s(void);\n"+ |
795 "void __retrieve_%(s)s(void);\n"+ |
818 "void __retrieve_%(s)s(void);\n"+ |
796 "void __publish_%(s)s(void);")%{'s':locstr} for locstr in locstrs]), |
819 "void __publish_%(s)s(void);")%{'s':locstr} for locstr in locstrs]), |
797 "retrieve_calls":"\n ".join([ |
820 "retrieve_calls":"\n ".join([ |
798 "__retrieve_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]), |
821 "__retrieve_%s();"%locstr for locstr in locstrs]), |
799 "publish_calls":"\n ".join([ #Call publish in reverse order |
822 "publish_calls":"\n ".join([ #Call publish in reverse order |
800 "__publish_%s();"%locstr for locstr in locstrs]), |
823 "__publish_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]), |
801 "init_calls":"\n ".join([ |
824 "init_calls":"\n ".join([ |
802 "init_level=%d; "%(i+1)+ |
825 "init_level=%d; "%(i+1)+ |
803 "if((res = __init_%s(argc,argv))){"%locstr + |
826 "if((res = __init_%s(argc,argv))){"%locstr + |
804 #"printf(\"%s\"); "%locstr + #for debug |
827 #"printf(\"%s\"); "%locstr + #for debug |
805 "return res;}" for i,locstr in enumerate(locstrs)]), |
828 "return res;}" for i,locstr in enumerate(locstrs)]), |
806 "cleanup_calls":"\n ".join([ |
829 "cleanup_calls":"\n ".join([ |
807 "if(init_level >= %d) "%i+ |
830 "if(init_level >= %d) "%i+ |
808 "__cleanup_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]) |
831 "__cleanup_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]) |
809 } |
832 } |
810 else: |
833 else: |
811 plc_main_code = targets.GetCode("plc_common_main") % { |
834 plc_main_code = targets.GetCode("plc_main_head") % { |
812 "calls_prototypes":"\n", |
835 "calls_prototypes":"\n", |
813 "retrieve_calls":"\n", |
836 "retrieve_calls":"\n", |
814 "publish_calls":"\n", |
837 "publish_calls":"\n", |
815 "init_calls":"\n", |
838 "init_calls":"\n", |
816 "cleanup_calls":"\n" |
839 "cleanup_calls":"\n" |
817 } |
840 } |
818 plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent()["name"]) |
841 plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent()["name"]) |
|
842 plc_main_code += targets.GetCode("plc_main_tail") |
819 return plc_main_code |
843 return plc_main_code |
820 |
844 |
821 |
845 |
822 def _Build(self): |
846 def _Build(self): |
823 """ |
847 """ |
888 fpath = os.path.join(extrafilespath,fname) |
912 fpath = os.path.join(extrafilespath,fname) |
889 open(fpath, "wb").write(fobject.read()) |
913 open(fpath, "wb").write(fobject.read()) |
890 # Now we can forget ExtraFiles (will close files object) |
914 # Now we can forget ExtraFiles (will close files object) |
891 del ExtraFiles |
915 del ExtraFiles |
892 |
916 |
|
917 # Header file for extensions |
|
918 open(os.path.join(buildpath,"beremiz.h"), "w").write(targets.GetHeader()) |
|
919 |
893 # Template based part of C code generation |
920 # Template based part of C code generation |
894 # files are stacked at the beginning, as files of confnode tree root |
921 # files are stacked at the beginning, as files of confnode tree root |
895 for generator, filename, name in [ |
922 for generator, filename, name in [ |
896 # debugger code |
923 # debugger code |
897 (self.Generate_plc_debugger, "plc_debugger.c", "Debugger"), |
924 (self.Generate_plc_debugger, "plc_debugger.c", "Debugger"), |
898 # init/cleanup/retrieve/publish, run and align code |
925 # init/cleanup/retrieve/publish, run and align code |
899 (self.Generate_plc_common_main,"plc_common_main.c","Common runtime")]: |
926 (self.Generate_plc_main,"plc_main.c","Common runtime")]: |
900 try: |
927 try: |
901 # Do generate |
928 # Do generate |
902 code = generator() |
929 code = generator() |
903 if code is None: |
930 if code is None: |
904 raise |
931 raise |
964 def _OpenView(self, name=None, onlyopened=False): |
991 def _OpenView(self, name=None, onlyopened=False): |
965 if name == "IEC code": |
992 if name == "IEC code": |
966 if self._IECCodeView is None: |
993 if self._IECCodeView is None: |
967 plc_file = self._getIECcodepath() |
994 plc_file = self._getIECcodepath() |
968 |
995 |
969 self._IECCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", None, None, instancepath=name) |
996 self._IECCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name) |
970 self._IECCodeView.SetTextSyntax("ALL") |
997 self._IECCodeView.SetTextSyntax("ALL") |
971 self._IECCodeView.SetKeywords(IEC_KEYWORDS) |
998 self._IECCodeView.SetKeywords(IEC_KEYWORDS) |
972 try: |
999 try: |
973 text = file(plc_file).read() |
1000 text = file(plc_file).read() |
974 except: |
1001 except: |
984 |
1011 |
985 elif name == "IEC raw code": |
1012 elif name == "IEC raw code": |
986 if self._IECRawCodeView is None: |
1013 if self._IECRawCodeView is None: |
987 controler = MiniTextControler(self._getIECrawcodepath(), self) |
1014 controler = MiniTextControler(self._getIECrawcodepath(), self) |
988 |
1015 |
989 self._IECRawCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", None, controler, instancepath=name) |
1016 self._IECRawCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name) |
990 self._IECRawCodeView.SetTextSyntax("ALL") |
1017 self._IECRawCodeView.SetTextSyntax("ALL") |
991 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS) |
1018 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS) |
992 self._IECRawCodeView.RefreshView() |
1019 self._IECRawCodeView.RefreshView() |
993 self._IECRawCodeView.SetIcon(GetBitmap("ST")) |
1020 self._IECRawCodeView.SetIcon(GetBitmap("ST")) |
994 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor) |
1021 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor) |
1075 # kill the builder |
1102 # kill the builder |
1076 self._builder = None |
1103 self._builder = None |
1077 self.CompareLocalAndRemotePLC() |
1104 self.CompareLocalAndRemotePLC() |
1078 |
1105 |
1079 def UpdatePLCLog(self, log_count): |
1106 def UpdatePLCLog(self, log_count): |
1080 if log_count : |
1107 if log_count: |
1081 to_console = [] |
1108 if self.AppFrame is not None: |
1082 for level, count, prev in zip(xrange(LogLevelsCount), log_count,self.previous_log_count): |
1109 self.AppFrame.LogViewer.SetLogCounters(log_count) |
1083 if count is not None and prev != count: |
1110 |
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 |
|
1110 def UpdateMethodsFromPLCStatus(self): |
1111 def UpdateMethodsFromPLCStatus(self): |
1111 status = None |
1112 status = None |
1112 if self._connector is not None: |
1113 if self._connector is not None: |
1113 PLCstatus = self._connector.GetPLCstatus() |
1114 PLCstatus = self._connector.GetPLCstatus() |
1114 if PLCstatus is not None: |
1115 if PLCstatus is not None: |
1115 status, log_count = PLCstatus |
1116 status, log_count = PLCstatus |
1116 self.UpdatePLCLog(log_count) |
1117 self.UpdatePLCLog(log_count) |
1117 if status is None: |
1118 if status is None: |
1118 self._connector = None |
1119 self._SetConnector(None, False) |
1119 status = "Disconnected" |
1120 status = "Disconnected" |
1120 if(self.previous_plcstate != status): |
1121 if(self.previous_plcstate != status): |
1121 for args in { |
1122 for args in { |
1122 "Started" : [("_Run", False), |
1123 "Started" : [("_Run", False), |
1123 ("_Stop", True)], |
1124 ("_Stop", True)], |
1131 ("_Transfer", False), |
1132 ("_Transfer", False), |
1132 ("_Connect", True), |
1133 ("_Connect", True), |
1133 ("_Disconnect", False)], |
1134 ("_Disconnect", False)], |
1134 }.get(status,[]): |
1135 }.get(status,[]): |
1135 self.ShowMethod(*args) |
1136 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)) |
|
1139 self.previous_plcstate = status |
1137 self.previous_plcstate = status |
1140 if self.AppFrame is not None: |
1138 if self.AppFrame is not None: |
1141 self.AppFrame.RefreshStatusToolBar() |
1139 self.AppFrame.RefreshStatusToolBar() |
1142 |
1140 if status == "Disconnected": |
|
1141 self.AppFrame.ConnectionStatusBar.SetStatusText(_(status), 1) |
|
1142 self.AppFrame.ConnectionStatusBar.SetStatusText('', 2) |
|
1143 else: |
|
1144 self.AppFrame.ConnectionStatusBar.SetStatusText( |
|
1145 _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1) |
|
1146 self.AppFrame.ConnectionStatusBar.SetStatusText(_(status), 2) |
|
1147 |
1143 def PullPLCStatusProc(self, event): |
1148 def PullPLCStatusProc(self, event): |
1144 if self._connector is None: |
|
1145 self.StatusTimer.Stop() |
|
1146 self.UpdateMethodsFromPLCStatus() |
1149 self.UpdateMethodsFromPLCStatus() |
1147 |
1150 |
1148 def RegisterDebugVarToConnector(self): |
1151 def RegisterDebugVarToConnector(self): |
1149 self.DebugTimer=None |
1152 self.DebugTimer=None |
1150 Idxs = [] |
1153 Idxs = [] |
1177 self._connector.SetTraceVariablesList(zip(*zip(*Idxs)[0:3])) |
1180 self._connector.SetTraceVariablesList(zip(*zip(*Idxs)[0:3])) |
1178 else: |
1181 else: |
1179 self.TracedIECPath = [] |
1182 self.TracedIECPath = [] |
1180 self._connector.SetTraceVariablesList([]) |
1183 self._connector.SetTraceVariablesList([]) |
1181 self.IECdebug_lock.release() |
1184 self.IECdebug_lock.release() |
1182 |
1185 |
|
1186 def IsPLCStarted(self): |
|
1187 return self.previous_plcstate == "Started" |
|
1188 |
1183 def ReArmDebugRegisterTimer(self): |
1189 def ReArmDebugRegisterTimer(self): |
1184 if self.DebugTimer is not None: |
1190 if self.DebugTimer is not None: |
1185 self.DebugTimer.cancel() |
1191 self.DebugTimer.cancel() |
1186 |
1192 |
1187 # Timer to prevent rapid-fire when registering many variables |
1193 # Prevent to call RegisterDebugVarToConnector when PLC is not started |
1188 # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead |
1194 # If an output location var is forced it's leads to segmentation fault in runtime |
1189 self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector]) |
1195 # Links between PLC located variables and real variables are not ready |
1190 # Rearm anti-rapid-fire timer |
1196 if self.IsPLCStarted(): |
1191 self.DebugTimer.start() |
1197 # Timer to prevent rapid-fire when registering many variables |
|
1198 # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead |
|
1199 self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector]) |
|
1200 # Rearm anti-rapid-fire timer |
|
1201 self.DebugTimer.start() |
1192 |
1202 |
1193 def GetDebugIECVariableType(self, IECPath): |
1203 def GetDebugIECVariableType(self, IECPath): |
1194 Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) |
1204 Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) |
1195 return IEC_Type |
1205 return IEC_Type |
1196 |
1206 |
1225 def UnsubscribeDebugIECVariable(self, IECPath, callableobj): |
1235 def UnsubscribeDebugIECVariable(self, IECPath, callableobj): |
1226 self.IECdebug_lock.acquire() |
1236 self.IECdebug_lock.acquire() |
1227 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1237 IECdebug_data = self.IECdebug_datas.get(IECPath, None) |
1228 if IECdebug_data is not None: |
1238 if IECdebug_data is not None: |
1229 IECdebug_data[0].pop(callableobj,None) |
1239 IECdebug_data[0].pop(callableobj,None) |
|
1240 if len(IECdebug_data[0]) == 0: |
|
1241 self.IECdebug_datas.pop(IECPath) |
1230 self.IECdebug_lock.release() |
1242 self.IECdebug_lock.release() |
1231 |
1243 |
1232 self.ReArmDebugRegisterTimer() |
1244 self.ReArmDebugRegisterTimer() |
1233 |
1245 |
1234 def UnsubscribeAllDebugIECVariable(self): |
1246 def UnsubscribeAllDebugIECVariable(self): |
1235 self.IECdebug_lock.acquire() |
1247 self.IECdebug_lock.acquire() |
1236 IECdebug_data = {} |
1248 self.IECdebug_datas = {} |
1237 self.IECdebug_lock.release() |
1249 self.IECdebug_lock.release() |
1238 |
1250 |
1239 self.ReArmDebugRegisterTimer() |
1251 self.ReArmDebugRegisterTimer() |
1240 |
1252 |
1241 def ForceDebugIECVariable(self, IECPath, fvalue): |
1253 def ForceDebugIECVariable(self, IECPath, fvalue): |
1301 if(Trace): |
1313 if(Trace): |
1302 plc_status, debug_tick, debug_vars = Trace |
1314 plc_status, debug_tick, debug_vars = Trace |
1303 else: |
1315 else: |
1304 plc_status = None |
1316 plc_status = None |
1305 debug_getvar_retry += 1 |
1317 debug_getvar_retry += 1 |
|
1318 #print [dict.keys() for IECPath, (dict, log, status, fvalue) in self.IECdebug_datas.items()] |
1306 if plc_status == "Started": |
1319 if plc_status == "Started": |
1307 self.IECdebug_lock.acquire() |
1320 self.IECdebug_lock.acquire() |
1308 if len(debug_vars) == len(self.TracedIECPath): |
1321 if len(debug_vars) == len(self.TracedIECPath): |
1309 if debug_getvar_retry > DEBUG_RETRIES_WARN: |
1322 if debug_getvar_retry > DEBUG_RETRIES_WARN: |
1310 self.logger.write(_("... debugger recovered\n")) |
1323 self.logger.write(_("... debugger recovered\n")) |
1370 |
1382 |
1371 # debugthread should die on his own |
1383 # debugthread should die on his own |
1372 #self.KillDebugThread() |
1384 #self.KillDebugThread() |
1373 |
1385 |
1374 wx.CallAfter(self.UpdateMethodsFromPLCStatus) |
1386 wx.CallAfter(self.UpdateMethodsFromPLCStatus) |
|
1387 |
|
1388 def _SetConnector(self, connector, update_status=True): |
|
1389 self._connector = connector |
|
1390 if self.AppFrame is not None: |
|
1391 self.AppFrame.LogViewer.SetLogSource(connector) |
|
1392 if connector is not None: |
|
1393 # Start the status Timer |
|
1394 self.StatusTimer.Start(milliseconds=500, oneShot=False) |
|
1395 else: |
|
1396 # Stop the status Timer |
|
1397 self.StatusTimer.Stop() |
|
1398 if update_status: |
|
1399 wx.CallAfter(self.UpdateMethodsFromPLCStatus) |
1375 |
1400 |
1376 def _Connect(self): |
1401 def _Connect(self): |
1377 # don't accept re-connetion if already connected |
1402 # don't accept re-connetion if already connected |
1378 if self._connector is not None: |
1403 if self._connector is not None: |
1379 self.logger.write_error(_("Already connected. Please disconnect\n")) |
1404 self.logger.write_error(_("Already connected. Please disconnect\n")) |