ProjectController.py
branch1.1 Korean release
changeset 968 eee7625de1f7
parent 923 6ef6e0b3a908
child 978 3290eff761f1
equal deleted inserted replaced
808:6e205c1f05a0 968:eee7625de1f7
    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)
  1329         #self.KillDebugThread()
  1372         #self.KillDebugThread()
  1330         
  1373         
  1331         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1374         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1332 
  1375 
  1333     def _Connect(self):
  1376     def _Connect(self):
  1334         # don't accept re-connetion is already connected
  1377         # don't accept re-connetion if already connected
  1335         if self._connector is not None:
  1378         if self._connector is not None:
  1336             self.logger.write_error(_("Already connected. Please disconnect\n"))
  1379             self.logger.write_error(_("Already connected. Please disconnect\n"))
  1337             return
  1380             return
  1338         
  1381         
  1339         # Get connector uri
  1382         # Get connector uri
  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
  1475                     self.logger.write(_("Transfer completed successfully.\n"))
  1519                     self.logger.write(_("Transfer completed successfully.\n"))
  1476                 else:
  1520                 else:
  1477                     self.logger.write_error(_("Transfer failed\n"))
  1521                     self.logger.write_error(_("Transfer failed\n"))
  1478             else:
  1522             else:
  1479                 self.logger.write_error(_("No PLC to transfer (did build succeed ?)\n"))
  1523                 self.logger.write_error(_("No PLC to transfer (did build succeed ?)\n"))
       
  1524 
       
  1525         self.previous_log_count = [None]*LogLevelsCount
  1480 
  1526 
  1481         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1527         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1482 
  1528 
  1483     StatusMethods = [
  1529     StatusMethods = [
  1484         {"bitmap" : "Build",
  1530         {"bitmap" : "Build",