ProjectController.py
branch1.1 Korean release
changeset 1280 72a826dfcfbb
parent 1262 7b9259945453
child 1282 f427352f9727
equal deleted inserted replaced
977:c8e008b8cefe 1280:72a826dfcfbb
    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
   111         self.ProjectPath = None
   111         self.ProjectPath = None
   112         self._setBuildPath(None)
   112         self._setBuildPath(None)
   113         self.DebugThread = None
   113         self.DebugThread = None
   114         self.debug_break = False
   114         self.debug_break = False
   115         self.previous_plcstate = None
   115         self.previous_plcstate = None
   116         self.previous_log_count = [None]*LogLevelsCount
       
   117         # copy ConfNodeMethods so that it can be later customized
   116         # copy ConfNodeMethods so that it can be later customized
   118         self.StatusMethods = [dic.copy() for dic in self.StatusMethods]
   117         self.StatusMethods = [dic.copy() for dic in self.StatusMethods]
   119 
   118 
   120     def LoadLibraries(self):
   119     def LoadLibraries(self):
   121         self.Libraries = []
   120         self.Libraries = []
   135         self.AppFrame = frame
   134         self.AppFrame = frame
   136         self.logger = logger
   135         self.logger = logger
   137         self.StatusTimer = None
   136         self.StatusTimer = None
   138         
   137         
   139         if frame is not None:
   138         if frame is not None:
       
   139             frame.LogViewer.SetLogSource(self._connector)
       
   140             
   140             # Timer to pull PLC status
   141             # Timer to pull PLC status
   141             ID_STATUSTIMER = wx.NewId()
   142             ID_STATUSTIMER = wx.NewId()
   142             self.StatusTimer = wx.Timer(self.AppFrame, ID_STATUSTIMER)
   143             self.StatusTimer = wx.Timer(self.AppFrame, ID_STATUSTIMER)
   143             self.AppFrame.Bind(wx.EVT_TIMER, self.PullPLCStatusProc, self.StatusTimer)
   144             self.AppFrame.Bind(wx.EVT_TIMER, self.PullPLCStatusProc, self.StatusTimer)
   144         
   145         
   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")
   362         answer = dirdialog.ShowModal()
   377         answer = dirdialog.ShowModal()
   363         dirdialog.Destroy()
   378         dirdialog.Destroy()
   364         if answer == wx.ID_OK:
   379         if answer == wx.ID_OK:
   365             newprojectpath = dirdialog.GetPath()
   380             newprojectpath = dirdialog.GetPath()
   366             if os.path.isdir(newprojectpath):
   381             if os.path.isdir(newprojectpath):
   367                 self.ProjectPath = newprojectpath
   382                 self.ProjectPath, old_project_path = newprojectpath, self.ProjectPath
   368                 if dosave:
   383                 self.SaveProject(old_project_path)
   369                     self.SaveProject()
       
   370                 self._setBuildPath(self.BuildPath)
   384                 self._setBuildPath(self.BuildPath)
   371                 return True
   385                 return True
   372         return False
   386         return False
   373 
   387 
   374     def GetLibrariesTypes(self):
   388     def GetLibrariesTypes(self):
   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"))
  1339                 self.logger.write(_("Debugger stopped.\n"))
  1352                 self.logger.write(_("Debugger stopped.\n"))
  1340         self.DebugThread = None
  1353         self.DebugThread = None
  1341 
  1354 
  1342     def _connect_debug(self): 
  1355     def _connect_debug(self): 
  1343         self.previous_plcstate = None
  1356         self.previous_plcstate = None
  1344         self.previous_log_count = [None]*LogLevelsCount
       
  1345         if self.AppFrame:
  1357         if self.AppFrame:
  1346             self.AppFrame.ResetGraphicViewers()
  1358             self.AppFrame.ResetGraphicViewers()
  1347         self.RegisterDebugVarToConnector()
  1359         self.RegisterDebugVarToConnector()
  1348         if self.DebugThread is None:
  1360         if self.DebugThread is None:
  1349             self.DebugThread = Thread(target=self.DebugThreadProc)
  1361             self.DebugThread = Thread(target=self.DebugThreadProc)
  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"))
  1415                     self.AppFrame.RefreshEditMenu()
  1440                     self.AppFrame.RefreshEditMenu()
  1416                     self.AppFrame.RefreshPageTitles()
  1441                     self.AppFrame.RefreshPageTitles()
  1417                            
  1442                            
  1418         # Get connector from uri
  1443         # Get connector from uri
  1419         try:
  1444         try:
  1420             self._connector = connectors.ConnectorFactory(uri, self)
  1445             self._SetConnector(connectors.ConnectorFactory(uri, self))
  1421         except Exception, msg:
  1446         except Exception, msg:
  1422             self.logger.write_error(_("Exception while connecting %s!\n")%uri)
  1447             self.logger.write_error(_("Exception while connecting %s!\n")%uri)
  1423             self.logger.write_error(traceback.format_exc())
  1448             self.logger.write_error(traceback.format_exc())
  1424 
  1449 
  1425         # Did connection success ?
  1450         # Did connection success ?
  1439                 status = _(self.previous_plcstate)
  1464                 status = _(self.previous_plcstate)
  1440             else:
  1465             else:
  1441                 status = ""
  1466                 status = ""
  1442 
  1467 
  1443             #self.logger.write(_("PLC is %s\n")%status)
  1468             #self.logger.write(_("PLC is %s\n")%status)
  1444             
       
  1445             # Start the status Timer
       
  1446             self.StatusTimer.Start(milliseconds=500, oneShot=False)
       
  1447             
  1469             
  1448             if self.previous_plcstate in ["Started","Stopped"]:
  1470             if self.previous_plcstate in ["Started","Stopped"]:
  1449                 if self.DebugAvailable() and self.GetIECProgramsAndVariables():
  1471                 if self.DebugAvailable() and self.GetIECProgramsAndVariables():
  1450                     self.logger.write(_("Debugger ready\n"))
  1472                     self.logger.write(_("Debugger ready\n"))
  1451                     self._connect_debug()
  1473                     self._connect_debug()
  1475 #                _("Cannot compare latest build to target. Please build.\n"))
  1497 #                _("Cannot compare latest build to target. Please build.\n"))
  1476             self.EnableMethod("_Transfer", False)
  1498             self.EnableMethod("_Transfer", False)
  1477 
  1499 
  1478 
  1500 
  1479     def _Disconnect(self):
  1501     def _Disconnect(self):
  1480         self._connector = None
  1502         self._SetConnector(None)
  1481         self.StatusTimer.Stop()
       
  1482         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
       
  1483         
  1503         
  1484     def _Transfer(self):
  1504     def _Transfer(self):
  1485         # Get the last build PLC's 
  1505         # Get the last build PLC's 
  1486         MD5 = self.GetLastBuildMD5()
  1506         MD5 = self.GetLastBuildMD5()
  1487         
  1507         
  1519                     self.logger.write(_("Transfer completed successfully.\n"))
  1539                     self.logger.write(_("Transfer completed successfully.\n"))
  1520                 else:
  1540                 else:
  1521                     self.logger.write_error(_("Transfer failed\n"))
  1541                     self.logger.write_error(_("Transfer failed\n"))
  1522             else:
  1542             else:
  1523                 self.logger.write_error(_("No PLC to transfer (did build succeed ?)\n"))
  1543                 self.logger.write_error(_("No PLC to transfer (did build succeed ?)\n"))
  1524 
       
  1525         self.previous_log_count = [None]*LogLevelsCount
       
  1526 
  1544 
  1527         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1545         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1528 
  1546 
  1529     StatusMethods = [
  1547     StatusMethods = [
  1530         {"bitmap" : "Build",
  1548         {"bitmap" : "Build",