ProjectController.py
changeset 1407 cf3d2b53dd68
parent 1401 611fded24ce4
child 1413 dd89016a5028
equal deleted inserted replaced
1406:82db84fe88ea 1407:cf3d2b53dd68
     1 """
     1 """
     2 Beremiz Project Controller 
     2 Beremiz Project Controller
     3 """
     3 """
     4 import os,sys,traceback
     4 import os,sys,traceback
     5 import time
     5 import time
     6 import features
     6 import features
     7 import shutil
     7 import shutil
    61 def GetAddMenuItems():
    61 def GetAddMenuItems():
    62     return ExtractMenuItemsFromCatalog(features.catalog)
    62     return ExtractMenuItemsFromCatalog(features.catalog)
    63 
    63 
    64 class ProjectController(ConfigTreeNode, PLCControler):
    64 class ProjectController(ConfigTreeNode, PLCControler):
    65     """
    65     """
    66     This class define Root object of the confnode tree. 
    66     This class define Root object of the confnode tree.
    67     It is responsible of :
    67     It is responsible of :
    68     - Managing project directory
    68     - Managing project directory
    69     - Building project
    69     - Building project
    70     - Handling PLCOpenEditor controler and view
    70     - Handling PLCOpenEditor controler and view
    71     - Loading user confnodes and instanciante them as children
    71     - Loading user confnodes and instanciante them as children
    72     - ...
    72     - ...
    73     
    73 
    74     """
    74     """
    75 
    75 
    76     # For root object, available Children Types are modules of the confnode packages.
    76     # For root object, available Children Types are modules of the confnode packages.
    77     CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog)
    77     CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog)
    78 
    78 
    90             </xsd:element>
    90             </xsd:element>
    91             <xsd:element name="Libraries" minOccurs="0">"""+(("""
    91             <xsd:element name="Libraries" minOccurs="0">"""+(("""
    92               <xsd:complexType>
    92               <xsd:complexType>
    93               """+"\n".join(['<xsd:attribute name='+
    93               """+"\n".join(['<xsd:attribute name='+
    94                              '"Enable_'+ libname + '_Library" '+
    94                              '"Enable_'+ libname + '_Library" '+
    95                              'type="xsd:boolean" use="optional" default="true"/>' 
    95                              'type="xsd:boolean" use="optional" default="true"/>'
    96                              for libname,lib in features.libraries])+"""
    96                              for libname,lib in features.libraries])+"""
    97               </xsd:complexType>""") if len(features.libraries)>0 else '<xsd:complexType/>') + """
    97               </xsd:complexType>""") if len(features.libraries)>0 else '<xsd:complexType/>') + """
    98             </xsd:element>
    98             </xsd:element>
    99           </xsd:sequence>
    99           </xsd:sequence>
   100           <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/>
   100           <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/>
   102         </xsd:complexType>
   102         </xsd:complexType>
   103       </xsd:element>
   103       </xsd:element>
   104     </xsd:schema>
   104     </xsd:schema>
   105     """
   105     """
   106     EditorType = ProjectNodeEditor
   106     EditorType = ProjectNodeEditor
   107     
   107 
   108     def __init__(self, frame, logger):
   108     def __init__(self, frame, logger):
   109         PLCControler.__init__(self)
   109         PLCControler.__init__(self)
   110 
   110 
   111         self.MandatoryParams = None
   111         self.MandatoryParams = None
   112         self._builder = None
   112         self._builder = None
   113         self._connector = None
   113         self._connector = None
   114         self.DispatchDebugValuesTimer = None
   114         self.DispatchDebugValuesTimer = None
   115         self.DebugValuesBuffers = []
   115         self.DebugValuesBuffers = []
   116         self.DebugTicks = []
   116         self.DebugTicks = []
   117         self.SetAppFrame(frame, logger)
   117         self.SetAppFrame(frame, logger)
   118         
   118 
   119         self.iec2c_path = os.path.join(base_folder, "matiec", "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else ""))
   119         self.iec2c_path = os.path.join(base_folder, "matiec", "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else ""))
   120         self.ieclib_path = os.path.join(base_folder, "matiec", "lib")
   120         self.ieclib_path = os.path.join(base_folder, "matiec", "lib")
   121         
   121 
   122         # Setup debug information
   122         # Setup debug information
   123         self.IECdebug_datas = {}
   123         self.IECdebug_datas = {}
   124         self.IECdebug_lock = Lock()
   124         self.IECdebug_lock = Lock()
   125 
   125 
   126         self.DebugTimer=None
   126         self.DebugTimer=None
   145 
   145 
   146     def __del__(self):
   146     def __del__(self):
   147         if self.DebugTimer:
   147         if self.DebugTimer:
   148             self.DebugTimer.cancel()
   148             self.DebugTimer.cancel()
   149         self.KillDebugThread()
   149         self.KillDebugThread()
   150     
   150 
   151     def LoadLibraries(self):
   151     def LoadLibraries(self):
   152         self.Libraries = []
   152         self.Libraries = []
   153         TypeStack=[]
   153         TypeStack=[]
   154         for libname,clsname in features.libraries:
   154         for libname,clsname in features.libraries:
   155             if self.BeremizRoot.Libraries is None or getattr(self.BeremizRoot.Libraries, "Enable_"+libname+"_Library"):
   155             if self.BeremizRoot.Libraries is None or getattr(self.BeremizRoot.Libraries, "Enable_"+libname+"_Library"):
   156                 Lib = GetClassImporter(clsname)()(self, libname, TypeStack)
   156                 Lib = GetClassImporter(clsname)()(self, libname, TypeStack)
   157                 TypeStack.append(Lib.GetTypes())
   157                 TypeStack.append(Lib.GetTypes())
   158                 self.Libraries.append(Lib)
   158                 self.Libraries.append(Lib)
   159     
   159 
   160     def SetAppFrame(self, frame, logger):
   160     def SetAppFrame(self, frame, logger):
   161         self.AppFrame = frame
   161         self.AppFrame = frame
   162         self.logger = logger
   162         self.logger = logger
   163         self.StatusTimer = None
   163         self.StatusTimer = None
   164         if self.DispatchDebugValuesTimer is not None:
   164         if self.DispatchDebugValuesTimer is not None:
   165             self.DispatchDebugValuesTimer.Stop()
   165             self.DispatchDebugValuesTimer.Stop()
   166         self.DispatchDebugValuesTimer = None
   166         self.DispatchDebugValuesTimer = None
   167         
   167 
   168         if frame is not None:
   168         if frame is not None:
   169             
   169 
   170             # Timer to pull PLC status
   170             # Timer to pull PLC status
   171             self.StatusTimer = wx.Timer(self.AppFrame, -1)
   171             self.StatusTimer = wx.Timer(self.AppFrame, -1)
   172             self.AppFrame.Bind(wx.EVT_TIMER, 
   172             self.AppFrame.Bind(wx.EVT_TIMER,
   173                 self.PullPLCStatusProc, self.StatusTimer)
   173                 self.PullPLCStatusProc, self.StatusTimer)
   174 
   174 
   175             if self._connector is not None:
   175             if self._connector is not None:
   176                 frame.LogViewer.SetLogSource(self._connector)
   176                 frame.LogViewer.SetLogSource(self._connector)
   177                 self.StatusTimer.Start(milliseconds=500, oneShot=False)
   177                 self.StatusTimer.Start(milliseconds=500, oneShot=False)
   178             
   178 
   179             # Timer to dispatch debug values to consumers
   179             # Timer to dispatch debug values to consumers
   180             self.DispatchDebugValuesTimer = wx.Timer(self.AppFrame, -1)
   180             self.DispatchDebugValuesTimer = wx.Timer(self.AppFrame, -1)
   181             self.AppFrame.Bind(wx.EVT_TIMER, 
   181             self.AppFrame.Bind(wx.EVT_TIMER,
   182                 self.DispatchDebugValuesProc, self.DispatchDebugValuesTimer)
   182                 self.DispatchDebugValuesProc, self.DispatchDebugValuesTimer)
   183             
   183 
   184             self.RefreshConfNodesBlockLists()
   184             self.RefreshConfNodesBlockLists()
   185 
   185 
   186     def ResetAppFrame(self, logger):
   186     def ResetAppFrame(self, logger):
   187         if self.AppFrame is not None:
   187         if self.AppFrame is not None:
   188             self.AppFrame.Unbind(wx.EVT_TIMER, self.StatusTimer)
   188             self.AppFrame.Unbind(wx.EVT_TIMER, self.StatusTimer)
   189             self.StatusTimer = None
   189             self.StatusTimer = None
   190             self.AppFrame = None
   190             self.AppFrame = None
   191         
   191 
   192         self.logger = logger
   192         self.logger = logger
   193 
   193 
   194     def CTNName(self):
   194     def CTNName(self):
   195         return "Project"
   195         return "Project"
   196 
   196 
   203     def GetCTRoot(self):
   203     def GetCTRoot(self):
   204         return self
   204         return self
   205 
   205 
   206     def GetIECLibPath(self):
   206     def GetIECLibPath(self):
   207         return self.ieclib_path
   207         return self.ieclib_path
   208     
   208 
   209     def GetIEC2cPath(self):
   209     def GetIEC2cPath(self):
   210         return self.iec2c_path
   210         return self.iec2c_path
   211     
   211 
   212     def GetCurrentLocation(self):
   212     def GetCurrentLocation(self):
   213         return ()
   213         return ()
   214 
   214 
   215     def GetCurrentName(self):
   215     def GetCurrentName(self):
   216         return ""
   216         return ""
   217     
   217 
   218     def _GetCurrentName(self):
   218     def _GetCurrentName(self):
   219         return ""
   219         return ""
   220 
   220 
   221     def GetProjectPath(self):
   221     def GetProjectPath(self):
   222         return self.ProjectPath
   222         return self.ProjectPath
   223 
   223 
   224     def GetProjectName(self):
   224     def GetProjectName(self):
   225         return os.path.split(self.ProjectPath)[1]
   225         return os.path.split(self.ProjectPath)[1]
   226     
   226 
   227     def GetIconName(self):
   227     def GetIconName(self):
   228         return "PROJECT"
   228         return "PROJECT"
   229     
   229 
   230     def GetDefaultTargetName(self):
   230     def GetDefaultTargetName(self):
   231         if wx.Platform == '__WXMSW__':
   231         if wx.Platform == '__WXMSW__':
   232             return "Win32"
   232             return "Win32"
   233         else:
   233         else:
   234             return "Linux"
   234             return "Linux"
   240             target = self.Parser.CreateElement("TargetType", "BeremizRoot")
   240             target = self.Parser.CreateElement("TargetType", "BeremizRoot")
   241             temp_root.setTargetType(target)
   241             temp_root.setTargetType(target)
   242             target_name = self.GetDefaultTargetName()
   242             target_name = self.GetDefaultTargetName()
   243             target.setcontent(self.Parser.CreateElement(target_name, "TargetType"))
   243             target.setcontent(self.Parser.CreateElement(target_name, "TargetType"))
   244         return target
   244         return target
   245     
   245 
   246     def GetParamsAttributes(self, path = None):
   246     def GetParamsAttributes(self, path = None):
   247         params = ConfigTreeNode.GetParamsAttributes(self, path)
   247         params = ConfigTreeNode.GetParamsAttributes(self, path)
   248         if params[0]["name"] == "BeremizRoot":
   248         if params[0]["name"] == "BeremizRoot":
   249             for child in params[0]["children"]:
   249             for child in params[0]["children"]:
   250                 if child["name"] == "TargetType" and child["value"] == '':
   250                 if child["name"] == "TargetType" and child["value"] == '':
   251                     child.update(self.GetTarget().getElementInfos("TargetType")) 
   251                     child.update(self.GetTarget().getElementInfos("TargetType"))
   252         return params
   252         return params
   253         
   253 
   254     def SetParamsAttribute(self, path, value):
   254     def SetParamsAttribute(self, path, value):
   255         if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None:
   255         if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None:
   256             self.BeremizRoot.setTargetType(self.GetTarget())
   256             self.BeremizRoot.setTargetType(self.GetTarget())
   257         res = ConfigTreeNode.SetParamsAttribute(self, path, value)
   257         res = ConfigTreeNode.SetParamsAttribute(self, path, value)
   258         if path.startswith("BeremizRoot.Libraries."):
   258         if path.startswith("BeremizRoot.Libraries."):
   259             wx.CallAfter(self.RefreshConfNodesBlockLists)
   259             wx.CallAfter(self.RefreshConfNodesBlockLists)
   260         return res
   260         return res
   261         
   261 
   262     # helper func to check project path write permission
   262     # helper func to check project path write permission
   263     def CheckProjectPathPerm(self, dosave=True):
   263     def CheckProjectPathPerm(self, dosave=True):
   264         if CheckPathPerm(self.ProjectPath):
   264         if CheckPathPerm(self.ProjectPath):
   265             return True
   265             return True
   266         if self.AppFrame is not None:
   266         if self.AppFrame is not None:
   267             dialog = wx.MessageDialog(self.AppFrame, 
   267             dialog = wx.MessageDialog(self.AppFrame,
   268                         _('You must have permission to work on the project\nWork on a project copy ?'),
   268                         _('You must have permission to work on the project\nWork on a project copy ?'),
   269                         _('Error'), 
   269                         _('Error'),
   270                         wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
   270                         wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
   271             answer = dialog.ShowModal()
   271             answer = dialog.ShowModal()
   272             dialog.Destroy()
   272             dialog.Destroy()
   273             if answer == wx.ID_YES:
   273             if answer == wx.ID_YES:
   274                 if self.SaveProjectAs():
   274                 if self.SaveProjectAs():
   275                     self.AppFrame.RefreshTitle()
   275                     self.AppFrame.RefreshTitle()
   276                     self.AppFrame.RefreshFileMenu()
   276                     self.AppFrame.RefreshFileMenu()
   277                     self.AppFrame.RefreshPageTitles()
   277                     self.AppFrame.RefreshPageTitles()
   278                     return True
   278                     return True
   279         return False
   279         return False
   280     
   280 
   281     def _getProjectFilesPath(self, project_path=None):
   281     def _getProjectFilesPath(self, project_path=None):
   282         if project_path is not None:
   282         if project_path is not None:
   283             return os.path.join(project_path, "project_files")
   283             return os.path.join(project_path, "project_files")
   284         projectfiles_path = os.path.join(self.GetProjectPath(), "project_files")
   284         projectfiles_path = os.path.join(self.GetProjectPath(), "project_files")
   285         if not os.path.exists(projectfiles_path):
   285         if not os.path.exists(projectfiles_path):
   286             os.mkdir(projectfiles_path)
   286             os.mkdir(projectfiles_path)
   287         return projectfiles_path
   287         return projectfiles_path
   288     
   288 
   289     def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"):
   289     def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"):
   290         self.ProjectAddConfiguration(config_name)
   290         self.ProjectAddConfiguration(config_name)
   291         self.ProjectAddConfigurationResource(config_name, res_name)
   291         self.ProjectAddConfigurationResource(config_name, res_name)
   292 
   292 
   293     def NewProject(self, ProjectPath, BuildPath=None):
   293     def NewProject(self, ProjectPath, BuildPath=None):
   297         @param PLCParams: properties of the PLCOpen program created
   297         @param PLCParams: properties of the PLCOpen program created
   298         """
   298         """
   299         # Verify that chosen folder is empty
   299         # Verify that chosen folder is empty
   300         if not os.path.isdir(ProjectPath) or len(os.listdir(ProjectPath)) > 0:
   300         if not os.path.isdir(ProjectPath) or len(os.listdir(ProjectPath)) > 0:
   301             return _("Chosen folder isn't empty. You can't use it for a new project!")
   301             return _("Chosen folder isn't empty. You can't use it for a new project!")
   302         
   302 
   303         # Create PLCOpen program
   303         # Create PLCOpen program
   304         self.CreateNewProject(
   304         self.CreateNewProject(
   305             {"projectName": _("Unnamed"),
   305             {"projectName": _("Unnamed"),
   306              "productName": _("Unnamed"),
   306              "productName": _("Unnamed"),
   307              "productVersion": "1",
   307              "productVersion": "1",
   308              "companyName": _("Unknown"),
   308              "companyName": _("Unknown"),
   309              "creationDateTime": datetime(*localtime()[:6])})
   309              "creationDateTime": datetime(*localtime()[:6])})
   310         self.AddProjectDefaultConfiguration()
   310         self.AddProjectDefaultConfiguration()
   311         
   311 
   312         # Change XSD into class members
   312         # Change XSD into class members
   313         self._AddParamsMembers()
   313         self._AddParamsMembers()
   314         self.Children = {}
   314         self.Children = {}
   315         # Keep track of the root confnode (i.e. project path)
   315         # Keep track of the root confnode (i.e. project path)
   316         self.ProjectPath = ProjectPath
   316         self.ProjectPath = ProjectPath
   318         # get confnodes bloclist (is that usefull at project creation?)
   318         # get confnodes bloclist (is that usefull at project creation?)
   319         self.RefreshConfNodesBlockLists()
   319         self.RefreshConfNodesBlockLists()
   320         # this will create files base XML files
   320         # this will create files base XML files
   321         self.SaveProject()
   321         self.SaveProject()
   322         return None
   322         return None
   323         
   323 
   324     def LoadProject(self, ProjectPath, BuildPath=None):
   324     def LoadProject(self, ProjectPath, BuildPath=None):
   325         """
   325         """
   326         Load a project contained in a folder
   326         Load a project contained in a folder
   327         @param ProjectPath: path of the project folder
   327         @param ProjectPath: path of the project folder
   328         """
   328         """
   355             if result:
   355             if result:
   356                 return result
   356                 return result
   357             #Load and init all the children
   357             #Load and init all the children
   358             self.LoadChildren()
   358             self.LoadChildren()
   359         self.RefreshConfNodesBlockLists()
   359         self.RefreshConfNodesBlockLists()
   360         
   360 
   361         if os.path.exists(self._getBuildPath()):
   361         if os.path.exists(self._getBuildPath()):
   362             self.EnableMethod("_Clean", True)
   362             self.EnableMethod("_Clean", True)
   363 
   363 
   364         if os.path.isfile(self._getIECcodepath()):
   364         if os.path.isfile(self._getIECcodepath()):
   365             self.ShowMethod("_showIECcode", True)
   365             self.ShowMethod("_showIECcode", True)
   366         
   366 
   367         self.UpdateMethodsFromPLCStatus()
   367         self.UpdateMethodsFromPLCStatus()
   368         
   368 
   369         return None
   369         return None
   370     
   370 
   371     def RecursiveConfNodeInfos(self, confnode):
   371     def RecursiveConfNodeInfos(self, confnode):
   372         values = []
   372         values = []
   373         for CTNChild in confnode.IECSortedChildren():
   373         for CTNChild in confnode.IECSortedChildren():
   374             values.append(
   374             values.append(
   375                 {"name": "%s: %s" % (CTNChild.GetFullIEC_Channel(),
   375                 {"name": "%s: %s" % (CTNChild.GetFullIEC_Channel(),
   376                                      CTNChild.CTNName()), 
   376                                      CTNChild.CTNName()),
   377                  "tagname": CTNChild.CTNFullName(),
   377                  "tagname": CTNChild.CTNFullName(),
   378                  "type": ITEM_CONFNODE, 
   378                  "type": ITEM_CONFNODE,
   379                  "confnode": CTNChild,
   379                  "confnode": CTNChild,
   380                  "icon": CTNChild.GetIconName(),
   380                  "icon": CTNChild.GetIconName(),
   381                  "values": self.RecursiveConfNodeInfos(CTNChild)})
   381                  "values": self.RecursiveConfNodeInfos(CTNChild)})
   382         return values
   382         return values
   383     
   383 
   384     def GetProjectInfos(self):
   384     def GetProjectInfos(self):
   385         infos = PLCControler.GetProjectInfos(self)
   385         infos = PLCControler.GetProjectInfos(self)
   386         configurations = infos["values"].pop(-1)
   386         configurations = infos["values"].pop(-1)
   387         resources = None
   387         resources = None
   388         for config_infos in configurations["values"]:
   388         for config_infos in configurations["values"]:
   392                 resources["values"].extend(config_infos["values"][0]["values"])
   392                 resources["values"].extend(config_infos["values"][0]["values"])
   393         if resources is not None:
   393         if resources is not None:
   394             infos["values"].append(resources)
   394             infos["values"].append(resources)
   395         infos["values"].extend(self.RecursiveConfNodeInfos(self))
   395         infos["values"].extend(self.RecursiveConfNodeInfos(self))
   396         return infos
   396         return infos
   397     
   397 
   398     def CloseProject(self):
   398     def CloseProject(self):
   399         self.ClearChildren()
   399         self.ClearChildren()
   400         self.ResetAppFrame(None)
   400         self.ResetAppFrame(None)
   401         
   401 
   402     def SaveProject(self, from_project_path=None):
   402     def SaveProject(self, from_project_path=None):
   403         if self.CheckProjectPathPerm(False):
   403         if self.CheckProjectPathPerm(False):
   404             if from_project_path is not None:
   404             if from_project_path is not None:
   405                 old_projectfiles_path = self._getProjectFilesPath(from_project_path)
   405                 old_projectfiles_path = self._getProjectFilesPath(from_project_path)
   406                 if os.path.isdir(old_projectfiles_path):
   406                 if os.path.isdir(old_projectfiles_path):
   407                     shutil.copytree(old_projectfiles_path, 
   407                     shutil.copytree(old_projectfiles_path,
   408                                     self._getProjectFilesPath(self.ProjectPath))
   408                                     self._getProjectFilesPath(self.ProjectPath))
   409             self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml'))
   409             self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml'))
   410             result = self.CTNRequestSave(from_project_path)
   410             result = self.CTNRequestSave(from_project_path)
   411             if result:
   411             if result:
   412                 self.logger.write_error(result)
   412                 self.logger.write_error(result)
   413     
   413 
   414     def SaveProjectAs(self):
   414     def SaveProjectAs(self):
   415         # Ask user to choose a path with write permissions
   415         # Ask user to choose a path with write permissions
   416         if wx.Platform == '__WXMSW__':
   416         if wx.Platform == '__WXMSW__':
   417             path = os.getenv("USERPROFILE")
   417             path = os.getenv("USERPROFILE")
   418         else:
   418         else:
   442         self.GetIECProgramsAndVariables()
   442         self.GetIECProgramsAndVariables()
   443         LibIECCflags = '"-I%s" -Wno-unused-function'%os.path.abspath(self.GetIECLibPath())
   443         LibIECCflags = '"-I%s" -Wno-unused-function'%os.path.abspath(self.GetIECLibPath())
   444         LocatedCCodeAndFlags=[]
   444         LocatedCCodeAndFlags=[]
   445         Extras=[]
   445         Extras=[]
   446         for lib in self.Libraries:
   446         for lib in self.Libraries:
   447             res=lib.Generate_C(buildpath,self._VariablesList,LibIECCflags)  
   447             res=lib.Generate_C(buildpath,self._VariablesList,LibIECCflags)
   448             LocatedCCodeAndFlags.append(res[:2])
   448             LocatedCCodeAndFlags.append(res[:2])
   449             if len(res)>2:
   449             if len(res)>2:
   450                 Extras.extend(res[2:])
   450                 Extras.extend(res[2:])
   451         return map(list,zip(*LocatedCCodeAndFlags))+[tuple(Extras)]
   451         return map(list,zip(*LocatedCCodeAndFlags))+[tuple(Extras)]
   452     
   452 
   453     # Update PLCOpenEditor ConfNode Block types from loaded confnodes
   453     # Update PLCOpenEditor ConfNode Block types from loaded confnodes
   454     def RefreshConfNodesBlockLists(self):
   454     def RefreshConfNodesBlockLists(self):
   455         if getattr(self, "Children", None) is not None:
   455         if getattr(self, "Children", None) is not None:
   456             self.ClearConfNodeTypes()
   456             self.ClearConfNodeTypes()
   457             self.AddConfNodeTypesList(self.GetLibrariesTypes())
   457             self.AddConfNodeTypesList(self.GetLibrariesTypes())
   458         if self.AppFrame is not None:
   458         if self.AppFrame is not None:
   459             self.AppFrame.RefreshLibraryPanel()
   459             self.AppFrame.RefreshLibraryPanel()
   460             self.AppFrame.RefreshEditor()
   460             self.AppFrame.RefreshEditor()
   461     
   461 
   462     # Update a PLCOpenEditor Pou variable location
   462     # Update a PLCOpenEditor Pou variable location
   463     def UpdateProjectVariableLocation(self, old_leading, new_leading):
   463     def UpdateProjectVariableLocation(self, old_leading, new_leading):
   464         self.Project.updateElementAddress(old_leading, new_leading)
   464         self.Project.updateElementAddress(old_leading, new_leading)
   465         self.BufferProject()
   465         self.BufferProject()
   466         if self.AppFrame is not None:
   466         if self.AppFrame is not None:
   467             self.AppFrame.RefreshTitle()
   467             self.AppFrame.RefreshTitle()
   468             self.AppFrame.RefreshPouInstanceVariablesPanel()
   468             self.AppFrame.RefreshPouInstanceVariablesPanel()
   469             self.AppFrame.RefreshFileMenu()
   469             self.AppFrame.RefreshFileMenu()
   470             self.AppFrame.RefreshEditMenu()
   470             self.AppFrame.RefreshEditMenu()
   471             wx.CallAfter(self.AppFrame.RefreshEditor)
   471             wx.CallAfter(self.AppFrame.RefreshEditor)
   472     
   472 
   473     def GetVariableLocationTree(self):
   473     def GetVariableLocationTree(self):
   474         '''
   474         '''
   475         This function is meant to be overridden by confnodes.
   475         This function is meant to be overridden by confnodes.
   476 
   476 
   477         It should returns an list of dictionaries
   477         It should returns an list of dictionaries
   478         
   478 
   479         - IEC_type is an IEC type like BOOL/BYTE/SINT/...
   479         - IEC_type is an IEC type like BOOL/BYTE/SINT/...
   480         - location is a string of this variable's location, like "%IX0.0.0"
   480         - location is a string of this variable's location, like "%IX0.0.0"
   481         '''
   481         '''
   482         children = []
   482         children = []
   483         for child in self.IECSortedChildren():
   483         for child in self.IECSortedChildren():
   484             children.append(child.GetVariableLocationTree())
   484             children.append(child.GetVariableLocationTree())
   485         return children
   485         return children
   486     
   486 
   487     def ConfNodePath(self):
   487     def ConfNodePath(self):
   488         return os.path.split(__file__)[0]
   488         return os.path.split(__file__)[0]
   489     
   489 
   490     def CTNPath(self, CTNName=None):
   490     def CTNPath(self, CTNName=None):
   491         return self.ProjectPath
   491         return self.ProjectPath
   492     
   492 
   493     def ConfNodeXmlFilePath(self, CTNName=None):
   493     def ConfNodeXmlFilePath(self, CTNName=None):
   494         return os.path.join(self.CTNPath(CTNName), "beremiz.xml")
   494         return os.path.join(self.CTNPath(CTNName), "beremiz.xml")
   495 
   495 
   496     def ParentsTypesFactory(self):
   496     def ParentsTypesFactory(self):
   497         return self.ConfNodeTypesFactory()
   497         return self.ConfNodeTypesFactory()
   513         if CheckPathPerm(self.ProjectPath):
   513         if CheckPathPerm(self.ProjectPath):
   514             self.DefaultBuildPath = os.path.join(self.ProjectPath, "build")
   514             self.DefaultBuildPath = os.path.join(self.ProjectPath, "build")
   515         # Create a build path in temp folder
   515         # Create a build path in temp folder
   516         else:
   516         else:
   517             self.DefaultBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build")
   517             self.DefaultBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build")
   518             
   518 
   519         if not os.path.exists(self.DefaultBuildPath):
   519         if not os.path.exists(self.DefaultBuildPath):
   520             os.makedirs(self.DefaultBuildPath)
   520             os.makedirs(self.DefaultBuildPath)
   521         return self.DefaultBuildPath
   521         return self.DefaultBuildPath
   522     
   522 
   523     def _getExtraFilesPath(self):
   523     def _getExtraFilesPath(self):
   524         return os.path.join(self._getBuildPath(), "extra_files")
   524         return os.path.join(self._getBuildPath(), "extra_files")
   525 
   525 
   526     def _getIECcodepath(self):
   526     def _getIECcodepath(self):
   527         # define name for IEC code file
   527         # define name for IEC code file
   528         return os.path.join(self._getBuildPath(), "plc.st")
   528         return os.path.join(self._getBuildPath(), "plc.st")
   529     
   529 
   530     def _getIECgeneratedcodepath(self):
   530     def _getIECgeneratedcodepath(self):
   531         # define name for IEC generated code file
   531         # define name for IEC generated code file
   532         return os.path.join(self._getBuildPath(), "generated_plc.st")
   532         return os.path.join(self._getBuildPath(), "generated_plc.st")
   533     
   533 
   534     def _getIECrawcodepath(self):
   534     def _getIECrawcodepath(self):
   535         # define name for IEC raw code file
   535         # define name for IEC raw code file
   536         return os.path.join(self.CTNPath(), "raw_plc.st")
   536         return os.path.join(self.CTNPath(), "raw_plc.st")
   537     
   537 
   538     def GetLocations(self):
   538     def GetLocations(self):
   539         locations = []
   539         locations = []
   540         filepath = os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h")
   540         filepath = os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h")
   541         if os.path.isfile(filepath):
   541         if os.path.isfile(filepath):
   542             # IEC2C compiler generate a list of located variables : LOCATED_VARIABLES.h
   542             # IEC2C compiler generate a list of located variables : LOCATED_VARIABLES.h
   544             # each line of LOCATED_VARIABLES.h declares a located variable
   544             # each line of LOCATED_VARIABLES.h declares a located variable
   545             lines = [line.strip() for line in location_file.readlines()]
   545             lines = [line.strip() for line in location_file.readlines()]
   546             # This regular expression parses the lines genereated by IEC2C
   546             # This regular expression parses the lines genereated by IEC2C
   547             LOCATED_MODEL = re.compile("__LOCATED_VAR\((?P<IEC_TYPE>[A-Z]*),(?P<NAME>[_A-Za-z0-9]*),(?P<DIR>[QMI])(?:,(?P<SIZE>[XBWDL]))?,(?P<LOC>[,0-9]*)\)")
   547             LOCATED_MODEL = re.compile("__LOCATED_VAR\((?P<IEC_TYPE>[A-Z]*),(?P<NAME>[_A-Za-z0-9]*),(?P<DIR>[QMI])(?:,(?P<SIZE>[XBWDL]))?,(?P<LOC>[,0-9]*)\)")
   548             for line in lines:
   548             for line in lines:
   549                 # If line match RE, 
   549                 # If line match RE,
   550                 result = LOCATED_MODEL.match(line)
   550                 result = LOCATED_MODEL.match(line)
   551                 if result:
   551                 if result:
   552                     # Get the resulting dict
   552                     # Get the resulting dict
   553                     resdict = result.groupdict()
   553                     resdict = result.groupdict()
   554                     # rewrite string for variadic location as a tuple of integers
   554                     # rewrite string for variadic location as a tuple of integers
   555                     resdict['LOC'] = tuple(map(int,resdict['LOC'].split(',')))
   555                     resdict['LOC'] = tuple(map(int,resdict['LOC'].split(',')))
   556                     # set located size to 'X' if not given 
   556                     # set located size to 'X' if not given
   557                     if not resdict['SIZE']:
   557                     if not resdict['SIZE']:
   558                         resdict['SIZE'] = 'X'
   558                         resdict['SIZE'] = 'X'
   559                     # finally store into located variable list
   559                     # finally store into located variable list
   560                     locations.append(resdict)
   560                     locations.append(resdict)
   561         return locations
   561         return locations
   562     
   562 
   563     def GetConfNodeGlobalInstances(self):
   563     def GetConfNodeGlobalInstances(self):
   564         return self._GlobalInstances()
   564         return self._GlobalInstances()
   565     
   565 
   566     def _Generate_SoftPLC(self):
   566     def _Generate_SoftPLC(self):
       
   567         if self._Generate_PLC_ST():
       
   568             return self._Compile_ST_to_SoftPLC()
       
   569         return False
       
   570 
       
   571     def _Generate_PLC_ST(self):
   567         """
   572         """
   568         Generate SoftPLC ST/IL/SFC code out of PLCOpenEditor controller, and compile it with IEC2C
   573         Generate SoftPLC ST/IL/SFC code out of PLCOpenEditor controller, and compile it with IEC2C
   569         @param buildpath: path where files should be created
   574         @param buildpath: path where files should be created
   570         """
   575         """
   571 
   576 
   572         # Update PLCOpenEditor ConfNode Block types before generate ST code
   577         # Update PLCOpenEditor ConfNode Block types before generate ST code
   573         self.RefreshConfNodesBlockLists()
   578         self.RefreshConfNodesBlockLists()
   574         
   579 
   575         self.logger.write(_("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n"))
   580         self.logger.write(_("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n"))
   576         buildpath = self._getBuildPath()
       
   577         # ask PLCOpenEditor controller to write ST/IL/SFC code file
   581         # ask PLCOpenEditor controller to write ST/IL/SFC code file
   578         program, errors, warnings = self.GenerateProgram(self._getIECgeneratedcodepath())
   582         program, errors, warnings = self.GenerateProgram(self._getIECgeneratedcodepath())
   579         if len(warnings) > 0:
   583         if len(warnings) > 0:
   580             self.logger.write_warning(_("Warnings in ST/IL/SFC code generator :\n"))
   584             self.logger.write_warning(_("Warnings in ST/IL/SFC code generator :\n"))
   581             for warning in warnings:
   585             for warning in warnings:
   597             self.ProgramOffset += 1
   601             self.ProgramOffset += 1
   598         plc_file.close()
   602         plc_file.close()
   599         plc_file = open(self._getIECcodepath(), "a")
   603         plc_file = open(self._getIECcodepath(), "a")
   600         plc_file.write(open(self._getIECgeneratedcodepath(), "r").read())
   604         plc_file.write(open(self._getIECgeneratedcodepath(), "r").read())
   601         plc_file.close()
   605         plc_file.close()
   602 
   606         return True
       
   607 
       
   608     def _Compile_ST_to_SoftPLC(self):
   603         self.logger.write(_("Compiling IEC Program into C code...\n"))
   609         self.logger.write(_("Compiling IEC Program into C code...\n"))
       
   610         buildpath = self._getBuildPath()
   604 
   611 
   605         # Now compile IEC code into many C files
   612         # Now compile IEC code into many C files
   606         # files are listed to stdout, and errors to stderr. 
   613         # files are listed to stdout, and errors to stderr.
   607         status, result, err_result = ProcessLogger(
   614         status, result, err_result = ProcessLogger(
   608                self.logger,
   615                self.logger,
   609                "\"%s\" -f -I \"%s\" -T \"%s\" \"%s\""%(
   616                "\"%s\" -f -I \"%s\" -T \"%s\" \"%s\""%(
   610                          self.iec2c_path,
   617                          self.iec2c_path,
   611                          self.ieclib_path, 
   618                          self.ieclib_path,
   612                          buildpath,
   619                          buildpath,
   613                          self._getIECcodepath()),
   620                          self._getIECcodepath()),
   614                no_stdout=True, no_stderr=True).spin()
   621                no_stdout=True, no_stderr=True).spin()
   615         if status:
   622         if status:
   616             # Failed !
   623             # Failed !
   617             
   624 
   618             # parse iec2c's error message. if it contains a line number,
   625             # parse iec2c's error message. if it contains a line number,
   619             # then print those lines from the generated IEC file.
   626             # then print those lines from the generated IEC file.
   620             for err_line in err_result.split('\n'):
   627             for err_line in err_result.split('\n'):
   621                 self.logger.write_warning(err_line + "\n")
   628                 self.logger.write_warning(err_line + "\n")
   622 
   629 
   623                 m_result = MATIEC_ERROR_MODEL.match(err_line)
   630                 m_result = MATIEC_ERROR_MODEL.match(err_line)
   624                 if m_result is not None:
   631                 if m_result is not None:
   625                     first_line, first_column, last_line, last_column, error = m_result.groups()
   632                     first_line, first_column, last_line, last_column, error = m_result.groups()
   626                     first_line, last_line = int(first_line), int(last_line)
   633                     first_line, last_line = int(first_line), int(last_line)
   627                     
   634 
   628                     last_section = None
   635                     last_section = None
   629                     f = open(self._getIECcodepath())
   636                     f = open(self._getIECcodepath())
   630 
   637 
   631                     for i, line in enumerate(f.readlines()):
   638                     for i, line in enumerate(f.readlines()):
   632                         i = i + 1
   639                         i = i + 1
   638                                 self.logger.write_warning("In section: " + last_section)
   645                                 self.logger.write_warning("In section: " + last_section)
   639                                 last_section = None # only write section once
   646                                 last_section = None # only write section once
   640                             self.logger.write_warning("%04d: %s" % (i, line))
   647                             self.logger.write_warning("%04d: %s" % (i, line))
   641 
   648 
   642                     f.close()
   649                     f.close()
   643             
   650 
   644             self.logger.write_error(_("Error : IEC to C compiler returned %d\n")%status)
   651             self.logger.write_error(_("Error : IEC to C compiler returned %d\n")%status)
   645             return False
   652             return False
   646         
   653 
   647         # Now extract C files of stdout
   654         # Now extract C files of stdout
   648         C_files = [ fname for fname in result.splitlines() if fname[-2:]==".c" or fname[-2:]==".C" ]
   655         C_files = [ fname for fname in result.splitlines() if fname[-2:]==".c" or fname[-2:]==".C" ]
   649         # remove those that are not to be compiled because included by others
   656         # remove those that are not to be compiled because included by others
   650         C_files.remove("POUS.c")
   657         C_files.remove("POUS.c")
   651         if not C_files:
   658         if not C_files:
   677         """
   684         """
   678         # Get target, module and class name
   685         # Get target, module and class name
   679         targetname = self.GetTarget().getcontent().getLocalTag()
   686         targetname = self.GetTarget().getcontent().getLocalTag()
   680         targetclass = targets.GetBuilder(targetname)
   687         targetclass = targets.GetBuilder(targetname)
   681 
   688 
   682         # if target already 
   689         # if target already
   683         if self._builder is None or not isinstance(self._builder,targetclass):
   690         if self._builder is None or not isinstance(self._builder,targetclass):
   684             # Get classname instance
   691             # Get classname instance
   685             self._builder = targetclass(self)
   692             self._builder = targetclass(self)
   686         return self._builder
   693         return self._builder
   687 
   694 
   701     #######################################################################
   708     #######################################################################
   702     #
   709     #
   703     #                C CODE GENERATION METHODS
   710     #                C CODE GENERATION METHODS
   704     #
   711     #
   705     #######################################################################
   712     #######################################################################
   706     
   713 
   707     def CTNGenerate_C(self, buildpath, locations):
   714     def CTNGenerate_C(self, buildpath, locations):
   708         """
   715         """
   709         Return C code generated by iec2c compiler 
   716         Return C code generated by iec2c compiler
   710         when _generate_softPLC have been called
   717         when _generate_softPLC have been called
   711         @param locations: ignored
   718         @param locations: ignored
   712         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
   719         @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
   713         """
   720         """
   714 
   721 
   715         return ([(C_file_name, self.plcCFLAGS) 
   722         return ([(C_file_name, self.plcCFLAGS)
   716                 for C_file_name in self.PLCGeneratedCFiles ], 
   723                 for C_file_name in self.PLCGeneratedCFiles ],
   717                "", # no ldflags
   724                "", # no ldflags
   718                False) # do not expose retreive/publish calls
   725                False) # do not expose retreive/publish calls
   719     
   726 
   720     def ResetIECProgramsAndVariables(self):
   727     def ResetIECProgramsAndVariables(self):
   721         """
   728         """
   722         Reset variable and program list that are parsed from
   729         Reset variable and program list that are parsed from
   723         CSV file generated by IEC2C compiler.
   730         CSV file generated by IEC2C compiler.
   724         """
   731         """
   741                 ProgramsListAttributeName = ["num", "C_path", "type"]
   748                 ProgramsListAttributeName = ["num", "C_path", "type"]
   742                 VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"]
   749                 VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"]
   743                 self._ProgramList = []
   750                 self._ProgramList = []
   744                 self._VariablesList = []
   751                 self._VariablesList = []
   745                 self._IECPathToIdx = {}
   752                 self._IECPathToIdx = {}
   746                 
   753 
   747                 # Separate sections
   754                 # Separate sections
   748                 ListGroup = []
   755                 ListGroup = []
   749                 for line in open(csvfile,'r').xreadlines():
   756                 for line in open(csvfile,'r').xreadlines():
   750                     strippedline = line.strip()
   757                     strippedline = line.strip()
   751                     if strippedline.startswith("//"):
   758                     if strippedline.startswith("//"):
   752                         # Start new section
   759                         # Start new section
   753                         ListGroup.append([])
   760                         ListGroup.append([])
   754                     elif len(strippedline) > 0 and len(ListGroup) > 0:
   761                     elif len(strippedline) > 0 and len(ListGroup) > 0:
   755                         # append to this section
   762                         # append to this section
   756                         ListGroup[-1].append(strippedline)
   763                         ListGroup[-1].append(strippedline)
   757         
   764 
   758                 # first section contains programs
   765                 # first section contains programs
   759                 for line in ListGroup[0]:
   766                 for line in ListGroup[0]:
   760                     # Split and Maps each field to dictionnary entries
   767                     # Split and Maps each field to dictionnary entries
   761                     attrs = dict(zip(ProgramsListAttributeName,line.strip().split(';')))
   768                     attrs = dict(zip(ProgramsListAttributeName,line.strip().split(';')))
   762                     # Truncate "C_path" to remove conf an ressources names
   769                     # Truncate "C_path" to remove conf an ressources names
   763                     attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:])
   770                     attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:])
   764                     # Push this dictionnary into result.
   771                     # Push this dictionnary into result.
   765                     self._ProgramList.append(attrs)
   772                     self._ProgramList.append(attrs)
   766         
   773 
   767                 # second section contains all variables
   774                 # second section contains all variables
   768                 config_FBs = {}
   775                 config_FBs = {}
   769                 for line in ListGroup[1]:
   776                 for line in ListGroup[1]:
   770                     # Split and Maps each field to dictionnary entries
   777                     # Split and Maps each field to dictionnary entries
   771                     attrs = dict(zip(VariablesListAttributeName,line.strip().split(';')))
   778                     attrs = dict(zip(VariablesListAttributeName,line.strip().split(';')))
   774                     if len(parts) > 2:
   781                     if len(parts) > 2:
   775                         config_FB = config_FBs.get(tuple(parts[:2]))
   782                         config_FB = config_FBs.get(tuple(parts[:2]))
   776                         if config_FB:
   783                         if config_FB:
   777                             parts = [config_FB] + parts[2:]
   784                             parts = [config_FB] + parts[2:]
   778                             attrs["C_path"] = '.'.join(parts)
   785                             attrs["C_path"] = '.'.join(parts)
   779                         else: 
   786                         else:
   780                             attrs["C_path"] = '__'.join(parts[1:])
   787                             attrs["C_path"] = '__'.join(parts[1:])
   781                     else:
   788                     else:
   782                         attrs["C_path"] = '__'.join(parts)
   789                         attrs["C_path"] = '__'.join(parts)
   783                         if attrs["vartype"] == "FB":
   790                         if attrs["vartype"] == "FB":
   784                             config_FBs[tuple(parts)] = attrs["C_path"]
   791                             config_FBs[tuple(parts)] = attrs["C_path"]
   786                     self._VariablesList.append(attrs)
   793                     self._VariablesList.append(attrs)
   787                     # Fill in IEC<->C translation dicts
   794                     # Fill in IEC<->C translation dicts
   788                     IEC_path=attrs["IEC_path"]
   795                     IEC_path=attrs["IEC_path"]
   789                     Idx=int(attrs["num"])
   796                     Idx=int(attrs["num"])
   790                     self._IECPathToIdx[IEC_path]=(Idx, attrs["type"])
   797                     self._IECPathToIdx[IEC_path]=(Idx, attrs["type"])
   791                 
   798 
   792                 # third section contains ticktime
   799                 # third section contains ticktime
   793                 if len(ListGroup) > 2:
   800                 if len(ListGroup) > 2:
   794                     self._Ticktime = int(ListGroup[2][0]) 
   801                     self._Ticktime = int(ListGroup[2][0])
   795                 
   802 
   796             except Exception,e:
   803             except Exception,e:
   797                 self.logger.write_error(_("Cannot open/parse VARIABLES.csv!\n"))
   804                 self.logger.write_error(_("Cannot open/parse VARIABLES.csv!\n"))
   798                 self.logger.write_error(traceback.format_exc())
   805                 self.logger.write_error(traceback.format_exc())
   799                 self.ResetIECProgramsAndVariables()
   806                 self.ResetIECProgramsAndVariables()
   800                 return False
   807                 return False
   816               {"EXT":"extern __IEC_%(type)s_p %(C_path)s;",
   823               {"EXT":"extern __IEC_%(type)s_p %(C_path)s;",
   817                "IN":"extern __IEC_%(type)s_p %(C_path)s;",
   824                "IN":"extern __IEC_%(type)s_p %(C_path)s;",
   818                "MEM":"extern __IEC_%(type)s_p %(C_path)s;",
   825                "MEM":"extern __IEC_%(type)s_p %(C_path)s;",
   819                "OUT":"extern __IEC_%(type)s_p %(C_path)s;",
   826                "OUT":"extern __IEC_%(type)s_p %(C_path)s;",
   820                "VAR":"extern __IEC_%(type)s_t %(C_path)s;",
   827                "VAR":"extern __IEC_%(type)s_t %(C_path)s;",
   821                "FB":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v 
   828                "FB":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v
   822                for v in self._VariablesList if v["C_path"].find('.')<0]),
   829                for v in self._VariablesList if v["C_path"].find('.')<0]),
   823            "for_each_variable_do_code":"\n".join([
   830            "for_each_variable_do_code":"\n".join([
   824                {"EXT":"    (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n",
   831                {"EXT":"    (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n",
   825                 "IN":"    (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n",
   832                 "IN":"    (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n",
   826                 "MEM":"    (*fp)((void*)&(%(C_path)s),%(type)s_O_ENUM);\n",
   833                 "MEM":"    (*fp)((void*)&(%(C_path)s),%(type)s_O_ENUM);\n",
   834                 "IN":"        return %(type)s_P_ENUM;\n",
   841                 "IN":"        return %(type)s_P_ENUM;\n",
   835                 "MEM":"        return %(type)s_O_ENUM;\n",
   842                 "MEM":"        return %(type)s_O_ENUM;\n",
   836                 "OUT":"        return %(type)s_O_ENUM;\n",
   843                 "OUT":"        return %(type)s_O_ENUM;\n",
   837                 "VAR":"        return %(type)s_ENUM;\n"}[v["vartype"]]%v
   844                 "VAR":"        return %(type)s_ENUM;\n"}[v["vartype"]]%v
   838                 for v in self._VariablesList if v["vartype"] != "FB" and v["type"] in DebugTypesSize ])}
   845                 for v in self._VariablesList if v["vartype"] != "FB" and v["type"] in DebugTypesSize ])}
   839         
   846 
   840         return debug_code
   847         return debug_code
   841         
   848 
   842     def Generate_plc_main(self):
   849     def Generate_plc_main(self):
   843         """
   850         """
   844         Use confnodes layout given in LocationCFilesAndCFLAGS to
   851         Use confnodes layout given in LocationCFilesAndCFLAGS to
   845         generate glue code that dispatch calls to all confnodes
   852         generate glue code that dispatch calls to all confnodes
   846         """
   853         """
   847         # filter location that are related to code that will be called
   854         # filter location that are related to code that will be called
   848         # in retreive, publish, init, cleanup
   855         # in retreive, publish, init, cleanup
   849         locstrs = map(lambda x:"_".join(map(str,x)),
   856         locstrs = map(lambda x:"_".join(map(str,x)),
   850            [loc for loc,Cfiles,DoCalls in self.LocationCFilesAndCFLAGS if loc and DoCalls])
   857            [loc for loc,Cfiles,DoCalls in self.LocationCFilesAndCFLAGS if loc and DoCalls])
   851         
   858 
   852         # Generate main, based on template
   859         # Generate main, based on template
   853         if not self.BeremizRoot.getDisable_Extensions():
   860         if not self.BeremizRoot.getDisable_Extensions():
   854             plc_main_code = targets.GetCode("plc_main_head") % {
   861             plc_main_code = targets.GetCode("plc_main_head") % {
   855                 "calls_prototypes":"\n".join([(
   862                 "calls_prototypes":"\n".join([(
   856                       "int __init_%(s)s(int argc,char **argv);\n"+
   863                       "int __init_%(s)s(int argc,char **argv);\n"+
   880                 }
   887                 }
   881         plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent().getLocalTag())
   888         plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent().getLocalTag())
   882         plc_main_code += targets.GetCode("plc_main_tail")
   889         plc_main_code += targets.GetCode("plc_main_tail")
   883         return plc_main_code
   890         return plc_main_code
   884 
   891 
   885         
   892 
   886     def _Build(self):
   893     def _Build(self):
   887         """
   894         """
   888         Method called by user to (re)build SoftPLC and confnode tree
   895         Method called by user to (re)build SoftPLC and confnode tree
   889         """
   896         """
   890         if self.AppFrame is not None:
   897         if self.AppFrame is not None:
   891             self.AppFrame.ClearErrors()
   898             self.AppFrame.ClearErrors()
   892         self._CloseView(self._IECCodeView)
   899         self._CloseView(self._IECCodeView)
   893         
   900 
   894         buildpath = self._getBuildPath()
   901         buildpath = self._getBuildPath()
   895 
   902 
   896         # Eventually create build dir
   903         # Eventually create build dir
   897         if not os.path.exists(buildpath):
   904         if not os.path.exists(buildpath):
   898             os.mkdir(buildpath)
   905             os.mkdir(buildpath)
   906         IECGenRes = self._Generate_SoftPLC()
   913         IECGenRes = self._Generate_SoftPLC()
   907         self.ShowMethod("_showIECcode", True)
   914         self.ShowMethod("_showIECcode", True)
   908 
   915 
   909         # If IEC code gen fail, bail out.
   916         # If IEC code gen fail, bail out.
   910         if not IECGenRes:
   917         if not IECGenRes:
   911             self.logger.write_error(_("IEC-61131-3 code generation failed !\n"))
   918             self.logger.write_error(_("PLC code generation failed !\n"))
   912             self.ResetBuildMD5()
   919             self.ResetBuildMD5()
   913             return False
   920             return False
   914 
   921 
   915         # Reset variable and program list that are parsed from
   922         # Reset variable and program list that are parsed from
   916         # CSV file generated by IEC2C compiler.
   923         # CSV file generated by IEC2C compiler.
   917         self.ResetIECProgramsAndVariables()
   924         self.ResetIECProgramsAndVariables()
   918         
   925 
       
   926         # Collect platform specific C code
       
   927         # Code and other files from extension
       
   928         if not self._Generate_runtime():
       
   929             return False
       
   930 
       
   931         # Get current or fresh builder
       
   932         builder = self.GetBuilder()
       
   933         if builder is None:
       
   934             self.logger.write_error(_("Fatal : cannot get builder.\n"))
       
   935             self.ResetBuildMD5()
       
   936             return False
       
   937 
       
   938         # Build
       
   939         try:
       
   940             if not builder.build() :
       
   941                 self.logger.write_error(_("C Build failed.\n"))
       
   942                 return False
       
   943         except Exception, exc:
       
   944             self.logger.write_error(_("C Build crashed !\n"))
       
   945             self.logger.write_error(traceback.format_exc())
       
   946             self.ResetBuildMD5()
       
   947             return False
       
   948 
       
   949         self.logger.write(_("Successfully built.\n"))
       
   950         # Update GUI status about need for transfer
       
   951         self.CompareLocalAndRemotePLC()
       
   952         return True
       
   953 
       
   954     def _Generate_runtime(self):
       
   955         buildpath = self._getBuildPath()
       
   956 
   919         # Generate C code and compilation params from confnode hierarchy
   957         # Generate C code and compilation params from confnode hierarchy
   920         try:
   958         try:
   921             CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C(
   959             CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C(
   922                 buildpath, 
   960                 buildpath,
   923                 self.PLCGeneratedLocatedVars)
   961                 self.PLCGeneratedLocatedVars)
   924         except Exception, exc:
   962         except Exception, exc:
   925             self.logger.write_error(_("Runtime extensions C code generation failed !\n"))
   963             self.logger.write_error(_("Runtime IO extensions C code generation failed !\n"))
   926             self.logger.write_error(traceback.format_exc())
   964             self.logger.write_error(traceback.format_exc())
   927             self.ResetBuildMD5()
   965             self.ResetBuildMD5()
   928             return False
   966             return False
   929 
   967 
   930         # Generate C code and compilation params from liraries
   968         # Generate C code and compilation params from liraries
   931         try:
   969         try:
   932             LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(buildpath)
   970             LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(buildpath)
   933         except Exception, exc:
   971         except Exception, exc:
   934             self.logger.write_error(_("Runtime extensions C code generation failed !\n"))
   972             self.logger.write_error(_("Runtime library extensions C code generation failed !\n"))
   935             self.logger.write_error(traceback.format_exc())
   973             self.logger.write_error(traceback.format_exc())
   936             self.ResetBuildMD5()
   974             self.ResetBuildMD5()
   937             return False
   975             return False
   938 
   976 
   939         self.LocationCFilesAndCFLAGS =  CTNLocationCFilesAndCFLAGS + LibCFilesAndCFLAGS
   977         self.LocationCFilesAndCFLAGS =  CTNLocationCFilesAndCFLAGS + LibCFilesAndCFLAGS
   940         self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS
   978         self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS
   941         ExtraFiles = CTNExtraFiles + LibExtraFiles
   979         ExtraFiles = CTNExtraFiles + LibExtraFiles
   942         
   980 
   943         # Get temporary directory path
   981         # Get temporary directory path
   944         extrafilespath = self._getExtraFilesPath()
   982         extrafilespath = self._getExtraFilesPath()
   945         # Remove old directory
   983         # Remove old directory
   946         if os.path.exists(extrafilespath):
   984         if os.path.exists(extrafilespath):
   947             shutil.rmtree(extrafilespath)
   985             shutil.rmtree(extrafilespath)
   951         for fname,fobject in ExtraFiles:
   989         for fname,fobject in ExtraFiles:
   952             fpath = os.path.join(extrafilespath,fname)
   990             fpath = os.path.join(extrafilespath,fname)
   953             open(fpath, "wb").write(fobject.read())
   991             open(fpath, "wb").write(fobject.read())
   954         # Now we can forget ExtraFiles (will close files object)
   992         # Now we can forget ExtraFiles (will close files object)
   955         del ExtraFiles
   993         del ExtraFiles
   956         
   994 
   957         # Header file for extensions
   995         # Header file for extensions
   958         open(os.path.join(buildpath,"beremiz.h"), "w").write(targets.GetHeader())
   996         open(os.path.join(buildpath,"beremiz.h"), "w").write(targets.GetHeader())
   959 
   997 
   960         # Template based part of C code generation
   998         # Template based part of C code generation
   961         # files are stacked at the beginning, as files of confnode tree root
   999         # files are stacked at the beginning, as files of confnode tree root
   976             except Exception, exc:
  1014             except Exception, exc:
   977                 self.logger.write_error(name+_(" generation failed !\n"))
  1015                 self.logger.write_error(name+_(" generation failed !\n"))
   978                 self.logger.write_error(traceback.format_exc())
  1016                 self.logger.write_error(traceback.format_exc())
   979                 self.ResetBuildMD5()
  1017                 self.ResetBuildMD5()
   980                 return False
  1018                 return False
   981 
       
   982         self.logger.write(_("C code generated successfully.\n"))
  1019         self.logger.write(_("C code generated successfully.\n"))
   983 
       
   984         # Get current or fresh builder
       
   985         builder = self.GetBuilder()
       
   986         if builder is None:
       
   987             self.logger.write_error(_("Fatal : cannot get builder.\n"))
       
   988             self.ResetBuildMD5()
       
   989             return False
       
   990 
       
   991         # Build
       
   992         try:
       
   993             if not builder.build() :
       
   994                 self.logger.write_error(_("C Build failed.\n"))
       
   995                 return False
       
   996         except Exception, exc:
       
   997             self.logger.write_error(_("C Build crashed !\n"))
       
   998             self.logger.write_error(traceback.format_exc())
       
   999             self.ResetBuildMD5()
       
  1000             return False
       
  1001 
       
  1002         self.logger.write(_("Successfully built.\n"))
       
  1003         # Update GUI status about need for transfer
       
  1004         self.CompareLocalAndRemotePLC()
       
  1005         return True
  1020         return True
  1006     
  1021 
  1007     def ShowError(self, logger, from_location, to_location):
  1022     def ShowError(self, logger, from_location, to_location):
  1008         chunk_infos = self.GetChunkInfos(from_location, to_location)
  1023         chunk_infos = self.GetChunkInfos(from_location, to_location)
  1009         for infos, (start_row, start_col) in chunk_infos:
  1024         for infos, (start_row, start_col) in chunk_infos:
  1010             start = (from_location[0] - start_row, from_location[1] - start_col)
  1025             start = (from_location[0] - start_row, from_location[1] - start_col)
  1011             end = (to_location[0] - start_row, to_location[1] - start_col)
  1026             end = (to_location[0] - start_row, to_location[1] - start_col)
  1012             if self.AppFrame is not None:
  1027             if self.AppFrame is not None:
  1013                 self.AppFrame.ShowError(infos, start, end)
  1028                 self.AppFrame.ShowError(infos, start, end)
  1014     
  1029 
  1015     _IECCodeView = None
  1030     _IECCodeView = None
  1016     def _showIECcode(self):
  1031     def _showIECcode(self):
  1017         self._OpenView("IEC code")
  1032         self._OpenView("IEC code")
  1018 
  1033 
  1019     _IECRawCodeView = None
  1034     _IECRawCodeView = None
  1020     def _editIECrawcode(self):
  1035     def _editIECrawcode(self):
  1021         self._OpenView("IEC raw code")
  1036         self._OpenView("IEC raw code")
  1022     
  1037 
  1023     _ProjectFilesView = None
  1038     _ProjectFilesView = None
  1024     def _OpenProjectFiles(self):
  1039     def _OpenProjectFiles(self):
  1025         self._OpenView("Project Files")
  1040         self._OpenView("Project Files")
  1026     
  1041 
  1027     _FileEditors = {}
  1042     _FileEditors = {}
  1028     def _OpenFileEditor(self, filepath):
  1043     def _OpenFileEditor(self, filepath):
  1029         self._OpenView(filepath)
  1044         self._OpenView(filepath)
  1030     
  1045 
  1031     def _OpenView(self, name=None, onlyopened=False):
  1046     def _OpenView(self, name=None, onlyopened=False):
  1032         if name == "IEC code":
  1047         if name == "IEC code":
  1033             if self._IECCodeView is None:
  1048             if self._IECCodeView is None:
  1034                 plc_file = self._getIECcodepath()
  1049                 plc_file = self._getIECcodepath()
  1035             
  1050 
  1036                 self._IECCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name)
  1051                 self._IECCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name)
  1037                 self._IECCodeView.SetTextSyntax("ALL")
  1052                 self._IECCodeView.SetTextSyntax("ALL")
  1038                 self._IECCodeView.SetKeywords(IEC_KEYWORDS)
  1053                 self._IECCodeView.SetKeywords(IEC_KEYWORDS)
  1039                 try:
  1054                 try:
  1040                     text = file(plc_file).read()
  1055                     text = file(plc_file).read()
  1041                 except:
  1056                 except:
  1042                     text = '(* No IEC code have been generated at that time ! *)'
  1057                     text = '(* No IEC code have been generated at that time ! *)'
  1043                 self._IECCodeView.SetText(text = text)
  1058                 self._IECCodeView.SetText(text = text)
  1044                 self._IECCodeView.SetIcon(GetBitmap("ST"))
  1059                 self._IECCodeView.SetIcon(GetBitmap("ST"))
  1045                 setattr(self._IECCodeView, "_OnClose", self.OnCloseEditor)
  1060                 setattr(self._IECCodeView, "_OnClose", self.OnCloseEditor)
  1046             
  1061 
  1047             if self._IECCodeView is not None:
  1062             if self._IECCodeView is not None:
  1048                 self.AppFrame.EditProjectElement(self._IECCodeView, name)
  1063                 self.AppFrame.EditProjectElement(self._IECCodeView, name)
  1049             
  1064 
  1050             return self._IECCodeView
  1065             return self._IECCodeView
  1051         
  1066 
  1052         elif name == "IEC raw code":
  1067         elif name == "IEC raw code":
  1053             if self._IECRawCodeView is None:
  1068             if self._IECRawCodeView is None:
  1054                 controler = MiniTextControler(self._getIECrawcodepath(), self)
  1069                 controler = MiniTextControler(self._getIECrawcodepath(), self)
  1055                 
  1070 
  1056                 self._IECRawCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name)
  1071                 self._IECRawCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name)
  1057                 self._IECRawCodeView.SetTextSyntax("ALL")
  1072                 self._IECRawCodeView.SetTextSyntax("ALL")
  1058                 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS)
  1073                 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS)
  1059                 self._IECRawCodeView.RefreshView()
  1074                 self._IECRawCodeView.RefreshView()
  1060                 self._IECRawCodeView.SetIcon(GetBitmap("ST"))
  1075                 self._IECRawCodeView.SetIcon(GetBitmap("ST"))
  1061                 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor)
  1076                 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor)
  1062             
  1077 
  1063             if self._IECRawCodeView is not None:
  1078             if self._IECRawCodeView is not None:
  1064                 self.AppFrame.EditProjectElement(self._IECRawCodeView, name)
  1079                 self.AppFrame.EditProjectElement(self._IECRawCodeView, name)
  1065             
  1080 
  1066             return self._IECRawCodeView
  1081             return self._IECRawCodeView
  1067         
  1082 
  1068         elif name == "Project Files":
  1083         elif name == "Project Files":
  1069             if self._ProjectFilesView is None:
  1084             if self._ProjectFilesView is None:
  1070                 self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True)
  1085                 self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True)
  1071                 
  1086 
  1072                 extensions = []
  1087                 extensions = []
  1073                 for extension, name, editor in features.file_editors:
  1088                 for extension, name, editor in features.file_editors:
  1074                     if extension not in extensions:
  1089                     if extension not in extensions:
  1075                         extensions.append(extension)
  1090                         extensions.append(extension)
  1076                 self._ProjectFilesView.SetEditableFileExtensions(extensions) 
  1091                 self._ProjectFilesView.SetEditableFileExtensions(extensions)
  1077                 
  1092 
  1078             if self._ProjectFilesView is not None:
  1093             if self._ProjectFilesView is not None:
  1079                 self.AppFrame.EditProjectElement(self._ProjectFilesView, name)
  1094                 self.AppFrame.EditProjectElement(self._ProjectFilesView, name)
  1080             
  1095 
  1081             return self._ProjectFilesView
  1096             return self._ProjectFilesView
  1082         
  1097 
  1083         elif name is not None and name.find("::") != -1:
  1098         elif name is not None and name.find("::") != -1:
  1084             filepath, editor_name = name.split("::")
  1099             filepath, editor_name = name.split("::")
  1085             if not self._FileEditors.has_key(filepath):
  1100             if not self._FileEditors.has_key(filepath):
  1086                 if os.path.isfile(filepath):
  1101                 if os.path.isfile(filepath):
  1087                     file_extension = os.path.splitext(filepath)[1]
  1102                     file_extension = os.path.splitext(filepath)[1]
  1088                         
  1103 
  1089                     editors = dict([(edit_name, edit_class)
  1104                     editors = dict([(edit_name, edit_class)
  1090                                     for extension, edit_name, edit_class in features.file_editors
  1105                                     for extension, edit_name, edit_class in features.file_editors
  1091                                     if extension == file_extension])
  1106                                     if extension == file_extension])
  1092                     
  1107 
  1093                     if editor_name == "":
  1108                     if editor_name == "":
  1094                         if len(editors) == 1:
  1109                         if len(editors) == 1:
  1095                             editor_name = editors.keys()[0]
  1110                             editor_name = editors.keys()[0]
  1096                         elif len(editors) > 0:
  1111                         elif len(editors) > 0:
  1097                             names = editors.keys()
  1112                             names = editors.keys()
  1098                             dialog = wx.SingleChoiceDialog(self.AppFrame, 
  1113                             dialog = wx.SingleChoiceDialog(self.AppFrame,
  1099                                   _("Select an editor:"), _("Editor selection"), 
  1114                                   _("Select an editor:"), _("Editor selection"),
  1100                                   names, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL)
  1115                                   names, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL)
  1101                             if dialog.ShowModal() == wx.ID_OK:
  1116                             if dialog.ShowModal() == wx.ID_OK:
  1102                                 editor_name = names[dialog.GetSelection()]
  1117                                 editor_name = names[dialog.GetSelection()]
  1103                             dialog.Destroy()
  1118                             dialog.Destroy()
  1104                         
  1119 
  1105                     if editor_name != "":
  1120                     if editor_name != "":
  1106                         name = "::".join([filepath, editor_name])
  1121                         name = "::".join([filepath, editor_name])
  1107                         
  1122 
  1108                         editor = editors[editor_name]()
  1123                         editor = editors[editor_name]()
  1109                         self._FileEditors[filepath] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame)
  1124                         self._FileEditors[filepath] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame)
  1110                         self._FileEditors[filepath].SetIcon(GetBitmap("FILE"))
  1125                         self._FileEditors[filepath].SetIcon(GetBitmap("FILE"))
  1111                         if isinstance(self._FileEditors[filepath], DebugViewer):
  1126                         if isinstance(self._FileEditors[filepath], DebugViewer):
  1112                             self._FileEditors[filepath].SetDataProducer(self)
  1127                             self._FileEditors[filepath].SetDataProducer(self)
  1113             
  1128 
  1114             if self._FileEditors.has_key(filepath):
  1129             if self._FileEditors.has_key(filepath):
  1115                 editor = self._FileEditors[filepath]
  1130                 editor = self._FileEditors[filepath]
  1116                 self.AppFrame.EditProjectElement(editor, editor.GetTagName())
  1131                 self.AppFrame.EditProjectElement(editor, editor.GetTagName())
  1117                 
  1132 
  1118             return self._FileEditors.get(filepath)
  1133             return self._FileEditors.get(filepath)
  1119         else:
  1134         else:
  1120             return ConfigTreeNode._OpenView(self, self.CTNName(), onlyopened)
  1135             return ConfigTreeNode._OpenView(self, self.CTNName(), onlyopened)
  1121 
  1136 
  1122     def OnCloseEditor(self, view):
  1137     def OnCloseEditor(self, view):
  1145 
  1160 
  1146     def UpdatePLCLog(self, log_count):
  1161     def UpdatePLCLog(self, log_count):
  1147         if log_count:
  1162         if log_count:
  1148             if self.AppFrame is not None:
  1163             if self.AppFrame is not None:
  1149                 self.AppFrame.LogViewer.SetLogCounters(log_count)
  1164                 self.AppFrame.LogViewer.SetLogCounters(log_count)
  1150     
  1165 
  1151     def UpdateMethodsFromPLCStatus(self):
  1166     def UpdateMethodsFromPLCStatus(self):
  1152         status = None
  1167         status = None
  1153         if self._connector is not None:
  1168         if self._connector is not None:
  1154             PLCstatus = self._connector.GetPLCstatus()
  1169             PLCstatus = self._connector.GetPLCstatus()
  1155             if PLCstatus is not None:
  1170             if PLCstatus is not None:
  1182                     self.AppFrame.ConnectionStatusBar.SetStatusText('', 2)
  1197                     self.AppFrame.ConnectionStatusBar.SetStatusText('', 2)
  1183                 else:
  1198                 else:
  1184                     self.AppFrame.ConnectionStatusBar.SetStatusText(
  1199                     self.AppFrame.ConnectionStatusBar.SetStatusText(
  1185                         _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1)
  1200                         _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1)
  1186                     self.AppFrame.ConnectionStatusBar.SetStatusText(_(status), 2)
  1201                     self.AppFrame.ConnectionStatusBar.SetStatusText(_(status), 2)
  1187                 
  1202 
  1188     def PullPLCStatusProc(self, event):
  1203     def PullPLCStatusProc(self, event):
  1189         self.UpdateMethodsFromPLCStatus()
  1204         self.UpdateMethodsFromPLCStatus()
  1190         
  1205 
  1191     def SnapshotAndResetDebugValuesBuffers(self):
  1206     def SnapshotAndResetDebugValuesBuffers(self):
  1192         buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers, 
  1207         buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers,
  1193             [list() for iec_path in self.TracedIECPath])
  1208             [list() for iec_path in self.TracedIECPath])
  1194         ticks, self.DebugTicks = self.DebugTicks, []
  1209         ticks, self.DebugTicks = self.DebugTicks, []
  1195         return ticks, buffers
  1210         return ticks, buffers
  1196     
  1211 
  1197     def RegisterDebugVarToConnector(self):
  1212     def RegisterDebugVarToConnector(self):
  1198         self.DebugTimer=None
  1213         self.DebugTimer=None
  1199         Idxs = []
  1214         Idxs = []
  1200         self.TracedIECPath = []
  1215         self.TracedIECPath = []
  1201         if self._connector is not None:
  1216         if self._connector is not None:
  1206                 if len(WeakCallableDict) == 0:
  1221                 if len(WeakCallableDict) == 0:
  1207                     # Callable Dict is empty.
  1222                     # Callable Dict is empty.
  1208                     # This variable is not needed anymore!
  1223                     # This variable is not needed anymore!
  1209                     IECPathsToPop.append(IECPath)
  1224                     IECPathsToPop.append(IECPath)
  1210                 elif IECPath != "__tick__":
  1225                 elif IECPath != "__tick__":
  1211                     # Convert 
  1226                     # Convert
  1212                     Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None))
  1227                     Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None))
  1213                     if Idx is not None:
  1228                     if Idx is not None:
  1214                         if IEC_Type in DebugTypesSize: 
  1229                         if IEC_Type in DebugTypesSize:
  1215                             Idxs.append((Idx, IEC_Type, fvalue, IECPath))
  1230                             Idxs.append((Idx, IEC_Type, fvalue, IECPath))
  1216                         else:
  1231                         else:
  1217                             self.logger.write_warning(_("Debug: Unsupported type to debug '%s'\n")%IEC_Type)
  1232                             self.logger.write_warning(_("Debug: Unsupported type to debug '%s'\n")%IEC_Type)
  1218                     else:
  1233                     else:
  1219                         self.logger.write_warning(_("Debug: Unknown variable '%s'\n")%IECPath)
  1234                         self.logger.write_warning(_("Debug: Unknown variable '%s'\n")%IECPath)
  1227             else:
  1242             else:
  1228                 self.TracedIECPath = []
  1243                 self.TracedIECPath = []
  1229                 self._connector.SetTraceVariablesList([])
  1244                 self._connector.SetTraceVariablesList([])
  1230             self.SnapshotAndResetDebugValuesBuffers()
  1245             self.SnapshotAndResetDebugValuesBuffers()
  1231             self.IECdebug_lock.release()
  1246             self.IECdebug_lock.release()
  1232     
  1247 
  1233     def IsPLCStarted(self):
  1248     def IsPLCStarted(self):
  1234         return self.previous_plcstate == "Started"
  1249         return self.previous_plcstate == "Started"
  1235     
  1250 
  1236     def ReArmDebugRegisterTimer(self):
  1251     def ReArmDebugRegisterTimer(self):
  1237         if self.DebugTimer is not None:
  1252         if self.DebugTimer is not None:
  1238             self.DebugTimer.cancel()
  1253             self.DebugTimer.cancel()
  1239         
  1254 
  1240         # Prevent to call RegisterDebugVarToConnector when PLC is not started
  1255         # Prevent to call RegisterDebugVarToConnector when PLC is not started
  1241         # If an output location var is forced it's leads to segmentation fault in runtime
  1256         # If an output location var is forced it's leads to segmentation fault in runtime
  1242         # Links between PLC located variables and real variables are not ready
  1257         # Links between PLC located variables and real variables are not ready
  1243         if self.IsPLCStarted():
  1258         if self.IsPLCStarted():
  1244             # Timer to prevent rapid-fire when registering many variables
  1259             # Timer to prevent rapid-fire when registering many variables
  1248             self.DebugTimer.start()
  1263             self.DebugTimer.start()
  1249 
  1264 
  1250     def GetDebugIECVariableType(self, IECPath):
  1265     def GetDebugIECVariableType(self, IECPath):
  1251         Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None))
  1266         Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None))
  1252         return IEC_Type
  1267         return IEC_Type
  1253         
  1268 
  1254     def SubscribeDebugIECVariable(self, IECPath, callableobj, buffer_list=False, *args, **kwargs):
  1269     def SubscribeDebugIECVariable(self, IECPath, callableobj, buffer_list=False, *args, **kwargs):
  1255         """
  1270         """
  1256         Dispatching use a dictionnary linking IEC variable paths
  1271         Dispatching use a dictionnary linking IEC variable paths
  1257         to a WeakKeyDictionary linking 
  1272         to a WeakKeyDictionary linking
  1258         weakly referenced callables to optionnal args
  1273         weakly referenced callables to optionnal args
  1259         """
  1274         """
  1260         if IECPath != "__tick__" and not self._IECPathToIdx.has_key(IECPath):
  1275         if IECPath != "__tick__" and not self._IECPathToIdx.has_key(IECPath):
  1261             return None
  1276             return None
  1262         
  1277 
  1263         self.IECdebug_lock.acquire()
  1278         self.IECdebug_lock.acquire()
  1264         # If no entry exist, create a new one with a fresh WeakKeyDictionary
  1279         # If no entry exist, create a new one with a fresh WeakKeyDictionary
  1265         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
  1280         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
  1266         if IECdebug_data is None:
  1281         if IECdebug_data is None:
  1267             IECdebug_data  = [
  1282             IECdebug_data  = [
  1271                     None,
  1286                     None,
  1272                     buffer_list]                # Forced value
  1287                     buffer_list]                # Forced value
  1273             self.IECdebug_datas[IECPath] = IECdebug_data
  1288             self.IECdebug_datas[IECPath] = IECdebug_data
  1274         else:
  1289         else:
  1275             IECdebug_data[4] |= buffer_list
  1290             IECdebug_data[4] |= buffer_list
  1276         
  1291 
  1277         IECdebug_data[0][callableobj]=(buffer_list, args, kwargs)
  1292         IECdebug_data[0][callableobj]=(buffer_list, args, kwargs)
  1278 
  1293 
  1279         self.IECdebug_lock.release()
  1294         self.IECdebug_lock.release()
  1280         
  1295 
  1281         self.ReArmDebugRegisterTimer()
  1296         self.ReArmDebugRegisterTimer()
  1282         
  1297 
  1283         return IECdebug_data[1]
  1298         return IECdebug_data[1]
  1284 
  1299 
  1285     def UnsubscribeDebugIECVariable(self, IECPath, callableobj):
  1300     def UnsubscribeDebugIECVariable(self, IECPath, callableobj):
  1286         self.IECdebug_lock.acquire()
  1301         self.IECdebug_lock.acquire()
  1287         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
  1302         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
  1290             if len(IECdebug_data[0]) == 0:
  1305             if len(IECdebug_data[0]) == 0:
  1291                 self.IECdebug_datas.pop(IECPath)
  1306                 self.IECdebug_datas.pop(IECPath)
  1292             else:
  1307             else:
  1293                 IECdebug_data[4] = reduce(
  1308                 IECdebug_data[4] = reduce(
  1294                     lambda x, y: x|y,
  1309                     lambda x, y: x|y,
  1295                     [buffer_list for buffer_list,args,kwargs 
  1310                     [buffer_list for buffer_list,args,kwargs
  1296                      in IECdebug_data[0].itervalues()],
  1311                      in IECdebug_data[0].itervalues()],
  1297                     False)
  1312                     False)
  1298         self.IECdebug_lock.release()
  1313         self.IECdebug_lock.release()
  1299 
  1314 
  1300         self.ReArmDebugRegisterTimer()
  1315         self.ReArmDebugRegisterTimer()
  1307         self.ReArmDebugRegisterTimer()
  1322         self.ReArmDebugRegisterTimer()
  1308 
  1323 
  1309     def ForceDebugIECVariable(self, IECPath, fvalue):
  1324     def ForceDebugIECVariable(self, IECPath, fvalue):
  1310         if not self.IECdebug_datas.has_key(IECPath):
  1325         if not self.IECdebug_datas.has_key(IECPath):
  1311             return
  1326             return
  1312         
  1327 
  1313         self.IECdebug_lock.acquire()
  1328         self.IECdebug_lock.acquire()
  1314         
  1329 
  1315         # If no entry exist, create a new one with a fresh WeakKeyDictionary
  1330         # If no entry exist, create a new one with a fresh WeakKeyDictionary
  1316         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
  1331         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
  1317         IECdebug_data[2] = "Forced"
  1332         IECdebug_data[2] = "Forced"
  1318         IECdebug_data[3] = fvalue
  1333         IECdebug_data[3] = fvalue
  1319         
  1334 
  1320         self.IECdebug_lock.release()
  1335         self.IECdebug_lock.release()
  1321         
  1336 
  1322         self.ReArmDebugRegisterTimer()
  1337         self.ReArmDebugRegisterTimer()
  1323     
  1338 
  1324     def ReleaseDebugIECVariable(self, IECPath):
  1339     def ReleaseDebugIECVariable(self, IECPath):
  1325         if not self.IECdebug_datas.has_key(IECPath):
  1340         if not self.IECdebug_datas.has_key(IECPath):
  1326             return
  1341             return
  1327         
  1342 
  1328         self.IECdebug_lock.acquire()
  1343         self.IECdebug_lock.acquire()
  1329         
  1344 
  1330         # If no entry exist, create a new one with a fresh WeakKeyDictionary
  1345         # If no entry exist, create a new one with a fresh WeakKeyDictionary
  1331         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
  1346         IECdebug_data = self.IECdebug_datas.get(IECPath, None)
  1332         IECdebug_data[2] = "Registered"
  1347         IECdebug_data[2] = "Registered"
  1333         IECdebug_data[3] = None
  1348         IECdebug_data[3] = None
  1334         
  1349 
  1335         self.IECdebug_lock.release()
  1350         self.IECdebug_lock.release()
  1336         
  1351 
  1337         self.ReArmDebugRegisterTimer()
  1352         self.ReArmDebugRegisterTimer()
  1338     
  1353 
  1339     def CallWeakcallables(self, IECPath, function_name, *cargs):
  1354     def CallWeakcallables(self, IECPath, function_name, *cargs):
  1340         data_tuple = self.IECdebug_datas.get(IECPath, None)
  1355         data_tuple = self.IECdebug_datas.get(IECPath, None)
  1341         if data_tuple is not None:
  1356         if data_tuple is not None:
  1342             WeakCallableDict, data_log, status, fvalue, buffer_list = data_tuple
  1357             WeakCallableDict, data_log, status, fvalue, buffer_list = data_tuple
  1343             #data_log.append((debug_tick, value))
  1358             #data_log.append((debug_tick, value))
  1413             for IECPath, values in zip(self.TracedIECPath, buffers):
  1428             for IECPath, values in zip(self.TracedIECPath, buffers):
  1414                 if len(values) > 0:
  1429                 if len(values) > 0:
  1415                     self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values)
  1430                     self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values)
  1416             if len(debug_ticks) > 0:
  1431             if len(debug_ticks) > 0:
  1417                 self.CallWeakcallables("__tick__", "NewDataAvailable", debug_ticks)
  1432                 self.CallWeakcallables("__tick__", "NewDataAvailable", debug_ticks)
  1418         
  1433 
  1419         delay = time.time() - start_time
  1434         delay = time.time() - start_time
  1420         next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay)
  1435         next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay)
  1421         if self.DispatchDebugValuesTimer is not None and self.DebugThread is not None:
  1436         if self.DispatchDebugValuesTimer is not None and self.DebugThread is not None:
  1422             self.DispatchDebugValuesTimer.Start(
  1437             self.DispatchDebugValuesTimer.Start(
  1423                 int(next_refresh * 1000), oneShot=True)
  1438                 int(next_refresh * 1000), oneShot=True)
  1435                 self.logger.write(_("Debugger stopped.\n"))
  1450                 self.logger.write(_("Debugger stopped.\n"))
  1436         self.DebugThread = None
  1451         self.DebugThread = None
  1437         if self.DispatchDebugValuesTimer is not None:
  1452         if self.DispatchDebugValuesTimer is not None:
  1438             self.DispatchDebugValuesTimer.Stop()
  1453             self.DispatchDebugValuesTimer.Stop()
  1439 
  1454 
  1440     def _connect_debug(self): 
  1455     def _connect_debug(self):
  1441         self.previous_plcstate = None
  1456         self.previous_plcstate = None
  1442         if self.AppFrame:
  1457         if self.AppFrame:
  1443             self.AppFrame.ResetGraphicViewers()
  1458             self.AppFrame.ResetGraphicViewers()
  1444         self.RegisterDebugVarToConnector()
  1459         self.RegisterDebugVarToConnector()
  1445         if self.DispatchDebugValuesTimer is not None:
  1460         if self.DispatchDebugValuesTimer is not None:
  1446             self.DispatchDebugValuesTimer.Start(
  1461             self.DispatchDebugValuesTimer.Start(
  1447                 int(REFRESH_PERIOD * 1000), oneShot=True)
  1462                 int(REFRESH_PERIOD * 1000), oneShot=True)
  1448         if self.DebugThread is None:
  1463         if self.DebugThread is None:
  1449             self.DebugThread = Thread(target=self.DebugThreadProc)
  1464             self.DebugThread = Thread(target=self.DebugThreadProc)
  1450             self.DebugThread.start()
  1465             self.DebugThread.start()
  1451     
  1466 
  1452     def _Run(self):
  1467     def _Run(self):
  1453         """
  1468         """
  1454         Start PLC
  1469         Start PLC
  1455         """
  1470         """
  1456         if self.GetIECProgramsAndVariables():
  1471         if self.GetIECProgramsAndVariables():
  1458             self.logger.write(_("Starting PLC\n"))
  1473             self.logger.write(_("Starting PLC\n"))
  1459             self._connect_debug()
  1474             self._connect_debug()
  1460         else:
  1475         else:
  1461             self.logger.write_error(_("Couldn't start PLC !\n"))
  1476             self.logger.write_error(_("Couldn't start PLC !\n"))
  1462         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1477         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1463        
  1478 
  1464     def _Stop(self):
  1479     def _Stop(self):
  1465         """
  1480         """
  1466         Stop PLC
  1481         Stop PLC
  1467         """
  1482         """
  1468         if self._connector is not None and not self._connector.StopPLC():
  1483         if self._connector is not None and not self._connector.StopPLC():
  1469             self.logger.write_error(_("Couldn't stop PLC !\n"))
  1484             self.logger.write_error(_("Couldn't stop PLC !\n"))
  1470 
  1485 
  1471         # debugthread should die on his own
  1486         # debugthread should die on his own
  1472         #self.KillDebugThread()
  1487         #self.KillDebugThread()
  1473         
  1488 
  1474         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1489         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1475 
  1490 
  1476     def _SetConnector(self, connector, update_status=True):
  1491     def _SetConnector(self, connector, update_status=True):
  1477         self._connector = connector
  1492         self._connector = connector
  1478         if self.AppFrame is not None:
  1493         if self.AppFrame is not None:
  1491     def _Connect(self):
  1506     def _Connect(self):
  1492         # don't accept re-connetion if already connected
  1507         # don't accept re-connetion if already connected
  1493         if self._connector is not None:
  1508         if self._connector is not None:
  1494             self.logger.write_error(_("Already connected. Please disconnect\n"))
  1509             self.logger.write_error(_("Already connected. Please disconnect\n"))
  1495             return
  1510             return
  1496         
  1511 
  1497         # Get connector uri
  1512         # Get connector uri
  1498         uri = self.\
  1513         uri = self.\
  1499               BeremizRoot.\
  1514               BeremizRoot.\
  1500               getURI_location().\
  1515               getURI_location().\
  1501               strip()
  1516               strip()
  1510                 dialog.Destroy()
  1525                 dialog.Destroy()
  1511             except:
  1526             except:
  1512                 self.logger.write_error(_("Local service discovery failed!\n"))
  1527                 self.logger.write_error(_("Local service discovery failed!\n"))
  1513                 self.logger.write_error(traceback.format_exc())
  1528                 self.logger.write_error(traceback.format_exc())
  1514                 uri = None
  1529                 uri = None
  1515             
  1530 
  1516             # Nothing choosed or cancel button
  1531             # Nothing choosed or cancel button
  1517             if uri is None or answer == wx.ID_CANCEL:
  1532             if uri is None or answer == wx.ID_CANCEL:
  1518                 self.logger.write_error(_("Connection canceled!\n"))
  1533                 self.logger.write_error(_("Connection canceled!\n"))
  1519                 return
  1534                 return
  1520             else:
  1535             else:
  1527                 if self.AppFrame is not None:
  1542                 if self.AppFrame is not None:
  1528                     self.AppFrame.RefreshTitle()
  1543                     self.AppFrame.RefreshTitle()
  1529                     self.AppFrame.RefreshFileMenu()
  1544                     self.AppFrame.RefreshFileMenu()
  1530                     self.AppFrame.RefreshEditMenu()
  1545                     self.AppFrame.RefreshEditMenu()
  1531                     self.AppFrame.RefreshPageTitles()
  1546                     self.AppFrame.RefreshPageTitles()
  1532                            
  1547 
  1533         # Get connector from uri
  1548         # Get connector from uri
  1534         try:
  1549         try:
  1535             self._SetConnector(connectors.ConnectorFactory(uri, self))
  1550             self._SetConnector(connectors.ConnectorFactory(uri, self))
  1536         except Exception, msg:
  1551         except Exception, msg:
  1537             self.logger.write_error(_("Exception while connecting %s!\n")%uri)
  1552             self.logger.write_error(_("Exception while connecting %s!\n")%uri)
  1545             self.ShowMethod("_Connect", False)
  1560             self.ShowMethod("_Connect", False)
  1546             self.ShowMethod("_Disconnect", True)
  1561             self.ShowMethod("_Disconnect", True)
  1547             self.ShowMethod("_Transfer", True)
  1562             self.ShowMethod("_Transfer", True)
  1548 
  1563 
  1549             self.CompareLocalAndRemotePLC()
  1564             self.CompareLocalAndRemotePLC()
  1550             
  1565 
  1551             # Init with actual PLC status and print it
  1566             # Init with actual PLC status and print it
  1552             self.UpdateMethodsFromPLCStatus()
  1567             self.UpdateMethodsFromPLCStatus()
  1553             if self.previous_plcstate is not None:
  1568             if self.previous_plcstate is not None:
  1554                 status = _(self.previous_plcstate)
  1569                 status = _(self.previous_plcstate)
  1555             else:
  1570             else:
  1556                 status = ""
  1571                 status = ""
  1557 
  1572 
  1558             #self.logger.write(_("PLC is %s\n")%status)
  1573             #self.logger.write(_("PLC is %s\n")%status)
  1559             
  1574 
  1560             if self.previous_plcstate in ["Started","Stopped"]:
  1575             if self.previous_plcstate in ["Started","Stopped"]:
  1561                 if self.DebugAvailable() and self.GetIECProgramsAndVariables():
  1576                 if self.DebugAvailable() and self.GetIECProgramsAndVariables():
  1562                     self.logger.write(_("Debugger ready\n"))
  1577                     self.logger.write(_("Debugger ready\n"))
  1563                     self._connect_debug()
  1578                     self._connect_debug()
  1564                 else:
  1579                 else:
  1588             self.EnableMethod("_Transfer", False)
  1603             self.EnableMethod("_Transfer", False)
  1589 
  1604 
  1590 
  1605 
  1591     def _Disconnect(self):
  1606     def _Disconnect(self):
  1592         self._SetConnector(None)
  1607         self._SetConnector(None)
  1593         
  1608 
  1594     def _Transfer(self):
  1609     def _Transfer(self):
  1595         # Get the last build PLC's 
  1610         # Get the last build PLC's
  1596         MD5 = self.GetLastBuildMD5()
  1611         MD5 = self.GetLastBuildMD5()
  1597         
  1612 
  1598         # Check if md5 file is empty : ask user to build PLC 
  1613         # Check if md5 file is empty : ask user to build PLC
  1599         if MD5 is None :
  1614         if MD5 is None :
  1600             self.logger.write_error(_("Failed : Must build before transfer.\n"))
  1615             self.logger.write_error(_("Failed : Must build before transfer.\n"))
  1601             return False
  1616             return False
  1602 
  1617 
  1603         # Compare PLC project with PLC on target
  1618         # Compare PLC project with PLC on target
  1607 
  1622 
  1608         # Get temprary directory path
  1623         # Get temprary directory path
  1609         extrafiles = []
  1624         extrafiles = []
  1610         for extrafilespath in [self._getExtraFilesPath(),
  1625         for extrafilespath in [self._getExtraFilesPath(),
  1611                                self._getProjectFilesPath()]:
  1626                                self._getProjectFilesPath()]:
  1612         
  1627 
  1613             extrafiles.extend(
  1628             extrafiles.extend(
  1614                      [(name, open(os.path.join(extrafilespath, name), 
  1629                      [(name, open(os.path.join(extrafilespath, name),
  1615                                   'rb').read()) \
  1630                                   'rb').read()) \
  1616                       for name in os.listdir(extrafilespath)])
  1631                       for name in os.listdir(extrafilespath)])
  1617         
  1632 
  1618         # Send PLC on target
  1633         # Send PLC on target
  1619         builder = self.GetBuilder()
  1634         builder = self.GetBuilder()
  1620         if builder is not None:
  1635         if builder is not None:
  1621             data = builder.GetBinaryCode()
  1636             data = builder.GetBinaryCode()
  1622             if data is not None :
  1637             if data is not None :