ProjectController.py
changeset 2279 70143c20d2c0
parent 2248 d9353e440887
child 2263 5227f54f6369
child 2415 f7d8891fe708
equal deleted inserted replaced
2278:a3ac46366b86 2279:70143c20d2c0
    63 from runtime.typemapping import DebugTypesSize, UnpackDebugBuffer
    63 from runtime.typemapping import DebugTypesSize, UnpackDebugBuffer
    64 from ConfigTreeNode import ConfigTreeNode, XSDSchemaErrorMessage
    64 from ConfigTreeNode import ConfigTreeNode, XSDSchemaErrorMessage
    65 
    65 
    66 base_folder = paths.AbsParentDir(__file__)
    66 base_folder = paths.AbsParentDir(__file__)
    67 
    67 
    68 MATIEC_ERROR_MODEL = re.compile(".*\.st:(\d+)-(\d+)\.\.(\d+)-(\d+): (?:error)|(?:warning) : (.*)$")
    68 MATIEC_ERROR_MODEL = re.compile(
       
    69     ".*\.st:(\d+)-(\d+)\.\.(\d+)-(\d+): (?:error)|(?:warning) : (.*)$")
    69 
    70 
    70 
    71 
    71 def ExtractChildrenTypesFromCatalog(catalog):
    72 def ExtractChildrenTypesFromCatalog(catalog):
    72     children_types = []
    73     children_types = []
    73     for n, d, _h, c in catalog:
    74     for n, d, _h, c in catalog:
    92 def GetAddMenuItems():
    93 def GetAddMenuItems():
    93     return ExtractMenuItemsFromCatalog(features.catalog)
    94     return ExtractMenuItemsFromCatalog(features.catalog)
    94 
    95 
    95 
    96 
    96 class Iec2CSettings(object):
    97 class Iec2CSettings(object):
       
    98 
    97     def __init__(self):
    99     def __init__(self):
    98         self.iec2c = None
   100         self.iec2c = None
    99         self.iec2c_buildopts = None
   101         self.iec2c_buildopts = None
   100         self.ieclib_path = self.findLibPath()
   102         self.ieclib_path = self.findLibPath()
   101         self.ieclib_c_path = self.findLibCPath()
   103         self.ieclib_c_path = self.findLibCPath()
   107                 path = p
   109                 path = p
   108                 break
   110                 break
   109         return path
   111         return path
   110 
   112 
   111     def findCmd(self):
   113     def findCmd(self):
   112         cmd = "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else "")
   114         cmd = "iec2c" + (".exe" if wx.Platform == '__WXMSW__' else "")
   113         paths = [
   115         paths = [
   114             os.path.join(base_folder, "matiec")
   116             os.path.join(base_folder, "matiec")
   115         ]
   117         ]
   116         path = self.findObject(paths, lambda p: os.path.isfile(os.path.join(p, cmd)))
   118         path = self.findObject(
       
   119             paths, lambda p: os.path.isfile(os.path.join(p, cmd)))
   117 
   120 
   118         # otherwise use iec2c from PATH
   121         # otherwise use iec2c from PATH
   119         if path is not None:
   122         if path is not None:
   120             cmd = os.path.join(path, cmd)
   123             cmd = os.path.join(path, cmd)
   121 
   124 
   124     def findLibPath(self):
   127     def findLibPath(self):
   125         paths = [
   128         paths = [
   126             os.path.join(base_folder, "matiec", "lib"),
   129             os.path.join(base_folder, "matiec", "lib"),
   127             "/usr/lib/matiec"
   130             "/usr/lib/matiec"
   128         ]
   131         ]
   129         path = self.findObject(paths, lambda p: os.path.isfile(os.path.join(p, "ieclib.txt")))
   132         path = self.findObject(
       
   133             paths, lambda p: os.path.isfile(os.path.join(p, "ieclib.txt")))
   130         return path
   134         return path
   131 
   135 
   132     def findLibCPath(self):
   136     def findLibCPath(self):
   133         path = None
   137         path = None
   134         if self.ieclib_path is not None:
   138         if self.ieclib_path is not None:
   144         buildcmd = "\"%s\" -h" % (self.getCmd())
   148         buildcmd = "\"%s\" -h" % (self.getCmd())
   145         options = ["-f", "-l", "-p"]
   149         options = ["-f", "-l", "-p"]
   146 
   150 
   147         buildopt = ""
   151         buildopt = ""
   148         try:
   152         try:
   149             # Invoke compiler. Output files are listed to stdout, errors to stderr
   153             # Invoke compiler.
       
   154             # Output files are listed to stdout, errors to stderr
   150             _status, result, _err_result = ProcessLogger(None, buildcmd,
   155             _status, result, _err_result = ProcessLogger(None, buildcmd,
   151                                                          no_stdout=True,
   156                                                          no_stdout=True,
   152                                                          no_stderr=True).spin()
   157                                                          no_stderr=True).spin()
   153         except Exception:
   158         except Exception:
   154             return buildopt
   159             return buildopt
   184         <xsd:complexType>
   189         <xsd:complexType>
   185           <xsd:sequence>
   190           <xsd:sequence>
   186             <xsd:element name="TargetType">
   191             <xsd:element name="TargetType">
   187               <xsd:complexType>
   192               <xsd:complexType>
   188                 <xsd:choice minOccurs="0">
   193                 <xsd:choice minOccurs="0">
   189                 """+targets.GetTargetChoices()+"""
   194                 """ + targets.GetTargetChoices() + """
   190                 </xsd:choice>
   195                 </xsd:choice>
   191               </xsd:complexType>
   196               </xsd:complexType>
   192             </xsd:element>"""+(("""
   197             </xsd:element>""" + (("""
   193             <xsd:element name="Libraries" minOccurs="0">
   198             <xsd:element name="Libraries" minOccurs="0">
   194               <xsd:complexType>
   199               <xsd:complexType>
   195               """+"\n".join(['<xsd:attribute name=' +
   200               """ + "\n".join(['<xsd:attribute name=' +
   196                              '"Enable_' + libname + '_Library" ' +
   201                                '"Enable_' + libname + '_Library" ' +
   197                              'type="xsd:boolean" use="optional" default="false"/>'
   202                                'type="xsd:boolean" use="optional" default="' +
   198                              for libname, _lib in features.libraries])+"""
   203                                ('true' if default else 'false') + '"/>'
       
   204                                for libname, _lib, default in features.libraries]) + """
   199               </xsd:complexType>
   205               </xsd:complexType>
   200             </xsd:element>""") if len(features.libraries) > 0 else '') + """
   206             </xsd:element>""") if len(features.libraries) > 0 else '') + """
   201           </xsd:sequence>
   207           </xsd:sequence>
   202           <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/>
   208           <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/>
   203           <xsd:attribute name="Disable_Extensions" type="xsd:boolean" use="optional" default="false"/>
   209           <xsd:attribute name="Disable_Extensions" type="xsd:boolean" use="optional" default="false"/>
   207     """
   213     """
   208     return XSD
   214     return XSD
   209 
   215 
   210 
   216 
   211 class ProjectController(ConfigTreeNode, PLCControler):
   217 class ProjectController(ConfigTreeNode, PLCControler):
       
   218 
   212     """
   219     """
   213     This class define Root object of the confnode tree.
   220     This class define Root object of the confnode tree.
   214     It is responsible of :
   221     It is responsible of :
   215     - Managing project directory
   222     - Managing project directory
   216     - Building project
   223     - Building project
   217     - Handling PLCOpenEditor controler and view
   224     - Handling PLCOpenEditor controler and view
   218     - Loading user confnodes and instanciante them as children
   225     - Loading user confnodes and instanciante them as children
   219     - ...
   226     - ...
   220 
   227 
   221     """
   228     """
   222     # For root object, available Children Types are modules of the confnode packages.
   229     # For root object, available Children Types are modules of the confnode
       
   230     # packages.
   223     CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog)
   231     CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog)
   224     XSD = GetProjectControllerXSD()
   232     XSD = GetProjectControllerXSD()
   225     EditorType = ProjectNodeEditor
   233     EditorType = ProjectNodeEditor
   226     iec2c_cfg = None
   234     iec2c_cfg = None
   227 
   235 
   268         self.KillDebugThread()
   276         self.KillDebugThread()
   269 
   277 
   270     def LoadLibraries(self):
   278     def LoadLibraries(self):
   271         self.Libraries = []
   279         self.Libraries = []
   272         TypeStack = []
   280         TypeStack = []
   273         for libname, clsname in features.libraries:
   281         for libname, clsname, _default in features.libraries:
   274             if self.BeremizRoot.Libraries is not None and getattr(self.BeremizRoot.Libraries, "Enable_"+libname+"_Library"):
   282             if self.BeremizRoot.Libraries is not None and \
       
   283                getattr(self.BeremizRoot.Libraries,
       
   284                        "Enable_" + libname + "_Library"):
   275                 Lib = GetClassImporter(clsname)()(self, libname, TypeStack)
   285                 Lib = GetClassImporter(clsname)()(self, libname, TypeStack)
   276                 TypeStack.append(Lib.GetTypes())
   286                 TypeStack.append(Lib.GetTypes())
   277                 self.Libraries.append(Lib)
   287                 self.Libraries.append(Lib)
   278 
   288 
   279     def SetAppFrame(self, frame, logger):
   289     def SetAppFrame(self, frame, logger):
   359         if target.getcontent() is None:
   369         if target.getcontent() is None:
   360             temp_root = self.Parser.CreateRoot()
   370             temp_root = self.Parser.CreateRoot()
   361             target = self.Parser.CreateElement("TargetType", "BeremizRoot")
   371             target = self.Parser.CreateElement("TargetType", "BeremizRoot")
   362             temp_root.setTargetType(target)
   372             temp_root.setTargetType(target)
   363             target_name = self.GetDefaultTargetName()
   373             target_name = self.GetDefaultTargetName()
   364             target.setcontent(self.Parser.CreateElement(target_name, "TargetType"))
   374             target.setcontent(
       
   375                 self.Parser.CreateElement(target_name, "TargetType"))
   365         return target
   376         return target
   366 
   377 
   367     def GetParamsAttributes(self, path=None):
   378     def GetParamsAttributes(self, path=None):
   368         params = ConfigTreeNode.GetParamsAttributes(self, path)
   379         params = ConfigTreeNode.GetParamsAttributes(self, path)
   369         if params[0]["name"] == "BeremizRoot":
   380         if params[0]["name"] == "BeremizRoot":
   370             for child in params[0]["children"]:
   381             for child in params[0]["children"]:
   371                 if child["name"] == "TargetType" and child["value"] == '':
   382                 if child["name"] == "TargetType" and child["value"] == '':
   372                     child.update(self.GetTarget().getElementInfos("TargetType"))
   383                     child.update(
       
   384                         self.GetTarget().getElementInfos("TargetType"))
   373         return params
   385         return params
   374 
   386 
   375     def SetParamsAttribute(self, path, value):
   387     def SetParamsAttribute(self, path, value):
   376         if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None:
   388         if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None:
   377             self.BeremizRoot.setTargetType(self.GetTarget())
   389             self.BeremizRoot.setTargetType(self.GetTarget())
   401         return False
   413         return False
   402 
   414 
   403     def _getProjectFilesPath(self, project_path=None):
   415     def _getProjectFilesPath(self, project_path=None):
   404         if project_path is not None:
   416         if project_path is not None:
   405             return os.path.join(project_path, "project_files")
   417             return os.path.join(project_path, "project_files")
   406         projectfiles_path = os.path.join(self.GetProjectPath(), "project_files")
   418         projectfiles_path = os.path.join(
       
   419             self.GetProjectPath(), "project_files")
   407         if not os.path.exists(projectfiles_path):
   420         if not os.path.exists(projectfiles_path):
   408             os.mkdir(projectfiles_path)
   421             os.mkdir(projectfiles_path)
   409         return projectfiles_path
   422         return projectfiles_path
   410 
   423 
   411     def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"):
   424     def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"):
   412         self.ProjectAddConfiguration(config_name)
   425         self.ProjectAddConfiguration(config_name)
   413         self.ProjectAddConfigurationResource(config_name, res_name)
   426         self.ProjectAddConfigurationResource(config_name, res_name)
   414 
   427 
   415     def SetProjectDefaultConfiguration(self):
   428     def SetProjectDefaultConfiguration(self):
   416         # Sets default task and instance for new project
   429         # Sets default task and instance for new project
   417         config = self.Project.getconfiguration(self.GetProjectMainConfigurationName())
   430         config = self.Project.getconfiguration(
       
   431             self.GetProjectMainConfigurationName())
   418         resource = config.getresource()[0].getname()
   432         resource = config.getresource()[0].getname()
   419         config = config.getname()
   433         config = config.getname()
   420         resource_tagname = ComputeConfigurationResourceName(config, resource)
   434         resource_tagname = ComputeConfigurationResourceName(config, resource)
   421         def_task = [
   435         def_task = [
   422             {'Priority': '0', 'Single': '', 'Interval': 'T#20ms', 'Name': 'task0', 'Triggering': 'Cyclic'}]
   436             {'Priority': '0', 'Single': '', 'Interval': 'T#20ms', 'Name': 'task0', 'Triggering': 'Cyclic'}]
   469         # Load PLCOpen file
   483         # Load PLCOpen file
   470         error = self.OpenXMLFile(plc_file)
   484         error = self.OpenXMLFile(plc_file)
   471         if error is not None:
   485         if error is not None:
   472             if self.Project is not None:
   486             if self.Project is not None:
   473                 (fname_err, lnum, src) = (("PLC",) + error)
   487                 (fname_err, lnum, src) = (("PLC",) + error)
   474                 self.logger.write_warning(XSDSchemaErrorMessage.format(a1=fname_err, a2=lnum, a3=src))
   488                 self.logger.write_warning(
       
   489                     XSDSchemaErrorMessage.format(a1=fname_err, a2=lnum, a3=src))
   475             else:
   490             else:
   476                 return error, False
   491                 return error, False
   477         if len(self.GetProjectConfigNames()) == 0:
   492         if len(self.GetProjectConfigNames()) == 0:
   478             self.AddProjectDefaultConfiguration()
   493             self.AddProjectDefaultConfiguration()
   479         # Change XSD into class members
   494         # Change XSD into class members
   526         self.ResetAppFrame(None)
   541         self.ResetAppFrame(None)
   527 
   542 
   528     def CheckNewProjectPath(self, old_project_path, new_project_path):
   543     def CheckNewProjectPath(self, old_project_path, new_project_path):
   529         if old_project_path == new_project_path:
   544         if old_project_path == new_project_path:
   530             message = (_("Save path is the same as path of a project! \n"))
   545             message = (_("Save path is the same as path of a project! \n"))
   531             dialog = wx.MessageDialog(self.AppFrame, message, _("Error"), wx.OK | wx.ICON_ERROR)
   546             dialog = wx.MessageDialog(
       
   547                 self.AppFrame, message, _("Error"), wx.OK | wx.ICON_ERROR)
   532             dialog.ShowModal()
   548             dialog.ShowModal()
   533             return False
   549             return False
   534         else:
   550         else:
   535             plc_file = os.path.join(new_project_path, "plc.xml")
   551             plc_file = os.path.join(new_project_path, "plc.xml")
   536             if os.path.isfile(plc_file):
   552             if os.path.isfile(plc_file):
   537                 message = (_("Selected directory already contains another project. Overwrite? \n"))
   553                 message = (
   538                 dialog = wx.MessageDialog(self.AppFrame, message, _("Error"), wx.YES_NO | wx.ICON_ERROR)
   554                     _("Selected directory already contains another project. Overwrite? \n"))
       
   555                 dialog = wx.MessageDialog(
       
   556                     self.AppFrame, message, _("Error"), wx.YES_NO | wx.ICON_ERROR)
   539                 answer = dialog.ShowModal()
   557                 answer = dialog.ShowModal()
   540                 return answer == wx.ID_YES
   558                 return answer == wx.ID_YES
   541         return True
   559         return True
   542 
   560 
   543     def SaveProject(self, from_project_path=None):
   561     def SaveProject(self, from_project_path=None):
   544         if self.CheckProjectPathPerm(False):
   562         if self.CheckProjectPathPerm(False):
   545             if from_project_path is not None:
   563             if from_project_path is not None:
   546                 old_projectfiles_path = self._getProjectFilesPath(from_project_path)
   564                 old_projectfiles_path = self._getProjectFilesPath(
       
   565                     from_project_path)
   547                 if os.path.isdir(old_projectfiles_path):
   566                 if os.path.isdir(old_projectfiles_path):
   548                     shutil.copytree(old_projectfiles_path,
   567                     shutil.copytree(old_projectfiles_path,
   549                                     self._getProjectFilesPath(self.ProjectPath))
   568                                     self._getProjectFilesPath(self.ProjectPath))
   550             self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml'))
   569             self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml'))
   551             result = self.CTNRequestSave(from_project_path)
   570             result = self.CTNRequestSave(from_project_path)
   556         # Ask user to choose a path with write permissions
   575         # Ask user to choose a path with write permissions
   557         if wx.Platform == '__WXMSW__':
   576         if wx.Platform == '__WXMSW__':
   558             path = os.getenv("USERPROFILE")
   577             path = os.getenv("USERPROFILE")
   559         else:
   578         else:
   560             path = os.getenv("HOME")
   579             path = os.getenv("HOME")
   561         dirdialog = wx.DirDialog(self.AppFrame, _("Choose a directory to save project"), path, wx.DD_NEW_DIR_BUTTON)
   580         dirdialog = wx.DirDialog(
       
   581             self.AppFrame, _("Choose a directory to save project"), path, wx.DD_NEW_DIR_BUTTON)
   562         answer = dirdialog.ShowModal()
   582         answer = dirdialog.ShowModal()
   563         dirdialog.Destroy()
   583         dirdialog.Destroy()
   564         if answer == wx.ID_OK:
   584         if answer == wx.ID_OK:
   565             newprojectpath = dirdialog.GetPath()
   585             newprojectpath = dirdialog.GetPath()
   566             if os.path.isdir(newprojectpath):
   586             if os.path.isdir(newprojectpath):
   580 
   600 
   581     def GetLibrariesCCode(self, buildpath):
   601     def GetLibrariesCCode(self, buildpath):
   582         if len(self.Libraries) == 0:
   602         if len(self.Libraries) == 0:
   583             return [], [], ()
   603             return [], [], ()
   584         self.GetIECProgramsAndVariables()
   604         self.GetIECProgramsAndVariables()
   585         LibIECCflags = '"-I%s" -Wno-unused-function' % os.path.abspath(self.GetIECLibPath())
   605         LibIECCflags = '"-I%s" -Wno-unused-function' % os.path.abspath(
       
   606             self.GetIECLibPath())
   586         LocatedCCodeAndFlags = []
   607         LocatedCCodeAndFlags = []
   587         Extras = []
   608         Extras = []
   588         for lib in self.Libraries:
   609         for lib in self.Libraries:
   589             res = lib.Generate_C(buildpath, self._VariablesList, LibIECCflags)
   610             res = lib.Generate_C(buildpath, self._VariablesList, LibIECCflags)
   590             LocatedCCodeAndFlags.append(res[:2])
   611             LocatedCCodeAndFlags.append(res[:2])
   591             if len(res) > 2:
   612             if len(res) > 2:
   592                 Extras.extend(res[2:])
   613                 Extras.extend(res[2:])
   593         return map(list, zip(*LocatedCCodeAndFlags))+[tuple(Extras)]
   614         return map(list, zip(*LocatedCCodeAndFlags)) + [tuple(Extras)]
   594 
   615 
   595     # Update PLCOpenEditor ConfNode Block types from loaded confnodes
   616     # Update PLCOpenEditor ConfNode Block types from loaded confnodes
   596     def RefreshConfNodesBlockLists(self):
   617     def RefreshConfNodesBlockLists(self):
   597         if getattr(self, "Children", None) is not None:
   618         if getattr(self, "Children", None) is not None:
   598             self.ClearConfNodeTypes()
   619             self.ClearConfNodeTypes()
   654         # Create a build path in project folder if user has permissions
   675         # Create a build path in project folder if user has permissions
   655         if CheckPathPerm(self.ProjectPath):
   676         if CheckPathPerm(self.ProjectPath):
   656             self.DefaultBuildPath = os.path.join(self.ProjectPath, "build")
   677             self.DefaultBuildPath = os.path.join(self.ProjectPath, "build")
   657         # Create a build path in temp folder
   678         # Create a build path in temp folder
   658         else:
   679         else:
   659             self.DefaultBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build")
   680             self.DefaultBuildPath = os.path.join(
       
   681                 tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build")
   660 
   682 
   661         if not os.path.exists(self.DefaultBuildPath):
   683         if not os.path.exists(self.DefaultBuildPath):
   662             os.makedirs(self.DefaultBuildPath)
   684             os.makedirs(self.DefaultBuildPath)
   663         return self.DefaultBuildPath
   685         return self.DefaultBuildPath
   664 
   686 
   679 
   701 
   680     def GetLocations(self):
   702     def GetLocations(self):
   681         locations = []
   703         locations = []
   682         filepath = os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h")
   704         filepath = os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h")
   683         if os.path.isfile(filepath):
   705         if os.path.isfile(filepath):
   684             # IEC2C compiler generate a list of located variables : LOCATED_VARIABLES.h
   706             # IEC2C compiler generate a list of located variables :
   685             location_file = open(os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h"))
   707             # LOCATED_VARIABLES.h
       
   708             location_file = open(
       
   709                 os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h"))
   686             # each line of LOCATED_VARIABLES.h declares a located variable
   710             # each line of LOCATED_VARIABLES.h declares a located variable
   687             lines = [line.strip() for line in location_file.readlines()]
   711             lines = [line.strip() for line in location_file.readlines()]
   688             # This regular expression parses the lines genereated by IEC2C
   712             # This regular expression parses the lines genereated by IEC2C
   689             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]*)\)")
   713             LOCATED_MODEL = re.compile(
       
   714                 "__LOCATED_VAR\((?P<IEC_TYPE>[A-Z]*),(?P<NAME>[_A-Za-z0-9]*),(?P<DIR>[QMI])(?:,(?P<SIZE>[XBWDL]))?,(?P<LOC>[,0-9]*)\)")
   690             for line in lines:
   715             for line in lines:
   691                 # If line match RE,
   716                 # If line match RE,
   692                 result = LOCATED_MODEL.match(line)
   717                 result = LOCATED_MODEL.match(line)
   693                 if result:
   718                 if result:
   694                     # Get the resulting dict
   719                     # Get the resulting dict
   695                     resdict = result.groupdict()
   720                     resdict = result.groupdict()
   696                     # rewrite string for variadic location as a tuple of integers
   721                     # rewrite string for variadic location as a tuple of
       
   722                     # integers
   697                     resdict['LOC'] = tuple(map(int, resdict['LOC'].split(',')))
   723                     resdict['LOC'] = tuple(map(int, resdict['LOC'].split(',')))
   698                     # set located size to 'X' if not given
   724                     # set located size to 'X' if not given
   699                     if not resdict['SIZE']:
   725                     if not resdict['SIZE']:
   700                         resdict['SIZE'] = 'X'
   726                         resdict['SIZE'] = 'X'
   701                     # finally store into located variable list
   727                     # finally store into located variable list
   717         """
   743         """
   718 
   744 
   719         # Update PLCOpenEditor ConfNode Block types before generate ST code
   745         # Update PLCOpenEditor ConfNode Block types before generate ST code
   720         self.RefreshConfNodesBlockLists()
   746         self.RefreshConfNodesBlockLists()
   721 
   747 
   722         self.logger.write(_("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n"))
   748         self.logger.write(
       
   749             _("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n"))
   723         # ask PLCOpenEditor controller to write ST/IL/SFC code file
   750         # ask PLCOpenEditor controller to write ST/IL/SFC code file
   724         _program, errors, warnings = self.GenerateProgram(self._getIECgeneratedcodepath())
   751         _program, errors, warnings = self.GenerateProgram(
       
   752             self._getIECgeneratedcodepath())
   725         if len(warnings) > 0:
   753         if len(warnings) > 0:
   726             self.logger.write_warning(_("Warnings in ST/IL/SFC code generator :\n"))
   754             self.logger.write_warning(
       
   755                 _("Warnings in ST/IL/SFC code generator :\n"))
   727             for warning in warnings:
   756             for warning in warnings:
   728                 self.logger.write_warning("%s\n" % warning)
   757                 self.logger.write_warning("%s\n" % warning)
   729         if len(errors) > 0:
   758         if len(errors) > 0:
   730             # Failed !
   759             # Failed !
   731             self.logger.write_error(_("Error in ST/IL/SFC code generator :\n%s\n") % errors[0])
   760             self.logger.write_error(
       
   761                 _("Error in ST/IL/SFC code generator :\n%s\n") % errors[0])
   732             return False
   762             return False
   733         plc_file = open(self._getIECcodepath(), "w")
   763         plc_file = open(self._getIECcodepath(), "w")
   734         # Add ST Library from confnodes
   764         # Add ST Library from confnodes
   735         plc_file.write(self.GetLibrariesSTCode())
   765         plc_file.write(self.GetLibrariesSTCode())
   736         if os.path.isfile(self._getIECrawcodepath()):
   766         if os.path.isfile(self._getIECrawcodepath()):
   761             iec2c_libpath,
   791             iec2c_libpath,
   762             buildpath,
   792             buildpath,
   763             self._getIECcodepath())
   793             self._getIECcodepath())
   764 
   794 
   765         try:
   795         try:
   766             # Invoke compiler. Output files are listed to stdout, errors to stderr
   796             # Invoke compiler.
       
   797             # Output files are listed to stdout, errors to stderr
   767             status, result, err_result = ProcessLogger(self.logger, buildcmd,
   798             status, result, err_result = ProcessLogger(self.logger, buildcmd,
   768                                                        no_stdout=True, no_stderr=True).spin()
   799                                                        no_stdout=True,
       
   800                                                        no_stderr=True).spin()
   769         except Exception, e:
   801         except Exception, e:
   770             self.logger.write_error(buildcmd + "\n")
   802             self.logger.write_error(buildcmd + "\n")
   771             self.logger.write_error(repr(e) + "\n")
   803             self.logger.write_error(repr(e) + "\n")
   772             return False
   804             return False
   773 
   805 
   792                         if line[0] not in '\t \r\n':
   824                         if line[0] not in '\t \r\n':
   793                             last_section = line
   825                             last_section = line
   794 
   826 
   795                         if first_line <= i <= last_line:
   827                         if first_line <= i <= last_line:
   796                             if last_section is not None:
   828                             if last_section is not None:
   797                                 self.logger.write_warning("In section: " + last_section)
   829                                 self.logger.write_warning(
       
   830                                     "In section: " + last_section)
   798                                 last_section = None  # only write section once
   831                                 last_section = None  # only write section once
   799                             self.logger.write_warning("%04d: %s" % (i, line))
   832                             self.logger.write_warning("%04d: %s" % (i, line))
   800 
   833 
   801                     f.close()
   834                     f.close()
   802 
   835 
   803             self.logger.write_error(_("Error : IEC to C compiler returned %d\n") % status)
   836             self.logger.write_error(
       
   837                 _("Error : IEC to C compiler returned %d\n") % status)
   804             return False
   838             return False
   805 
   839 
   806         # Now extract C files of stdout
   840         # Now extract C files of stdout
   807         C_files = [fname for fname in result.splitlines() if fname[-2:] == ".c" or fname[-2:] == ".C"]
   841         C_files = [fname for fname in result.splitlines() if fname[
       
   842             -2:] == ".c" or fname[-2:] == ".C"]
   808         # remove those that are not to be compiled because included by others
   843         # remove those that are not to be compiled because included by others
   809         C_files.remove("POUS.c")
   844         C_files.remove("POUS.c")
   810         if not C_files:
   845         if not C_files:
   811             self.logger.write_error(_("Error : At least one configuration and one resource must be declared in PLC !\n"))
   846             self.logger.write_error(
       
   847                 _("Error : At least one configuration and one resource must be declared in PLC !\n"))
   812             return False
   848             return False
   813         # transform those base names to full names with path
   849         # transform those base names to full names with path
   814         C_files = map(lambda filename: os.path.join(buildpath, filename), C_files)
   850         C_files = map(
       
   851             lambda filename: os.path.join(buildpath, filename), C_files)
   815 
   852 
   816         # prepend beremiz include to configuration header
   853         # prepend beremiz include to configuration header
   817         H_files = [fname for fname in result.splitlines() if fname[-2:] == ".h" or fname[-2:] == ".H"]
   854         H_files = [fname for fname in result.splitlines() if fname[
       
   855             -2:] == ".h" or fname[-2:] == ".H"]
   818         H_files.remove("LOCATED_VARIABLES.h")
   856         H_files.remove("LOCATED_VARIABLES.h")
   819         H_files = map(lambda filename: os.path.join(buildpath, filename), H_files)
   857         H_files = map(
       
   858             lambda filename: os.path.join(buildpath, filename), H_files)
   820         for H_file in H_files:
   859         for H_file in H_files:
   821             with file(H_file, 'r') as original:
   860             with file(H_file, 'r') as original:
   822                 data = original.read()
   861                 data = original.read()
   823             with file(H_file, 'w') as modified:
   862             with file(H_file, 'w') as modified:
   824                 modified.write('#include "beremiz.h"\n' + data)
   863                 modified.write('#include "beremiz.h"\n' + data)
   825 
   864 
   826         self.logger.write(_("Extracting Located Variables...\n"))
   865         self.logger.write(_("Extracting Located Variables...\n"))
   827         # Keep track of generated located variables for later use by self._Generate_C
   866         # Keep track of generated located variables for later use by
       
   867         # self._Generate_C
   828         self.PLCGeneratedLocatedVars = self.GetLocations()
   868         self.PLCGeneratedLocatedVars = self.GetLocations()
   829         # Keep track of generated C files for later use by self.CTNGenerate_C
   869         # Keep track of generated C files for later use by self.CTNGenerate_C
   830         self.PLCGeneratedCFiles = C_files
   870         self.PLCGeneratedCFiles = C_files
   831         # compute CFLAGS for plc
   871         # compute CFLAGS for plc
   832         self.plcCFLAGS = '"-I%s" -Wno-unused-function' % self.iec2c_cfg.getLibCPath()
   872         self.plcCFLAGS = '"-I%s" -Wno-unused-function' % self.iec2c_cfg.getLibCPath()
   857         if builder is not None:
   897         if builder is not None:
   858             return builder.GetBinaryCodeMD5()
   898             return builder.GetBinaryCodeMD5()
   859         else:
   899         else:
   860             return None
   900             return None
   861 
   901 
   862     #######################################################################
   902     #
   863     #
   903     #
   864     #                C CODE GENERATION METHODS
   904     #                C CODE GENERATION METHODS
   865     #
   905     #
   866     #######################################################################
   906     #
   867 
   907 
   868     def CTNGenerate_C(self, buildpath, locations):
   908     def CTNGenerate_C(self, buildpath, locations):
   869         """
   909         """
   870         Return C code generated by iec2c compiler
   910         Return C code generated by iec2c compiler
   871         when _generate_softPLC have been called
   911         when _generate_softPLC have been called
   900         if self._ProgramList is None or self._VariablesList is None:
   940         if self._ProgramList is None or self._VariablesList is None:
   901             try:
   941             try:
   902                 csvfile = os.path.join(self._getBuildPath(), "VARIABLES.csv")
   942                 csvfile = os.path.join(self._getBuildPath(), "VARIABLES.csv")
   903                 # describes CSV columns
   943                 # describes CSV columns
   904                 ProgramsListAttributeName = ["num", "C_path", "type"]
   944                 ProgramsListAttributeName = ["num", "C_path", "type"]
   905                 VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"]
   945                 VariablesListAttributeName = [
       
   946                     "num", "vartype", "IEC_path", "C_path", "type"]
   906                 self._ProgramList = []
   947                 self._ProgramList = []
   907                 self._VariablesList = []
   948                 self._VariablesList = []
   908                 self._DbgVariablesList = []
   949                 self._DbgVariablesList = []
   909                 self._IECPathToIdx = {}
   950                 self._IECPathToIdx = {}
   910 
   951 
   920                         ListGroup[-1].append(strippedline)
   961                         ListGroup[-1].append(strippedline)
   921 
   962 
   922                 # first section contains programs
   963                 # first section contains programs
   923                 for line in ListGroup[0]:
   964                 for line in ListGroup[0]:
   924                     # Split and Maps each field to dictionnary entries
   965                     # Split and Maps each field to dictionnary entries
   925                     attrs = dict(zip(ProgramsListAttributeName, line.strip().split(';')))
   966                     attrs = dict(
       
   967                         zip(ProgramsListAttributeName, line.strip().split(';')))
   926                     # Truncate "C_path" to remove conf an resources names
   968                     # Truncate "C_path" to remove conf an resources names
   927                     attrs["C_path"] = '__'.join(attrs["C_path"].split(".", 2)[1:])
   969                     attrs["C_path"] = '__'.join(
       
   970                         attrs["C_path"].split(".", 2)[1:])
   928                     # Push this dictionnary into result.
   971                     # Push this dictionnary into result.
   929                     self._ProgramList.append(attrs)
   972                     self._ProgramList.append(attrs)
   930 
   973 
   931                 # second section contains all variables
   974                 # second section contains all variables
   932                 config_FBs = {}
   975                 config_FBs = {}
   933                 Idx = 0
   976                 Idx = 0
   934                 for line in ListGroup[1]:
   977                 for line in ListGroup[1]:
   935                     # Split and Maps each field to dictionnary entries
   978                     # Split and Maps each field to dictionnary entries
   936                     attrs = dict(zip(VariablesListAttributeName, line.strip().split(';')))
   979                     attrs = dict(
       
   980                         zip(VariablesListAttributeName, line.strip().split(';')))
   937                     # Truncate "C_path" to remove conf an resources names
   981                     # Truncate "C_path" to remove conf an resources names
   938                     parts = attrs["C_path"].split(".", 2)
   982                     parts = attrs["C_path"].split(".", 2)
   939                     if len(parts) > 2:
   983                     if len(parts) > 2:
   940                         config_FB = config_FBs.get(tuple(parts[:2]))
   984                         config_FB = config_FBs.get(tuple(parts[:2]))
   941                         if config_FB:
   985                         if config_FB:
   962                 # third section contains ticktime
  1006                 # third section contains ticktime
   963                 if len(ListGroup) > 2:
  1007                 if len(ListGroup) > 2:
   964                     self._Ticktime = int(ListGroup[2][0])
  1008                     self._Ticktime = int(ListGroup[2][0])
   965 
  1009 
   966             except Exception:
  1010             except Exception:
   967                 self.logger.write_error(_("Cannot open/parse VARIABLES.csv!\n"))
  1011                 self.logger.write_error(
       
  1012                     _("Cannot open/parse VARIABLES.csv!\n"))
   968                 self.logger.write_error(traceback.format_exc())
  1013                 self.logger.write_error(traceback.format_exc())
   969                 self.ResetIECProgramsAndVariables()
  1014                 self.ResetIECProgramsAndVariables()
   970                 return False
  1015                 return False
   971 
  1016 
   972         return True
  1017         return True
  1032                     "void __retrieve_%(s)s(void);\n" +
  1077                     "void __retrieve_%(s)s(void);\n" +
  1033                     "void __publish_%(s)s(void);") % {'s': locstr} for locstr in locstrs]),
  1078                     "void __publish_%(s)s(void);") % {'s': locstr} for locstr in locstrs]),
  1034                 "retrieve_calls": "\n    ".join([
  1079                 "retrieve_calls": "\n    ".join([
  1035                     "__retrieve_%s();" % locstr for locstr in locstrs]),
  1080                     "__retrieve_%s();" % locstr for locstr in locstrs]),
  1036                 "publish_calls": "\n    ".join([  # Call publish in reverse order
  1081                 "publish_calls": "\n    ".join([  # Call publish in reverse order
  1037                     "__publish_%s();" % locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]),
  1082                     "__publish_%s();" % locstrs[i - 1] for i in xrange(len(locstrs), 0, -1)]),
  1038                 "init_calls": "\n    ".join([
  1083                 "init_calls": "\n    ".join([
  1039                     "init_level=%d; " % (i+1) +
  1084                     "init_level=%d; " % (i + 1) +
  1040                     "if((res = __init_%s(argc,argv))){" % locstr +
  1085                     "if((res = __init_%s(argc,argv))){" % locstr +
  1041                     # "printf(\"%s\"); "%locstr + #for debug
  1086                     # "printf(\"%s\"); "%locstr + #for debug
  1042                     "return res;}" for i, locstr in enumerate(locstrs)]),
  1087                     "return res;}" for i, locstr in enumerate(locstrs)]),
  1043                 "cleanup_calls": "\n    ".join([
  1088                 "cleanup_calls": "\n    ".join([
  1044                     "if(init_level >= %d) " % i +
  1089                     "if(init_level >= %d) " % i +
  1045                     "__cleanup_%s();" % locstrs[i-1] for i in xrange(len(locstrs), 0, -1)])
  1090                     "__cleanup_%s();" % locstrs[i - 1] for i in xrange(len(locstrs), 0, -1)])
  1046                 }
  1091             }
  1047         else:
  1092         else:
  1048             plc_main_code = targets.GetCode("plc_main_head.c") % {
  1093             plc_main_code = targets.GetCode("plc_main_head.c") % {
  1049                 "calls_prototypes": "\n",
  1094                 "calls_prototypes": "\n",
  1050                 "retrieve_calls":   "\n",
  1095                 "retrieve_calls":   "\n",
  1051                 "publish_calls":    "\n",
  1096                 "publish_calls":    "\n",
  1052                 "init_calls":       "\n",
  1097                 "init_calls":       "\n",
  1053                 "cleanup_calls":    "\n"
  1098                 "cleanup_calls":    "\n"
  1054             }
  1099             }
  1055         plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent().getLocalTag())
  1100         plc_main_code += targets.GetTargetCode(
       
  1101             self.GetTarget().getcontent().getLocalTag())
  1056         plc_main_code += targets.GetCode("plc_main_tail.c")
  1102         plc_main_code += targets.GetCode("plc_main_tail.c")
  1057         return plc_main_code
  1103         return plc_main_code
  1058 
  1104 
  1059     def _Build(self):
  1105     def _Build(self):
  1060         """
  1106         """
  1122         try:
  1168         try:
  1123             CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C(
  1169             CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C(
  1124                 buildpath,
  1170                 buildpath,
  1125                 self.PLCGeneratedLocatedVars)
  1171                 self.PLCGeneratedLocatedVars)
  1126         except Exception:
  1172         except Exception:
  1127             self.logger.write_error(_("Runtime IO extensions C code generation failed !\n"))
  1173             self.logger.write_error(
       
  1174                 _("Runtime IO extensions C code generation failed !\n"))
  1128             self.logger.write_error(traceback.format_exc())
  1175             self.logger.write_error(traceback.format_exc())
  1129             self.ResetBuildMD5()
  1176             self.ResetBuildMD5()
  1130             return False
  1177             return False
  1131 
  1178 
  1132         # Generate C code and compilation params from liraries
  1179         # Generate C code and compilation params from liraries
  1133         try:
  1180         try:
  1134             LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(buildpath)
  1181             LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(
       
  1182                 buildpath)
  1135         except Exception:
  1183         except Exception:
  1136             self.logger.write_error(_("Runtime library extensions C code generation failed !\n"))
  1184             self.logger.write_error(
       
  1185                 _("Runtime library extensions C code generation failed !\n"))
  1137             self.logger.write_error(traceback.format_exc())
  1186             self.logger.write_error(traceback.format_exc())
  1138             self.ResetBuildMD5()
  1187             self.ResetBuildMD5()
  1139             return False
  1188             return False
  1140 
  1189 
  1141         self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + CTNLocationCFilesAndCFLAGS
  1190         self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + \
       
  1191             CTNLocationCFilesAndCFLAGS
  1142         self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS
  1192         self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS
  1143         ExtraFiles = CTNExtraFiles + LibExtraFiles
  1193         ExtraFiles = CTNExtraFiles + LibExtraFiles
  1144 
  1194 
  1145         # Get temporary directory path
  1195         # Get temporary directory path
  1146         extrafilespath = self._getExtraFilesPath()
  1196         extrafilespath = self._getExtraFilesPath()
  1155             open(fpath, "wb").write(fobject.read())
  1205             open(fpath, "wb").write(fobject.read())
  1156         # Now we can forget ExtraFiles (will close files object)
  1206         # Now we can forget ExtraFiles (will close files object)
  1157         del ExtraFiles
  1207         del ExtraFiles
  1158 
  1208 
  1159         # Header file for extensions
  1209         # Header file for extensions
  1160         open(os.path.join(buildpath, "beremiz.h"), "w").write(targets.GetHeader())
  1210         open(os.path.join(buildpath, "beremiz.h"), "w").write(
       
  1211             targets.GetHeader())
  1161 
  1212 
  1162         # Template based part of C code generation
  1213         # Template based part of C code generation
  1163         # files are stacked at the beginning, as files of confnode tree root
  1214         # files are stacked at the beginning, as files of confnode tree root
  1164         c_source = [
  1215         c_source = [
  1165             #  debugger code
  1216             #  debugger code
  1174                 code = generator()
  1225                 code = generator()
  1175                 if code is None:
  1226                 if code is None:
  1176                     raise Exception
  1227                     raise Exception
  1177                 code_path = os.path.join(buildpath, filename)
  1228                 code_path = os.path.join(buildpath, filename)
  1178                 open(code_path, "w").write(code)
  1229                 open(code_path, "w").write(code)
  1179                 # Insert this file as first file to be compiled at root confnode
  1230                 # Insert this file as first file to be compiled at root
  1180                 self.LocationCFilesAndCFLAGS[0][1].insert(0, (code_path, self.plcCFLAGS))
  1231                 # confnode
       
  1232                 self.LocationCFilesAndCFLAGS[0][1].insert(
       
  1233                     0, (code_path, self.plcCFLAGS))
  1181             except Exception:
  1234             except Exception:
  1182                 self.logger.write_error(name+_(" generation failed !\n"))
  1235                 self.logger.write_error(name + _(" generation failed !\n"))
  1183                 self.logger.write_error(traceback.format_exc())
  1236                 self.logger.write_error(traceback.format_exc())
  1184                 self.ResetBuildMD5()
  1237                 self.ResetBuildMD5()
  1185                 return False
  1238                 return False
  1186         self.logger.write(_("C code generated successfully.\n"))
  1239         self.logger.write(_("C code generated successfully.\n"))
  1187         return True
  1240         return True
  1188 
  1241 
  1189     def ShowError(self, logger, from_location, to_location):
  1242     def ShowError(self, logger, from_location, to_location):
  1190         chunk_infos = self.GetChunkInfos(from_location, to_location)
  1243         chunk_infos = self.GetChunkInfos(from_location, to_location)
  1191         for infos, (start_row, start_col) in chunk_infos:
  1244         for infos, (start_row, start_col) in chunk_infos:
  1192             row = 1 if from_location[0] < start_row else (from_location[0] - start_row)
  1245             row = 1 if from_location[0] < start_row else (
  1193             col = 1 if (start_row != from_location[0]) else (from_location[1] - start_col)
  1246                 from_location[0] - start_row)
       
  1247             col = 1 if (start_row != from_location[0]) else (
       
  1248                 from_location[1] - start_col)
  1194             start = (row, col)
  1249             start = (row, col)
  1195 
  1250 
  1196             row = 1 if to_location[0] < start_row else (to_location[0] - start_row)
  1251             row = 1 if to_location[0] < start_row else (
  1197             col = 1 if (start_row != to_location[0]) else (to_location[1] - start_col)
  1252                 to_location[0] - start_row)
       
  1253             col = 1 if (start_row != to_location[0]) else (
       
  1254                 to_location[1] - start_col)
  1198             end = (row, col)
  1255             end = (row, col)
  1199 
  1256 
  1200             if self.AppFrame is not None:
  1257             if self.AppFrame is not None:
  1201                 self.AppFrame.ShowError(infos, start, end)
  1258                 self.AppFrame.ShowError(infos, start, end)
  1202 
  1259 
  1223     def _OpenView(self, name=None, onlyopened=False):
  1280     def _OpenView(self, name=None, onlyopened=False):
  1224         if name == "IEC code":
  1281         if name == "IEC code":
  1225             if self._IECCodeView is None:
  1282             if self._IECCodeView is None:
  1226                 plc_file = self._getIECcodepath()
  1283                 plc_file = self._getIECcodepath()
  1227 
  1284 
  1228                 self._IECCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name)
  1285                 self._IECCodeView = IECCodeViewer(
       
  1286                     self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name)
  1229                 self._IECCodeView.SetTextSyntax("ALL")
  1287                 self._IECCodeView.SetTextSyntax("ALL")
  1230                 self._IECCodeView.SetKeywords(IEC_KEYWORDS)
  1288                 self._IECCodeView.SetKeywords(IEC_KEYWORDS)
  1231                 try:
  1289                 try:
  1232                     text = file(plc_file).read()
  1290                     text = file(plc_file).read()
  1233                 except Exception:
  1291                 except Exception:
  1244 
  1302 
  1245         elif name == "IEC raw code":
  1303         elif name == "IEC raw code":
  1246             if self._IECRawCodeView is None:
  1304             if self._IECRawCodeView is None:
  1247                 controler = MiniTextControler(self._getIECrawcodepath(), self)
  1305                 controler = MiniTextControler(self._getIECrawcodepath(), self)
  1248 
  1306 
  1249                 self._IECRawCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name)
  1307                 self._IECRawCodeView = IECCodeViewer(
       
  1308                     self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name)
  1250                 self._IECRawCodeView.SetTextSyntax("ALL")
  1309                 self._IECRawCodeView.SetTextSyntax("ALL")
  1251                 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS)
  1310                 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS)
  1252                 self._IECRawCodeView.RefreshView()
  1311                 self._IECRawCodeView.RefreshView()
  1253                 self._IECRawCodeView.SetIcon(GetBitmap("ST"))
  1312                 self._IECRawCodeView.SetIcon(GetBitmap("ST"))
  1254                 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor)
  1313                 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor)
  1258 
  1317 
  1259             return self._IECRawCodeView
  1318             return self._IECRawCodeView
  1260 
  1319 
  1261         elif name == "Project Files":
  1320         elif name == "Project Files":
  1262             if self._ProjectFilesView is None:
  1321             if self._ProjectFilesView is None:
  1263                 self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True)
  1322                 self._ProjectFilesView = FileManagementPanel(
       
  1323                     self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True)
  1264 
  1324 
  1265                 extensions = []
  1325                 extensions = []
  1266                 for extension, name, editor in features.file_editors:
  1326                 for extension, name, editor in features.file_editors:
  1267                     if extension not in extensions:
  1327                     if extension not in extensions:
  1268                         extensions.append(extension)
  1328                         extensions.append(extension)
  1300 
  1360 
  1301                     if editor_name != "":
  1361                     if editor_name != "":
  1302                         name = "::".join([filepath, editor_name])
  1362                         name = "::".join([filepath, editor_name])
  1303 
  1363 
  1304                         editor = editors[editor_name]()
  1364                         editor = editors[editor_name]()
  1305                         self._FileEditors[filepath] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame)
  1365                         self._FileEditors[filepath] = editor(
       
  1366                             self.AppFrame.TabsOpened, self, name, self.AppFrame)
  1306                         self._FileEditors[filepath].SetIcon(GetBitmap("FILE"))
  1367                         self._FileEditors[filepath].SetIcon(GetBitmap("FILE"))
  1307                         if isinstance(self._FileEditors[filepath], DebugViewer):
  1368                         if isinstance(self._FileEditors[filepath], DebugViewer):
  1308                             self._FileEditors[filepath].SetDataProducer(self)
  1369                             self._FileEditors[filepath].SetDataProducer(self)
  1309 
  1370 
  1310             if filepath in self._FileEditors:
  1371             if filepath in self._FileEditors:
  1349 
  1410 
  1350     def UpdatePLCLog(self, log_count):
  1411     def UpdatePLCLog(self, log_count):
  1351         if log_count:
  1412         if log_count:
  1352             if self.AppFrame is not None:
  1413             if self.AppFrame is not None:
  1353                 self.AppFrame.LogViewer.SetLogCounters(log_count)
  1414                 self.AppFrame.LogViewer.SetLogCounters(log_count)
       
  1415 
       
  1416     DefaultMethods = {
       
  1417         "_Run": False,
       
  1418         "_Stop": False,
       
  1419         "_Transfer": False,
       
  1420         "_Connect": True,
       
  1421         "_Disconnect": False
       
  1422     }
       
  1423 
       
  1424     MethodsFromStatus = {
       
  1425         "Started":      {"_Stop": True,
       
  1426                          "_Transfer": True,
       
  1427                          "_Connect": False,
       
  1428                          "_Disconnect": True},
       
  1429         "Stopped":      {"_Run": True,
       
  1430                          "_Transfer": True,
       
  1431                          "_Connect": False,
       
  1432                          "_Disconnect": True},
       
  1433         "Empty":        {"_Transfer": True,
       
  1434                          "_Connect": False,
       
  1435                          "_Disconnect": True},
       
  1436         "Broken":       {"_Connect": False,
       
  1437                          "_Disconnect": True},
       
  1438         "Disconnected": {},
       
  1439     }
  1354 
  1440 
  1355     def UpdateMethodsFromPLCStatus(self):
  1441     def UpdateMethodsFromPLCStatus(self):
  1356         updated = False
  1442         updated = False
  1357         status = None
  1443         status = None
  1358         if self._connector is not None:
  1444         if self._connector is not None:
  1362                 self.UpdatePLCLog(log_count)
  1448                 self.UpdatePLCLog(log_count)
  1363         if status is None:
  1449         if status is None:
  1364             self._SetConnector(None, False)
  1450             self._SetConnector(None, False)
  1365             status = "Disconnected"
  1451             status = "Disconnected"
  1366         if self.previous_plcstate != status:
  1452         if self.previous_plcstate != status:
  1367             for args in {
  1453             allmethods = self.DefaultMethods.copy()
  1368                     "Started":      [("_Run", False),
  1454             allmethods.update(
  1369                                      ("_Stop", True)],
  1455                 self.MethodsFromStatus.get(status, {}))
  1370                     "Stopped":      [("_Run", True),
  1456             for method, active in allmethods.items():
  1371                                      ("_Stop", False)],
  1457                 self.ShowMethod(method, active)
  1372                     "Empty":        [("_Run", False),
       
  1373                                      ("_Stop", False)],
       
  1374                     "Broken":       [],
       
  1375                     "Disconnected": [("_Run", False),
       
  1376                                      ("_Stop", False),
       
  1377                                      ("_Transfer", False),
       
  1378                                      ("_Connect", True),
       
  1379                                      ("_Disconnect", False)],
       
  1380             }.get(status, []):
       
  1381                 self.ShowMethod(*args)
       
  1382             self.previous_plcstate = status
  1458             self.previous_plcstate = status
  1383             if self.AppFrame is not None:
  1459             if self.AppFrame is not None:
  1384                 updated = True
  1460                 updated = True
  1385                 self.AppFrame.RefreshStatusToolBar()
  1461                 self.AppFrame.RefreshStatusToolBar()
  1386                 if status == "Disconnected":
  1462                 if status == "Disconnected":
  1387                     self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 1)
  1463                     self.AppFrame.ConnectionStatusBar.SetStatusText(
       
  1464                         self.GetTextStatus(status), 1)
  1388                     self.AppFrame.ConnectionStatusBar.SetStatusText('', 2)
  1465                     self.AppFrame.ConnectionStatusBar.SetStatusText('', 2)
  1389                 else:
  1466                 else:
  1390                     self.AppFrame.ConnectionStatusBar.SetStatusText(
  1467                     self.AppFrame.ConnectionStatusBar.SetStatusText(
  1391                         _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1)
  1468                         _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1)
  1392                     self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 2)
  1469                     self.AppFrame.ConnectionStatusBar.SetStatusText(
       
  1470                         self.GetTextStatus(status), 2)
  1393         return updated
  1471         return updated
  1394 
  1472 
  1395     def GetTextStatus(self, status):
  1473     def GetTextStatus(self, status):
  1396         msgs = {
  1474         msgs = {
  1397             "Started":      _("Started"),
  1475             "Started":      _("Started"),
  1398             "Stopped":      _("Stopped"),
  1476             "Stopped":      _("Stopped"),
  1399             "Empty":        _("Empty"),
  1477             "Empty":        _("Empty"),
  1400             "Broken":       _("Broken"),
  1478             "Broken":       _("Broken"),
  1401             "Disconnected": _("Disconnected")
  1479             "Disconnected": _("Disconnected")
  1402             }
  1480         }
  1403         return msgs.get(status, status)
  1481         return msgs.get(status, status)
  1404 
  1482 
  1405     def ShowPLCProgress(self, status="", progress=0):
  1483     def ShowPLCProgress(self, status="", progress=0):
  1406         self.AppFrame.ProgressStatusBar.Show()
  1484         self.AppFrame.ProgressStatusBar.Show()
  1407         self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 1)
  1485         self.AppFrame.ConnectionStatusBar.SetStatusText(
       
  1486             self.GetTextStatus(status), 1)
  1408         self.AppFrame.ProgressStatusBar.SetValue(progress)
  1487         self.AppFrame.ProgressStatusBar.SetValue(progress)
  1409 
  1488 
  1410     def HidePLCProgress(self):
  1489     def HidePLCProgress(self):
  1411         # clear previous_plcstate to restore status
  1490         # clear previous_plcstate to restore status
  1412         # in UpdateMethodsFromPLCStatus()
  1491         # in UpdateMethodsFromPLCStatus()
  1418         self.UpdateMethodsFromPLCStatus()
  1497         self.UpdateMethodsFromPLCStatus()
  1419 
  1498 
  1420     def SnapshotAndResetDebugValuesBuffers(self):
  1499     def SnapshotAndResetDebugValuesBuffers(self):
  1421         if self._connector is not None:
  1500         if self._connector is not None:
  1422             plc_status, Traces = self._connector.GetTraceVariables()
  1501             plc_status, Traces = self._connector.GetTraceVariables()
  1423             # print [dict.keys() for IECPath, (dict, log, status, fvalue) in self.IECdebug_datas.items()]
  1502             # print [dict.keys() for IECPath, (dict, log, status, fvalue) in
       
  1503             # self.IECdebug_datas.items()]
  1424             if plc_status == "Started":
  1504             if plc_status == "Started":
  1425                 if len(Traces) > 0:
  1505                 if len(Traces) > 0:
  1426                     for debug_tick, debug_buff in Traces:
  1506                     for debug_tick, debug_buff in Traces:
  1427                         debug_vars = UnpackDebugBuffer(debug_buff, self.TracedIECTypes)
  1507                         debug_vars = UnpackDebugBuffer(
       
  1508                             debug_buff, self.TracedIECTypes)
  1428                         if debug_vars is not None and len(debug_vars) == len(self.TracedIECPath):
  1509                         if debug_vars is not None and len(debug_vars) == len(self.TracedIECPath):
  1429                             for IECPath, values_buffer, value in izip(
  1510                             for IECPath, values_buffer, value in izip(
  1430                                     self.TracedIECPath,
  1511                                     self.TracedIECPath,
  1431                                     self.DebugValuesBuffers,
  1512                                     self.DebugValuesBuffers,
  1432                                     debug_vars):
  1513                                     debug_vars):
  1433                                 IECdebug_data = self.IECdebug_datas.get(IECPath, None)
  1514                                 IECdebug_data = self.IECdebug_datas.get(
       
  1515                                     IECPath, None)
  1434                                 if IECdebug_data is not None and value is not None:
  1516                                 if IECdebug_data is not None and value is not None:
  1435                                     forced = IECdebug_data[2:4] == ["Forced", value]
  1517                                     forced = IECdebug_data[2:4] == [
       
  1518                                         "Forced", value]
  1436                                     if not IECdebug_data[4] and len(values_buffer) > 0:
  1519                                     if not IECdebug_data[4] and len(values_buffer) > 0:
  1437                                         values_buffer[-1] = (value, forced)
  1520                                         values_buffer[-1] = (value, forced)
  1438                                     else:
  1521                                     else:
  1439                                         values_buffer.append((value, forced))
  1522                                         values_buffer.append((value, forced))
  1440                             self.DebugTicks.append(debug_tick)
  1523                             self.DebugTicks.append(debug_tick)
  1459                     # Callable Dict is empty.
  1542                     # Callable Dict is empty.
  1460                     # This variable is not needed anymore!
  1543                     # This variable is not needed anymore!
  1461                     IECPathsToPop.append(IECPath)
  1544                     IECPathsToPop.append(IECPath)
  1462                 elif IECPath != "__tick__":
  1545                 elif IECPath != "__tick__":
  1463                     # Convert
  1546                     # Convert
  1464                     Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None))
  1547                     Idx, IEC_Type = self._IECPathToIdx.get(
       
  1548                         IECPath, (None, None))
  1465                     if Idx is not None:
  1549                     if Idx is not None:
  1466                         if IEC_Type in DebugTypesSize:
  1550                         if IEC_Type in DebugTypesSize:
  1467                             Idxs.append((Idx, IEC_Type, fvalue, IECPath))
  1551                             Idxs.append((Idx, IEC_Type, fvalue, IECPath))
  1468                         else:
  1552                         else:
  1469                             self.logger.write_warning(_("Debug: Unsupported type to debug '%s'\n") % IEC_Type)
  1553                             self.logger.write_warning(
       
  1554                                 _("Debug: Unsupported type to debug '%s'\n") % IEC_Type)
  1470                     else:
  1555                     else:
  1471                         self.logger.write_warning(_("Debug: Unknown variable '%s'\n") % IECPath)
  1556                         self.logger.write_warning(
       
  1557                             _("Debug: Unknown variable '%s'\n") % IECPath)
  1472             for IECPathToPop in IECPathsToPop:
  1558             for IECPathToPop in IECPathsToPop:
  1473                 self.IECdebug_datas.pop(IECPathToPop)
  1559                 self.IECdebug_datas.pop(IECPathToPop)
  1474 
  1560 
  1475             if Idxs:
  1561             if Idxs:
  1476                 Idxs.sort()
  1562                 Idxs.sort()
  1493         # Prevent to call RegisterDebugVarToConnector when PLC is not started
  1579         # Prevent to call RegisterDebugVarToConnector when PLC is not started
  1494         # If an output location var is forced it's leads to segmentation fault in runtime
  1580         # If an output location var is forced it's leads to segmentation fault in runtime
  1495         # Links between PLC located variables and real variables are not ready
  1581         # Links between PLC located variables and real variables are not ready
  1496         if self.IsPLCStarted():
  1582         if self.IsPLCStarted():
  1497             # Timer to prevent rapid-fire when registering many variables
  1583             # Timer to prevent rapid-fire when registering many variables
  1498             # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead
  1584             # use wx.CallAfter use keep using same thread. TODO : use wx.Timer
  1499             self.DebugTimer = Timer(0.5, wx.CallAfter, args=[self.RegisterDebugVarToConnector])
  1585             # instead
       
  1586             self.DebugTimer = Timer(
       
  1587                 0.5, wx.CallAfter, args=[self.RegisterDebugVarToConnector])
  1500             # Rearm anti-rapid-fire timer
  1588             # Rearm anti-rapid-fire timer
  1501             self.DebugTimer.start()
  1589             self.DebugTimer.start()
  1502 
  1590 
  1503     def GetDebugIECVariableType(self, IECPath):
  1591     def GetDebugIECVariableType(self, IECPath):
  1504         _Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None))
  1592         _Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None))
  1598         debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers()
  1686         debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers()
  1599         start_time = time.time()
  1687         start_time = time.time()
  1600         if len(self.TracedIECPath) == len(buffers):
  1688         if len(self.TracedIECPath) == len(buffers):
  1601             for IECPath, values in izip(self.TracedIECPath, buffers):
  1689             for IECPath, values in izip(self.TracedIECPath, buffers):
  1602                 if len(values) > 0:
  1690                 if len(values) > 0:
  1603                     self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values)
  1691                     self.CallWeakcallables(
       
  1692                         IECPath, "NewValues", debug_ticks, values)
  1604             if len(debug_ticks) > 0:
  1693             if len(debug_ticks) > 0:
  1605                 self.CallWeakcallables("__tick__", "NewDataAvailable", debug_ticks)
  1694                 self.CallWeakcallables(
       
  1695                     "__tick__", "NewDataAvailable", debug_ticks)
  1606 
  1696 
  1607         delay = time.time() - start_time
  1697         delay = time.time() - start_time
  1608         next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay)
  1698         next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay)
  1609         if self.DispatchDebugValuesTimer is not None:
  1699         if self.DispatchDebugValuesTimer is not None:
  1610             self.DispatchDebugValuesTimer.Start(
  1700             self.DispatchDebugValuesTimer.Start(
  1665                 wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1755                 wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1666 
  1756 
  1667     def _Connect(self):
  1757     def _Connect(self):
  1668         # don't accept re-connetion if already connected
  1758         # don't accept re-connetion if already connected
  1669         if self._connector is not None:
  1759         if self._connector is not None:
  1670             self.logger.write_error(_("Already connected. Please disconnect\n"))
  1760             self.logger.write_error(
       
  1761                 _("Already connected. Please disconnect\n"))
  1671             return
  1762             return
  1672 
  1763 
  1673         # Get connector uri
  1764         # Get connector uri
  1674         uri = self.BeremizRoot.getURI_location().strip()
  1765         uri = self.BeremizRoot.getURI_location().strip()
  1675 
  1766 
  1703 
  1794 
  1704         # Get connector from uri
  1795         # Get connector from uri
  1705         try:
  1796         try:
  1706             self._SetConnector(connectors.ConnectorFactory(uri, self))
  1797             self._SetConnector(connectors.ConnectorFactory(uri, self))
  1707         except Exception:
  1798         except Exception:
  1708             self.logger.write_error(_("Exception while connecting %s!\n") % uri)
  1799             self.logger.write_error(
       
  1800                 _("Exception while connecting %s!\n") % uri)
  1709             self.logger.write_error(traceback.format_exc())
  1801             self.logger.write_error(traceback.format_exc())
  1710 
  1802 
  1711         # Did connection success ?
  1803         # Did connection success ?
  1712         if self._connector is None:
  1804         if self._connector is None:
  1713             # Oups.
  1805             # Oups.
  1714             self.logger.write_error(_("Connection failed to %s!\n") % uri)
  1806             self.logger.write_error(_("Connection failed to %s!\n") % uri)
  1715         else:
  1807         else:
  1716             self.ShowMethod("_Connect", False)
       
  1717             self.ShowMethod("_Disconnect", True)
       
  1718             self.ShowMethod("_Transfer", True)
       
  1719 
       
  1720             self.CompareLocalAndRemotePLC()
  1808             self.CompareLocalAndRemotePLC()
  1721 
  1809 
  1722             # Init with actual PLC status and print it
  1810             # Init with actual PLC status and print it
  1723             self.UpdateMethodsFromPLCStatus()
  1811             self.UpdateMethodsFromPLCStatus()
  1724             if self.previous_plcstate in ["Started", "Stopped"]:
  1812             if self.previous_plcstate in ["Started", "Stopped"]:
  1725                 if self.DebugAvailable() and self.GetIECProgramsAndVariables():
  1813                 if self.DebugAvailable() and self.GetIECProgramsAndVariables():
  1726                     self.logger.write(_("Debugger ready\n"))
  1814                     self.logger.write(_("Debugger ready\n"))
  1727                     self._connect_debug()
  1815                     self._connect_debug()
  1728                 else:
  1816                 else:
  1729                     self.logger.write_warning(_("Debug does not match PLC - stop/transfert/start to re-enable\n"))
  1817                     self.logger.write_warning(
       
  1818                         _("Debug does not match PLC - stop/transfert/start to re-enable\n"))
  1730 
  1819 
  1731     def CompareLocalAndRemotePLC(self):
  1820     def CompareLocalAndRemotePLC(self):
  1732         if self._connector is None:
  1821         if self._connector is None:
  1733             return
  1822             return
  1734         # We are now connected. Update button status
  1823         # We are now connected. Update button status
  1735         MD5 = self.GetLastBuildMD5()
  1824         MD5 = self.GetLastBuildMD5()
  1736         # Check remote target PLC correspondance to that md5
  1825         # Check remote target PLC correspondance to that md5
  1737         if MD5 is not None:
  1826         if MD5 is not None:
  1738             if not self._connector.MatchMD5(MD5):
  1827             if not self._connector.MatchMD5(MD5):
  1739                 # self.logger.write_warning(
  1828                 # self.logger.write_warning(
  1740                 #     _("Latest build does not match with target, please transfer.\n"))
  1829                 # _("Latest build does not match with target, please
       
  1830                 # transfer.\n"))
  1741                 self.EnableMethod("_Transfer", True)
  1831                 self.EnableMethod("_Transfer", True)
  1742             else:
  1832             else:
  1743                 # self.logger.write(
  1833                 # self.logger.write(
  1744                 #     _("Latest build matches target, no transfer needed.\n"))
  1834                 #     _("Latest build matches target, no transfer needed.\n"))
  1745                 self.EnableMethod("_Transfer", True)
  1835                 self.EnableMethod("_Transfer", True)
  1768         # Get the last build PLC's
  1858         # Get the last build PLC's
  1769         MD5 = self.GetLastBuildMD5()
  1859         MD5 = self.GetLastBuildMD5()
  1770 
  1860 
  1771         # Check if md5 file is empty : ask user to build PLC
  1861         # Check if md5 file is empty : ask user to build PLC
  1772         if MD5 is None:
  1862         if MD5 is None:
  1773             self.logger.write_error(_("Failed : Must build before transfer.\n"))
  1863             self.logger.write_error(
       
  1864                 _("Failed : Must build before transfer.\n"))
  1774             return False
  1865             return False
  1775 
  1866 
  1776         # Compare PLC project with PLC on target
  1867         # Compare PLC project with PLC on target
  1777         if self._connector.MatchMD5(MD5):
  1868         if self._connector.MatchMD5(MD5):
  1778             self.logger.write(
  1869             self.logger.write(
  1803                     self.AppFrame.LogViewer.ResetLogCounters()
  1894                     self.AppFrame.LogViewer.ResetLogCounters()
  1804                 else:
  1895                 else:
  1805                     self.logger.write_error(_("Transfer failed\n"))
  1896                     self.logger.write_error(_("Transfer failed\n"))
  1806                 self.HidePLCProgress()
  1897                 self.HidePLCProgress()
  1807             else:
  1898             else:
  1808                 self.logger.write_error(_("No PLC to transfer (did build succeed ?)\n"))
  1899                 self.logger.write_error(
       
  1900                     _("No PLC to transfer (did build succeed ?)\n"))
  1809 
  1901 
  1810         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1902         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1811 
  1903 
  1812     StatusMethods = [
  1904     StatusMethods = [
  1813         {
  1905         {