ProjectController.py
changeset 2248 d9353e440887
parent 2245 fe3394697b02
child 2263 5227f54f6369
child 2415 f7d8891fe708
equal deleted inserted replaced
2247:921ba5658183 2248:d9353e440887
    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="' + 
   202                                'type="xsd:boolean" use="optional" default="' +
   198                                 ('true' if default else 'false') + '"/>'
   203                                ('true' if default else 'false') + '"/>'
   199                              for libname, _lib, default in features.libraries])+"""
   204                                for libname, _lib, default in features.libraries]) + """
   200               </xsd:complexType>
   205               </xsd:complexType>
   201             </xsd:element>""") if len(features.libraries) > 0 else '') + """
   206             </xsd:element>""") if len(features.libraries) > 0 else '') + """
   202           </xsd:sequence>
   207           </xsd:sequence>
   203           <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/>
   208           <xsd:attribute name="URI_location" type="xsd:string" use="optional" default=""/>
   204           <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"/>
   205         </xsd:complexType>
   210         </xsd:complexType>
   206       </xsd:element>
   211       </xsd:element>
   207     </xsd:schema>
   212     </xsd:schema>
   208     """
   213     """
   209     print XSD
       
   210     return XSD
   214     return XSD
   211 
   215 
   212 
   216 
   213 class ProjectController(ConfigTreeNode, PLCControler):
   217 class ProjectController(ConfigTreeNode, PLCControler):
       
   218 
   214     """
   219     """
   215     This class define Root object of the confnode tree.
   220     This class define Root object of the confnode tree.
   216     It is responsible of :
   221     It is responsible of :
   217     - Managing project directory
   222     - Managing project directory
   218     - Building project
   223     - Building project
   219     - Handling PLCOpenEditor controler and view
   224     - Handling PLCOpenEditor controler and view
   220     - Loading user confnodes and instanciante them as children
   225     - Loading user confnodes and instanciante them as children
   221     - ...
   226     - ...
   222 
   227 
   223     """
   228     """
   224     # 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.
   225     CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog)
   231     CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog)
   226     XSD = GetProjectControllerXSD()
   232     XSD = GetProjectControllerXSD()
   227     EditorType = ProjectNodeEditor
   233     EditorType = ProjectNodeEditor
   228     iec2c_cfg = None
   234     iec2c_cfg = None
   229 
   235 
   270         self.KillDebugThread()
   276         self.KillDebugThread()
   271 
   277 
   272     def LoadLibraries(self):
   278     def LoadLibraries(self):
   273         self.Libraries = []
   279         self.Libraries = []
   274         TypeStack = []
   280         TypeStack = []
   275         for libname, clsname, default in features.libraries:
   281         for libname, clsname, _default in features.libraries:
   276             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"):
   277                 Lib = GetClassImporter(clsname)()(self, libname, TypeStack)
   285                 Lib = GetClassImporter(clsname)()(self, libname, TypeStack)
   278                 TypeStack.append(Lib.GetTypes())
   286                 TypeStack.append(Lib.GetTypes())
   279                 self.Libraries.append(Lib)
   287                 self.Libraries.append(Lib)
   280 
   288 
   281     def SetAppFrame(self, frame, logger):
   289     def SetAppFrame(self, frame, logger):
   361         if target.getcontent() is None:
   369         if target.getcontent() is None:
   362             temp_root = self.Parser.CreateRoot()
   370             temp_root = self.Parser.CreateRoot()
   363             target = self.Parser.CreateElement("TargetType", "BeremizRoot")
   371             target = self.Parser.CreateElement("TargetType", "BeremizRoot")
   364             temp_root.setTargetType(target)
   372             temp_root.setTargetType(target)
   365             target_name = self.GetDefaultTargetName()
   373             target_name = self.GetDefaultTargetName()
   366             target.setcontent(self.Parser.CreateElement(target_name, "TargetType"))
   374             target.setcontent(
       
   375                 self.Parser.CreateElement(target_name, "TargetType"))
   367         return target
   376         return target
   368 
   377 
   369     def GetParamsAttributes(self, path=None):
   378     def GetParamsAttributes(self, path=None):
   370         params = ConfigTreeNode.GetParamsAttributes(self, path)
   379         params = ConfigTreeNode.GetParamsAttributes(self, path)
   371         if params[0]["name"] == "BeremizRoot":
   380         if params[0]["name"] == "BeremizRoot":
   372             for child in params[0]["children"]:
   381             for child in params[0]["children"]:
   373                 if child["name"] == "TargetType" and child["value"] == '':
   382                 if child["name"] == "TargetType" and child["value"] == '':
   374                     child.update(self.GetTarget().getElementInfos("TargetType"))
   383                     child.update(
       
   384                         self.GetTarget().getElementInfos("TargetType"))
   375         return params
   385         return params
   376 
   386 
   377     def SetParamsAttribute(self, path, value):
   387     def SetParamsAttribute(self, path, value):
   378         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:
   379             self.BeremizRoot.setTargetType(self.GetTarget())
   389             self.BeremizRoot.setTargetType(self.GetTarget())
   403         return False
   413         return False
   404 
   414 
   405     def _getProjectFilesPath(self, project_path=None):
   415     def _getProjectFilesPath(self, project_path=None):
   406         if project_path is not None:
   416         if project_path is not None:
   407             return os.path.join(project_path, "project_files")
   417             return os.path.join(project_path, "project_files")
   408         projectfiles_path = os.path.join(self.GetProjectPath(), "project_files")
   418         projectfiles_path = os.path.join(
       
   419             self.GetProjectPath(), "project_files")
   409         if not os.path.exists(projectfiles_path):
   420         if not os.path.exists(projectfiles_path):
   410             os.mkdir(projectfiles_path)
   421             os.mkdir(projectfiles_path)
   411         return projectfiles_path
   422         return projectfiles_path
   412 
   423 
   413     def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"):
   424     def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"):
   414         self.ProjectAddConfiguration(config_name)
   425         self.ProjectAddConfiguration(config_name)
   415         self.ProjectAddConfigurationResource(config_name, res_name)
   426         self.ProjectAddConfigurationResource(config_name, res_name)
   416 
   427 
   417     def SetProjectDefaultConfiguration(self):
   428     def SetProjectDefaultConfiguration(self):
   418         # Sets default task and instance for new project
   429         # Sets default task and instance for new project
   419         config = self.Project.getconfiguration(self.GetProjectMainConfigurationName())
   430         config = self.Project.getconfiguration(
       
   431             self.GetProjectMainConfigurationName())
   420         resource = config.getresource()[0].getname()
   432         resource = config.getresource()[0].getname()
   421         config = config.getname()
   433         config = config.getname()
   422         resource_tagname = ComputeConfigurationResourceName(config, resource)
   434         resource_tagname = ComputeConfigurationResourceName(config, resource)
   423         def_task = [
   435         def_task = [
   424             {'Priority': '0', 'Single': '', 'Interval': 'T#20ms', 'Name': 'task0', 'Triggering': 'Cyclic'}]
   436             {'Priority': '0', 'Single': '', 'Interval': 'T#20ms', 'Name': 'task0', 'Triggering': 'Cyclic'}]
   471         # Load PLCOpen file
   483         # Load PLCOpen file
   472         error = self.OpenXMLFile(plc_file)
   484         error = self.OpenXMLFile(plc_file)
   473         if error is not None:
   485         if error is not None:
   474             if self.Project is not None:
   486             if self.Project is not None:
   475                 (fname_err, lnum, src) = (("PLC",) + error)
   487                 (fname_err, lnum, src) = (("PLC",) + error)
   476                 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))
   477             else:
   490             else:
   478                 return error, False
   491                 return error, False
   479         if len(self.GetProjectConfigNames()) == 0:
   492         if len(self.GetProjectConfigNames()) == 0:
   480             self.AddProjectDefaultConfiguration()
   493             self.AddProjectDefaultConfiguration()
   481         # Change XSD into class members
   494         # Change XSD into class members
   528         self.ResetAppFrame(None)
   541         self.ResetAppFrame(None)
   529 
   542 
   530     def CheckNewProjectPath(self, old_project_path, new_project_path):
   543     def CheckNewProjectPath(self, old_project_path, new_project_path):
   531         if old_project_path == new_project_path:
   544         if old_project_path == new_project_path:
   532             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"))
   533             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)
   534             dialog.ShowModal()
   548             dialog.ShowModal()
   535             return False
   549             return False
   536         else:
   550         else:
   537             plc_file = os.path.join(new_project_path, "plc.xml")
   551             plc_file = os.path.join(new_project_path, "plc.xml")
   538             if os.path.isfile(plc_file):
   552             if os.path.isfile(plc_file):
   539                 message = (_("Selected directory already contains another project. Overwrite? \n"))
   553                 message = (
   540                 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)
   541                 answer = dialog.ShowModal()
   557                 answer = dialog.ShowModal()
   542                 return answer == wx.ID_YES
   558                 return answer == wx.ID_YES
   543         return True
   559         return True
   544 
   560 
   545     def SaveProject(self, from_project_path=None):
   561     def SaveProject(self, from_project_path=None):
   546         if self.CheckProjectPathPerm(False):
   562         if self.CheckProjectPathPerm(False):
   547             if from_project_path is not None:
   563             if from_project_path is not None:
   548                 old_projectfiles_path = self._getProjectFilesPath(from_project_path)
   564                 old_projectfiles_path = self._getProjectFilesPath(
       
   565                     from_project_path)
   549                 if os.path.isdir(old_projectfiles_path):
   566                 if os.path.isdir(old_projectfiles_path):
   550                     shutil.copytree(old_projectfiles_path,
   567                     shutil.copytree(old_projectfiles_path,
   551                                     self._getProjectFilesPath(self.ProjectPath))
   568                                     self._getProjectFilesPath(self.ProjectPath))
   552             self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml'))
   569             self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml'))
   553             result = self.CTNRequestSave(from_project_path)
   570             result = self.CTNRequestSave(from_project_path)
   558         # Ask user to choose a path with write permissions
   575         # Ask user to choose a path with write permissions
   559         if wx.Platform == '__WXMSW__':
   576         if wx.Platform == '__WXMSW__':
   560             path = os.getenv("USERPROFILE")
   577             path = os.getenv("USERPROFILE")
   561         else:
   578         else:
   562             path = os.getenv("HOME")
   579             path = os.getenv("HOME")
   563         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)
   564         answer = dirdialog.ShowModal()
   582         answer = dirdialog.ShowModal()
   565         dirdialog.Destroy()
   583         dirdialog.Destroy()
   566         if answer == wx.ID_OK:
   584         if answer == wx.ID_OK:
   567             newprojectpath = dirdialog.GetPath()
   585             newprojectpath = dirdialog.GetPath()
   568             if os.path.isdir(newprojectpath):
   586             if os.path.isdir(newprojectpath):
   582 
   600 
   583     def GetLibrariesCCode(self, buildpath):
   601     def GetLibrariesCCode(self, buildpath):
   584         if len(self.Libraries) == 0:
   602         if len(self.Libraries) == 0:
   585             return [], [], ()
   603             return [], [], ()
   586         self.GetIECProgramsAndVariables()
   604         self.GetIECProgramsAndVariables()
   587         LibIECCflags = '"-I%s" -Wno-unused-function' % os.path.abspath(self.GetIECLibPath())
   605         LibIECCflags = '"-I%s" -Wno-unused-function' % os.path.abspath(
       
   606             self.GetIECLibPath())
   588         LocatedCCodeAndFlags = []
   607         LocatedCCodeAndFlags = []
   589         Extras = []
   608         Extras = []
   590         for lib in self.Libraries:
   609         for lib in self.Libraries:
   591             res = lib.Generate_C(buildpath, self._VariablesList, LibIECCflags)
   610             res = lib.Generate_C(buildpath, self._VariablesList, LibIECCflags)
   592             LocatedCCodeAndFlags.append(res[:2])
   611             LocatedCCodeAndFlags.append(res[:2])
   593             if len(res) > 2:
   612             if len(res) > 2:
   594                 Extras.extend(res[2:])
   613                 Extras.extend(res[2:])
   595         return map(list, zip(*LocatedCCodeAndFlags))+[tuple(Extras)]
   614         return map(list, zip(*LocatedCCodeAndFlags)) + [tuple(Extras)]
   596 
   615 
   597     # Update PLCOpenEditor ConfNode Block types from loaded confnodes
   616     # Update PLCOpenEditor ConfNode Block types from loaded confnodes
   598     def RefreshConfNodesBlockLists(self):
   617     def RefreshConfNodesBlockLists(self):
   599         if getattr(self, "Children", None) is not None:
   618         if getattr(self, "Children", None) is not None:
   600             self.ClearConfNodeTypes()
   619             self.ClearConfNodeTypes()
   656         # Create a build path in project folder if user has permissions
   675         # Create a build path in project folder if user has permissions
   657         if CheckPathPerm(self.ProjectPath):
   676         if CheckPathPerm(self.ProjectPath):
   658             self.DefaultBuildPath = os.path.join(self.ProjectPath, "build")
   677             self.DefaultBuildPath = os.path.join(self.ProjectPath, "build")
   659         # Create a build path in temp folder
   678         # Create a build path in temp folder
   660         else:
   679         else:
   661             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")
   662 
   682 
   663         if not os.path.exists(self.DefaultBuildPath):
   683         if not os.path.exists(self.DefaultBuildPath):
   664             os.makedirs(self.DefaultBuildPath)
   684             os.makedirs(self.DefaultBuildPath)
   665         return self.DefaultBuildPath
   685         return self.DefaultBuildPath
   666 
   686 
   681 
   701 
   682     def GetLocations(self):
   702     def GetLocations(self):
   683         locations = []
   703         locations = []
   684         filepath = os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h")
   704         filepath = os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h")
   685         if os.path.isfile(filepath):
   705         if os.path.isfile(filepath):
   686             # IEC2C compiler generate a list of located variables : LOCATED_VARIABLES.h
   706             # IEC2C compiler generate a list of located variables :
   687             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"))
   688             # each line of LOCATED_VARIABLES.h declares a located variable
   710             # each line of LOCATED_VARIABLES.h declares a located variable
   689             lines = [line.strip() for line in location_file.readlines()]
   711             lines = [line.strip() for line in location_file.readlines()]
   690             # This regular expression parses the lines genereated by IEC2C
   712             # This regular expression parses the lines genereated by IEC2C
   691             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]*)\)")
   692             for line in lines:
   715             for line in lines:
   693                 # If line match RE,
   716                 # If line match RE,
   694                 result = LOCATED_MODEL.match(line)
   717                 result = LOCATED_MODEL.match(line)
   695                 if result:
   718                 if result:
   696                     # Get the resulting dict
   719                     # Get the resulting dict
   697                     resdict = result.groupdict()
   720                     resdict = result.groupdict()
   698                     # rewrite string for variadic location as a tuple of integers
   721                     # rewrite string for variadic location as a tuple of
       
   722                     # integers
   699                     resdict['LOC'] = tuple(map(int, resdict['LOC'].split(',')))
   723                     resdict['LOC'] = tuple(map(int, resdict['LOC'].split(',')))
   700                     # set located size to 'X' if not given
   724                     # set located size to 'X' if not given
   701                     if not resdict['SIZE']:
   725                     if not resdict['SIZE']:
   702                         resdict['SIZE'] = 'X'
   726                         resdict['SIZE'] = 'X'
   703                     # finally store into located variable list
   727                     # finally store into located variable list
   719         """
   743         """
   720 
   744 
   721         # Update PLCOpenEditor ConfNode Block types before generate ST code
   745         # Update PLCOpenEditor ConfNode Block types before generate ST code
   722         self.RefreshConfNodesBlockLists()
   746         self.RefreshConfNodesBlockLists()
   723 
   747 
   724         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"))
   725         # ask PLCOpenEditor controller to write ST/IL/SFC code file
   750         # ask PLCOpenEditor controller to write ST/IL/SFC code file
   726         _program, errors, warnings = self.GenerateProgram(self._getIECgeneratedcodepath())
   751         _program, errors, warnings = self.GenerateProgram(
       
   752             self._getIECgeneratedcodepath())
   727         if len(warnings) > 0:
   753         if len(warnings) > 0:
   728             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"))
   729             for warning in warnings:
   756             for warning in warnings:
   730                 self.logger.write_warning("%s\n" % warning)
   757                 self.logger.write_warning("%s\n" % warning)
   731         if len(errors) > 0:
   758         if len(errors) > 0:
   732             # Failed !
   759             # Failed !
   733             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])
   734             return False
   762             return False
   735         plc_file = open(self._getIECcodepath(), "w")
   763         plc_file = open(self._getIECcodepath(), "w")
   736         # Add ST Library from confnodes
   764         # Add ST Library from confnodes
   737         plc_file.write(self.GetLibrariesSTCode())
   765         plc_file.write(self.GetLibrariesSTCode())
   738         if os.path.isfile(self._getIECrawcodepath()):
   766         if os.path.isfile(self._getIECrawcodepath()):
   763             iec2c_libpath,
   791             iec2c_libpath,
   764             buildpath,
   792             buildpath,
   765             self._getIECcodepath())
   793             self._getIECcodepath())
   766 
   794 
   767         try:
   795         try:
   768             # Invoke compiler. Output files are listed to stdout, errors to stderr
   796             # Invoke compiler.
       
   797             # Output files are listed to stdout, errors to stderr
   769             status, result, err_result = ProcessLogger(self.logger, buildcmd,
   798             status, result, err_result = ProcessLogger(self.logger, buildcmd,
   770                                                        no_stdout=True, no_stderr=True).spin()
   799                                                        no_stdout=True,
       
   800                                                        no_stderr=True).spin()
   771         except Exception, e:
   801         except Exception, e:
   772             self.logger.write_error(buildcmd + "\n")
   802             self.logger.write_error(buildcmd + "\n")
   773             self.logger.write_error(repr(e) + "\n")
   803             self.logger.write_error(repr(e) + "\n")
   774             return False
   804             return False
   775 
   805 
   794                         if line[0] not in '\t \r\n':
   824                         if line[0] not in '\t \r\n':
   795                             last_section = line
   825                             last_section = line
   796 
   826 
   797                         if first_line <= i <= last_line:
   827                         if first_line <= i <= last_line:
   798                             if last_section is not None:
   828                             if last_section is not None:
   799                                 self.logger.write_warning("In section: " + last_section)
   829                                 self.logger.write_warning(
       
   830                                     "In section: " + last_section)
   800                                 last_section = None  # only write section once
   831                                 last_section = None  # only write section once
   801                             self.logger.write_warning("%04d: %s" % (i, line))
   832                             self.logger.write_warning("%04d: %s" % (i, line))
   802 
   833 
   803                     f.close()
   834                     f.close()
   804 
   835 
   805             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)
   806             return False
   838             return False
   807 
   839 
   808         # Now extract C files of stdout
   840         # Now extract C files of stdout
   809         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"]
   810         # 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
   811         C_files.remove("POUS.c")
   844         C_files.remove("POUS.c")
   812         if not C_files:
   845         if not C_files:
   813             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"))
   814             return False
   848             return False
   815         # transform those base names to full names with path
   849         # transform those base names to full names with path
   816         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)
   817 
   852 
   818         # prepend beremiz include to configuration header
   853         # prepend beremiz include to configuration header
   819         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"]
   820         H_files.remove("LOCATED_VARIABLES.h")
   856         H_files.remove("LOCATED_VARIABLES.h")
   821         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)
   822         for H_file in H_files:
   859         for H_file in H_files:
   823             with file(H_file, 'r') as original:
   860             with file(H_file, 'r') as original:
   824                 data = original.read()
   861                 data = original.read()
   825             with file(H_file, 'w') as modified:
   862             with file(H_file, 'w') as modified:
   826                 modified.write('#include "beremiz.h"\n' + data)
   863                 modified.write('#include "beremiz.h"\n' + data)
   827 
   864 
   828         self.logger.write(_("Extracting Located Variables...\n"))
   865         self.logger.write(_("Extracting Located Variables...\n"))
   829         # 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
   830         self.PLCGeneratedLocatedVars = self.GetLocations()
   868         self.PLCGeneratedLocatedVars = self.GetLocations()
   831         # 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
   832         self.PLCGeneratedCFiles = C_files
   870         self.PLCGeneratedCFiles = C_files
   833         # compute CFLAGS for plc
   871         # compute CFLAGS for plc
   834         self.plcCFLAGS = '"-I%s" -Wno-unused-function' % self.iec2c_cfg.getLibCPath()
   872         self.plcCFLAGS = '"-I%s" -Wno-unused-function' % self.iec2c_cfg.getLibCPath()
   859         if builder is not None:
   897         if builder is not None:
   860             return builder.GetBinaryCodeMD5()
   898             return builder.GetBinaryCodeMD5()
   861         else:
   899         else:
   862             return None
   900             return None
   863 
   901 
   864     #######################################################################
   902     #
   865     #
   903     #
   866     #                C CODE GENERATION METHODS
   904     #                C CODE GENERATION METHODS
   867     #
   905     #
   868     #######################################################################
   906     #
   869 
   907 
   870     def CTNGenerate_C(self, buildpath, locations):
   908     def CTNGenerate_C(self, buildpath, locations):
   871         """
   909         """
   872         Return C code generated by iec2c compiler
   910         Return C code generated by iec2c compiler
   873         when _generate_softPLC have been called
   911         when _generate_softPLC have been called
   902         if self._ProgramList is None or self._VariablesList is None:
   940         if self._ProgramList is None or self._VariablesList is None:
   903             try:
   941             try:
   904                 csvfile = os.path.join(self._getBuildPath(), "VARIABLES.csv")
   942                 csvfile = os.path.join(self._getBuildPath(), "VARIABLES.csv")
   905                 # describes CSV columns
   943                 # describes CSV columns
   906                 ProgramsListAttributeName = ["num", "C_path", "type"]
   944                 ProgramsListAttributeName = ["num", "C_path", "type"]
   907                 VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"]
   945                 VariablesListAttributeName = [
       
   946                     "num", "vartype", "IEC_path", "C_path", "type"]
   908                 self._ProgramList = []
   947                 self._ProgramList = []
   909                 self._VariablesList = []
   948                 self._VariablesList = []
   910                 self._DbgVariablesList = []
   949                 self._DbgVariablesList = []
   911                 self._IECPathToIdx = {}
   950                 self._IECPathToIdx = {}
   912 
   951 
   922                         ListGroup[-1].append(strippedline)
   961                         ListGroup[-1].append(strippedline)
   923 
   962 
   924                 # first section contains programs
   963                 # first section contains programs
   925                 for line in ListGroup[0]:
   964                 for line in ListGroup[0]:
   926                     # Split and Maps each field to dictionnary entries
   965                     # Split and Maps each field to dictionnary entries
   927                     attrs = dict(zip(ProgramsListAttributeName, line.strip().split(';')))
   966                     attrs = dict(
       
   967                         zip(ProgramsListAttributeName, line.strip().split(';')))
   928                     # Truncate "C_path" to remove conf an resources names
   968                     # Truncate "C_path" to remove conf an resources names
   929                     attrs["C_path"] = '__'.join(attrs["C_path"].split(".", 2)[1:])
   969                     attrs["C_path"] = '__'.join(
       
   970                         attrs["C_path"].split(".", 2)[1:])
   930                     # Push this dictionnary into result.
   971                     # Push this dictionnary into result.
   931                     self._ProgramList.append(attrs)
   972                     self._ProgramList.append(attrs)
   932 
   973 
   933                 # second section contains all variables
   974                 # second section contains all variables
   934                 config_FBs = {}
   975                 config_FBs = {}
   935                 Idx = 0
   976                 Idx = 0
   936                 for line in ListGroup[1]:
   977                 for line in ListGroup[1]:
   937                     # Split and Maps each field to dictionnary entries
   978                     # Split and Maps each field to dictionnary entries
   938                     attrs = dict(zip(VariablesListAttributeName, line.strip().split(';')))
   979                     attrs = dict(
       
   980                         zip(VariablesListAttributeName, line.strip().split(';')))
   939                     # Truncate "C_path" to remove conf an resources names
   981                     # Truncate "C_path" to remove conf an resources names
   940                     parts = attrs["C_path"].split(".", 2)
   982                     parts = attrs["C_path"].split(".", 2)
   941                     if len(parts) > 2:
   983                     if len(parts) > 2:
   942                         config_FB = config_FBs.get(tuple(parts[:2]))
   984                         config_FB = config_FBs.get(tuple(parts[:2]))
   943                         if config_FB:
   985                         if config_FB:
   964                 # third section contains ticktime
  1006                 # third section contains ticktime
   965                 if len(ListGroup) > 2:
  1007                 if len(ListGroup) > 2:
   966                     self._Ticktime = int(ListGroup[2][0])
  1008                     self._Ticktime = int(ListGroup[2][0])
   967 
  1009 
   968             except Exception:
  1010             except Exception:
   969                 self.logger.write_error(_("Cannot open/parse VARIABLES.csv!\n"))
  1011                 self.logger.write_error(
       
  1012                     _("Cannot open/parse VARIABLES.csv!\n"))
   970                 self.logger.write_error(traceback.format_exc())
  1013                 self.logger.write_error(traceback.format_exc())
   971                 self.ResetIECProgramsAndVariables()
  1014                 self.ResetIECProgramsAndVariables()
   972                 return False
  1015                 return False
   973 
  1016 
   974         return True
  1017         return True
  1034                     "void __retrieve_%(s)s(void);\n" +
  1077                     "void __retrieve_%(s)s(void);\n" +
  1035                     "void __publish_%(s)s(void);") % {'s': locstr} for locstr in locstrs]),
  1078                     "void __publish_%(s)s(void);") % {'s': locstr} for locstr in locstrs]),
  1036                 "retrieve_calls": "\n    ".join([
  1079                 "retrieve_calls": "\n    ".join([
  1037                     "__retrieve_%s();" % locstr for locstr in locstrs]),
  1080                     "__retrieve_%s();" % locstr for locstr in locstrs]),
  1038                 "publish_calls": "\n    ".join([  # Call publish in reverse order
  1081                 "publish_calls": "\n    ".join([  # Call publish in reverse order
  1039                     "__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)]),
  1040                 "init_calls": "\n    ".join([
  1083                 "init_calls": "\n    ".join([
  1041                     "init_level=%d; " % (i+1) +
  1084                     "init_level=%d; " % (i + 1) +
  1042                     "if((res = __init_%s(argc,argv))){" % locstr +
  1085                     "if((res = __init_%s(argc,argv))){" % locstr +
  1043                     # "printf(\"%s\"); "%locstr + #for debug
  1086                     # "printf(\"%s\"); "%locstr + #for debug
  1044                     "return res;}" for i, locstr in enumerate(locstrs)]),
  1087                     "return res;}" for i, locstr in enumerate(locstrs)]),
  1045                 "cleanup_calls": "\n    ".join([
  1088                 "cleanup_calls": "\n    ".join([
  1046                     "if(init_level >= %d) " % i +
  1089                     "if(init_level >= %d) " % i +
  1047                     "__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)])
  1048                 }
  1091             }
  1049         else:
  1092         else:
  1050             plc_main_code = targets.GetCode("plc_main_head.c") % {
  1093             plc_main_code = targets.GetCode("plc_main_head.c") % {
  1051                 "calls_prototypes": "\n",
  1094                 "calls_prototypes": "\n",
  1052                 "retrieve_calls":   "\n",
  1095                 "retrieve_calls":   "\n",
  1053                 "publish_calls":    "\n",
  1096                 "publish_calls":    "\n",
  1054                 "init_calls":       "\n",
  1097                 "init_calls":       "\n",
  1055                 "cleanup_calls":    "\n"
  1098                 "cleanup_calls":    "\n"
  1056             }
  1099             }
  1057         plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent().getLocalTag())
  1100         plc_main_code += targets.GetTargetCode(
       
  1101             self.GetTarget().getcontent().getLocalTag())
  1058         plc_main_code += targets.GetCode("plc_main_tail.c")
  1102         plc_main_code += targets.GetCode("plc_main_tail.c")
  1059         return plc_main_code
  1103         return plc_main_code
  1060 
  1104 
  1061     def _Build(self):
  1105     def _Build(self):
  1062         """
  1106         """
  1124         try:
  1168         try:
  1125             CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C(
  1169             CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C(
  1126                 buildpath,
  1170                 buildpath,
  1127                 self.PLCGeneratedLocatedVars)
  1171                 self.PLCGeneratedLocatedVars)
  1128         except Exception:
  1172         except Exception:
  1129             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"))
  1130             self.logger.write_error(traceback.format_exc())
  1175             self.logger.write_error(traceback.format_exc())
  1131             self.ResetBuildMD5()
  1176             self.ResetBuildMD5()
  1132             return False
  1177             return False
  1133 
  1178 
  1134         # Generate C code and compilation params from liraries
  1179         # Generate C code and compilation params from liraries
  1135         try:
  1180         try:
  1136             LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(buildpath)
  1181             LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(
       
  1182                 buildpath)
  1137         except Exception:
  1183         except Exception:
  1138             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"))
  1139             self.logger.write_error(traceback.format_exc())
  1186             self.logger.write_error(traceback.format_exc())
  1140             self.ResetBuildMD5()
  1187             self.ResetBuildMD5()
  1141             return False
  1188             return False
  1142 
  1189 
  1143         self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + CTNLocationCFilesAndCFLAGS
  1190         self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + \
       
  1191             CTNLocationCFilesAndCFLAGS
  1144         self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS
  1192         self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS
  1145         ExtraFiles = CTNExtraFiles + LibExtraFiles
  1193         ExtraFiles = CTNExtraFiles + LibExtraFiles
  1146 
  1194 
  1147         # Get temporary directory path
  1195         # Get temporary directory path
  1148         extrafilespath = self._getExtraFilesPath()
  1196         extrafilespath = self._getExtraFilesPath()
  1157             open(fpath, "wb").write(fobject.read())
  1205             open(fpath, "wb").write(fobject.read())
  1158         # Now we can forget ExtraFiles (will close files object)
  1206         # Now we can forget ExtraFiles (will close files object)
  1159         del ExtraFiles
  1207         del ExtraFiles
  1160 
  1208 
  1161         # Header file for extensions
  1209         # Header file for extensions
  1162         open(os.path.join(buildpath, "beremiz.h"), "w").write(targets.GetHeader())
  1210         open(os.path.join(buildpath, "beremiz.h"), "w").write(
       
  1211             targets.GetHeader())
  1163 
  1212 
  1164         # Template based part of C code generation
  1213         # Template based part of C code generation
  1165         # 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
  1166         c_source = [
  1215         c_source = [
  1167             #  debugger code
  1216             #  debugger code
  1176                 code = generator()
  1225                 code = generator()
  1177                 if code is None:
  1226                 if code is None:
  1178                     raise Exception
  1227                     raise Exception
  1179                 code_path = os.path.join(buildpath, filename)
  1228                 code_path = os.path.join(buildpath, filename)
  1180                 open(code_path, "w").write(code)
  1229                 open(code_path, "w").write(code)
  1181                 # Insert this file as first file to be compiled at root confnode
  1230                 # Insert this file as first file to be compiled at root
  1182                 self.LocationCFilesAndCFLAGS[0][1].insert(0, (code_path, self.plcCFLAGS))
  1231                 # confnode
       
  1232                 self.LocationCFilesAndCFLAGS[0][1].insert(
       
  1233                     0, (code_path, self.plcCFLAGS))
  1183             except Exception:
  1234             except Exception:
  1184                 self.logger.write_error(name+_(" generation failed !\n"))
  1235                 self.logger.write_error(name + _(" generation failed !\n"))
  1185                 self.logger.write_error(traceback.format_exc())
  1236                 self.logger.write_error(traceback.format_exc())
  1186                 self.ResetBuildMD5()
  1237                 self.ResetBuildMD5()
  1187                 return False
  1238                 return False
  1188         self.logger.write(_("C code generated successfully.\n"))
  1239         self.logger.write(_("C code generated successfully.\n"))
  1189         return True
  1240         return True
  1190 
  1241 
  1191     def ShowError(self, logger, from_location, to_location):
  1242     def ShowError(self, logger, from_location, to_location):
  1192         chunk_infos = self.GetChunkInfos(from_location, to_location)
  1243         chunk_infos = self.GetChunkInfos(from_location, to_location)
  1193         for infos, (start_row, start_col) in chunk_infos:
  1244         for infos, (start_row, start_col) in chunk_infos:
  1194             row = 1 if from_location[0] < start_row else (from_location[0] - start_row)
  1245             row = 1 if from_location[0] < start_row else (
  1195             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)
  1196             start = (row, col)
  1249             start = (row, col)
  1197 
  1250 
  1198             row = 1 if to_location[0] < start_row else (to_location[0] - start_row)
  1251             row = 1 if to_location[0] < start_row else (
  1199             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)
  1200             end = (row, col)
  1255             end = (row, col)
  1201 
  1256 
  1202             if self.AppFrame is not None:
  1257             if self.AppFrame is not None:
  1203                 self.AppFrame.ShowError(infos, start, end)
  1258                 self.AppFrame.ShowError(infos, start, end)
  1204 
  1259 
  1225     def _OpenView(self, name=None, onlyopened=False):
  1280     def _OpenView(self, name=None, onlyopened=False):
  1226         if name == "IEC code":
  1281         if name == "IEC code":
  1227             if self._IECCodeView is None:
  1282             if self._IECCodeView is None:
  1228                 plc_file = self._getIECcodepath()
  1283                 plc_file = self._getIECcodepath()
  1229 
  1284 
  1230                 self._IECCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name)
  1285                 self._IECCodeView = IECCodeViewer(
       
  1286                     self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name)
  1231                 self._IECCodeView.SetTextSyntax("ALL")
  1287                 self._IECCodeView.SetTextSyntax("ALL")
  1232                 self._IECCodeView.SetKeywords(IEC_KEYWORDS)
  1288                 self._IECCodeView.SetKeywords(IEC_KEYWORDS)
  1233                 try:
  1289                 try:
  1234                     text = file(plc_file).read()
  1290                     text = file(plc_file).read()
  1235                 except Exception:
  1291                 except Exception:
  1246 
  1302 
  1247         elif name == "IEC raw code":
  1303         elif name == "IEC raw code":
  1248             if self._IECRawCodeView is None:
  1304             if self._IECRawCodeView is None:
  1249                 controler = MiniTextControler(self._getIECrawcodepath(), self)
  1305                 controler = MiniTextControler(self._getIECrawcodepath(), self)
  1250 
  1306 
  1251                 self._IECRawCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name)
  1307                 self._IECRawCodeView = IECCodeViewer(
       
  1308                     self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name)
  1252                 self._IECRawCodeView.SetTextSyntax("ALL")
  1309                 self._IECRawCodeView.SetTextSyntax("ALL")
  1253                 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS)
  1310                 self._IECRawCodeView.SetKeywords(IEC_KEYWORDS)
  1254                 self._IECRawCodeView.RefreshView()
  1311                 self._IECRawCodeView.RefreshView()
  1255                 self._IECRawCodeView.SetIcon(GetBitmap("ST"))
  1312                 self._IECRawCodeView.SetIcon(GetBitmap("ST"))
  1256                 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor)
  1313                 setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor)
  1260 
  1317 
  1261             return self._IECRawCodeView
  1318             return self._IECRawCodeView
  1262 
  1319 
  1263         elif name == "Project Files":
  1320         elif name == "Project Files":
  1264             if self._ProjectFilesView is None:
  1321             if self._ProjectFilesView is None:
  1265                 self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True)
  1322                 self._ProjectFilesView = FileManagementPanel(
       
  1323                     self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True)
  1266 
  1324 
  1267                 extensions = []
  1325                 extensions = []
  1268                 for extension, name, editor in features.file_editors:
  1326                 for extension, name, editor in features.file_editors:
  1269                     if extension not in extensions:
  1327                     if extension not in extensions:
  1270                         extensions.append(extension)
  1328                         extensions.append(extension)
  1302 
  1360 
  1303                     if editor_name != "":
  1361                     if editor_name != "":
  1304                         name = "::".join([filepath, editor_name])
  1362                         name = "::".join([filepath, editor_name])
  1305 
  1363 
  1306                         editor = editors[editor_name]()
  1364                         editor = editors[editor_name]()
  1307                         self._FileEditors[filepath] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame)
  1365                         self._FileEditors[filepath] = editor(
       
  1366                             self.AppFrame.TabsOpened, self, name, self.AppFrame)
  1308                         self._FileEditors[filepath].SetIcon(GetBitmap("FILE"))
  1367                         self._FileEditors[filepath].SetIcon(GetBitmap("FILE"))
  1309                         if isinstance(self._FileEditors[filepath], DebugViewer):
  1368                         if isinstance(self._FileEditors[filepath], DebugViewer):
  1310                             self._FileEditors[filepath].SetDataProducer(self)
  1369                             self._FileEditors[filepath].SetDataProducer(self)
  1311 
  1370 
  1312             if filepath in self._FileEditors:
  1371             if filepath in self._FileEditors:
  1393         if self.previous_plcstate != status:
  1452         if self.previous_plcstate != status:
  1394             allmethods = self.DefaultMethods.copy()
  1453             allmethods = self.DefaultMethods.copy()
  1395             allmethods.update(
  1454             allmethods.update(
  1396                 self.MethodsFromStatus.get(status, {}))
  1455                 self.MethodsFromStatus.get(status, {}))
  1397             for method, active in allmethods.items():
  1456             for method, active in allmethods.items():
  1398                 self.ShowMethod(method,active)
  1457                 self.ShowMethod(method, active)
  1399             self.previous_plcstate = status
  1458             self.previous_plcstate = status
  1400             if self.AppFrame is not None:
  1459             if self.AppFrame is not None:
  1401                 updated = True
  1460                 updated = True
  1402                 self.AppFrame.RefreshStatusToolBar()
  1461                 self.AppFrame.RefreshStatusToolBar()
  1403                 if status == "Disconnected":
  1462                 if status == "Disconnected":
  1404                     self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 1)
  1463                     self.AppFrame.ConnectionStatusBar.SetStatusText(
       
  1464                         self.GetTextStatus(status), 1)
  1405                     self.AppFrame.ConnectionStatusBar.SetStatusText('', 2)
  1465                     self.AppFrame.ConnectionStatusBar.SetStatusText('', 2)
  1406                 else:
  1466                 else:
  1407                     self.AppFrame.ConnectionStatusBar.SetStatusText(
  1467                     self.AppFrame.ConnectionStatusBar.SetStatusText(
  1408                         _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1)
  1468                         _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1)
  1409                     self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 2)
  1469                     self.AppFrame.ConnectionStatusBar.SetStatusText(
       
  1470                         self.GetTextStatus(status), 2)
  1410         return updated
  1471         return updated
  1411 
  1472 
  1412     def GetTextStatus(self, status):
  1473     def GetTextStatus(self, status):
  1413         msgs = {
  1474         msgs = {
  1414             "Started":      _("Started"),
  1475             "Started":      _("Started"),
  1415             "Stopped":      _("Stopped"),
  1476             "Stopped":      _("Stopped"),
  1416             "Empty":        _("Empty"),
  1477             "Empty":        _("Empty"),
  1417             "Broken":       _("Broken"),
  1478             "Broken":       _("Broken"),
  1418             "Disconnected": _("Disconnected")
  1479             "Disconnected": _("Disconnected")
  1419             }
  1480         }
  1420         return msgs.get(status, status)
  1481         return msgs.get(status, status)
  1421 
  1482 
  1422     def ShowPLCProgress(self, status="", progress=0):
  1483     def ShowPLCProgress(self, status="", progress=0):
  1423         self.AppFrame.ProgressStatusBar.Show()
  1484         self.AppFrame.ProgressStatusBar.Show()
  1424         self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 1)
  1485         self.AppFrame.ConnectionStatusBar.SetStatusText(
       
  1486             self.GetTextStatus(status), 1)
  1425         self.AppFrame.ProgressStatusBar.SetValue(progress)
  1487         self.AppFrame.ProgressStatusBar.SetValue(progress)
  1426 
  1488 
  1427     def HidePLCProgress(self):
  1489     def HidePLCProgress(self):
  1428         # clear previous_plcstate to restore status
  1490         # clear previous_plcstate to restore status
  1429         # in UpdateMethodsFromPLCStatus()
  1491         # in UpdateMethodsFromPLCStatus()
  1435         self.UpdateMethodsFromPLCStatus()
  1497         self.UpdateMethodsFromPLCStatus()
  1436 
  1498 
  1437     def SnapshotAndResetDebugValuesBuffers(self):
  1499     def SnapshotAndResetDebugValuesBuffers(self):
  1438         if self._connector is not None:
  1500         if self._connector is not None:
  1439             plc_status, Traces = self._connector.GetTraceVariables()
  1501             plc_status, Traces = self._connector.GetTraceVariables()
  1440             # 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()]
  1441             if plc_status == "Started":
  1504             if plc_status == "Started":
  1442                 if len(Traces) > 0:
  1505                 if len(Traces) > 0:
  1443                     for debug_tick, debug_buff in Traces:
  1506                     for debug_tick, debug_buff in Traces:
  1444                         debug_vars = UnpackDebugBuffer(debug_buff, self.TracedIECTypes)
  1507                         debug_vars = UnpackDebugBuffer(
       
  1508                             debug_buff, self.TracedIECTypes)
  1445                         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):
  1446                             for IECPath, values_buffer, value in izip(
  1510                             for IECPath, values_buffer, value in izip(
  1447                                     self.TracedIECPath,
  1511                                     self.TracedIECPath,
  1448                                     self.DebugValuesBuffers,
  1512                                     self.DebugValuesBuffers,
  1449                                     debug_vars):
  1513                                     debug_vars):
  1450                                 IECdebug_data = self.IECdebug_datas.get(IECPath, None)
  1514                                 IECdebug_data = self.IECdebug_datas.get(
       
  1515                                     IECPath, None)
  1451                                 if IECdebug_data is not None and value is not None:
  1516                                 if IECdebug_data is not None and value is not None:
  1452                                     forced = IECdebug_data[2:4] == ["Forced", value]
  1517                                     forced = IECdebug_data[2:4] == [
       
  1518                                         "Forced", value]
  1453                                     if not IECdebug_data[4] and len(values_buffer) > 0:
  1519                                     if not IECdebug_data[4] and len(values_buffer) > 0:
  1454                                         values_buffer[-1] = (value, forced)
  1520                                         values_buffer[-1] = (value, forced)
  1455                                     else:
  1521                                     else:
  1456                                         values_buffer.append((value, forced))
  1522                                         values_buffer.append((value, forced))
  1457                             self.DebugTicks.append(debug_tick)
  1523                             self.DebugTicks.append(debug_tick)
  1476                     # Callable Dict is empty.
  1542                     # Callable Dict is empty.
  1477                     # This variable is not needed anymore!
  1543                     # This variable is not needed anymore!
  1478                     IECPathsToPop.append(IECPath)
  1544                     IECPathsToPop.append(IECPath)
  1479                 elif IECPath != "__tick__":
  1545                 elif IECPath != "__tick__":
  1480                     # Convert
  1546                     # Convert
  1481                     Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None))
  1547                     Idx, IEC_Type = self._IECPathToIdx.get(
       
  1548                         IECPath, (None, None))
  1482                     if Idx is not None:
  1549                     if Idx is not None:
  1483                         if IEC_Type in DebugTypesSize:
  1550                         if IEC_Type in DebugTypesSize:
  1484                             Idxs.append((Idx, IEC_Type, fvalue, IECPath))
  1551                             Idxs.append((Idx, IEC_Type, fvalue, IECPath))
  1485                         else:
  1552                         else:
  1486                             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)
  1487                     else:
  1555                     else:
  1488                         self.logger.write_warning(_("Debug: Unknown variable '%s'\n") % IECPath)
  1556                         self.logger.write_warning(
       
  1557                             _("Debug: Unknown variable '%s'\n") % IECPath)
  1489             for IECPathToPop in IECPathsToPop:
  1558             for IECPathToPop in IECPathsToPop:
  1490                 self.IECdebug_datas.pop(IECPathToPop)
  1559                 self.IECdebug_datas.pop(IECPathToPop)
  1491 
  1560 
  1492             if Idxs:
  1561             if Idxs:
  1493                 Idxs.sort()
  1562                 Idxs.sort()
  1510         # Prevent to call RegisterDebugVarToConnector when PLC is not started
  1579         # Prevent to call RegisterDebugVarToConnector when PLC is not started
  1511         # 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
  1512         # Links between PLC located variables and real variables are not ready
  1581         # Links between PLC located variables and real variables are not ready
  1513         if self.IsPLCStarted():
  1582         if self.IsPLCStarted():
  1514             # Timer to prevent rapid-fire when registering many variables
  1583             # Timer to prevent rapid-fire when registering many variables
  1515             # 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
  1516             self.DebugTimer = Timer(0.5, wx.CallAfter, args=[self.RegisterDebugVarToConnector])
  1585             # instead
       
  1586             self.DebugTimer = Timer(
       
  1587                 0.5, wx.CallAfter, args=[self.RegisterDebugVarToConnector])
  1517             # Rearm anti-rapid-fire timer
  1588             # Rearm anti-rapid-fire timer
  1518             self.DebugTimer.start()
  1589             self.DebugTimer.start()
  1519 
  1590 
  1520     def GetDebugIECVariableType(self, IECPath):
  1591     def GetDebugIECVariableType(self, IECPath):
  1521         _Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None))
  1592         _Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None))
  1615         debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers()
  1686         debug_ticks, buffers = self.SnapshotAndResetDebugValuesBuffers()
  1616         start_time = time.time()
  1687         start_time = time.time()
  1617         if len(self.TracedIECPath) == len(buffers):
  1688         if len(self.TracedIECPath) == len(buffers):
  1618             for IECPath, values in izip(self.TracedIECPath, buffers):
  1689             for IECPath, values in izip(self.TracedIECPath, buffers):
  1619                 if len(values) > 0:
  1690                 if len(values) > 0:
  1620                     self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values)
  1691                     self.CallWeakcallables(
       
  1692                         IECPath, "NewValues", debug_ticks, values)
  1621             if len(debug_ticks) > 0:
  1693             if len(debug_ticks) > 0:
  1622                 self.CallWeakcallables("__tick__", "NewDataAvailable", debug_ticks)
  1694                 self.CallWeakcallables(
       
  1695                     "__tick__", "NewDataAvailable", debug_ticks)
  1623 
  1696 
  1624         delay = time.time() - start_time
  1697         delay = time.time() - start_time
  1625         next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay)
  1698         next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay)
  1626         if self.DispatchDebugValuesTimer is not None:
  1699         if self.DispatchDebugValuesTimer is not None:
  1627             self.DispatchDebugValuesTimer.Start(
  1700             self.DispatchDebugValuesTimer.Start(
  1682                 wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1755                 wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1683 
  1756 
  1684     def _Connect(self):
  1757     def _Connect(self):
  1685         # don't accept re-connetion if already connected
  1758         # don't accept re-connetion if already connected
  1686         if self._connector is not None:
  1759         if self._connector is not None:
  1687             self.logger.write_error(_("Already connected. Please disconnect\n"))
  1760             self.logger.write_error(
       
  1761                 _("Already connected. Please disconnect\n"))
  1688             return
  1762             return
  1689 
  1763 
  1690         # Get connector uri
  1764         # Get connector uri
  1691         uri = self.BeremizRoot.getURI_location().strip()
  1765         uri = self.BeremizRoot.getURI_location().strip()
  1692 
  1766 
  1720 
  1794 
  1721         # Get connector from uri
  1795         # Get connector from uri
  1722         try:
  1796         try:
  1723             self._SetConnector(connectors.ConnectorFactory(uri, self))
  1797             self._SetConnector(connectors.ConnectorFactory(uri, self))
  1724         except Exception:
  1798         except Exception:
  1725             self.logger.write_error(_("Exception while connecting %s!\n") % uri)
  1799             self.logger.write_error(
       
  1800                 _("Exception while connecting %s!\n") % uri)
  1726             self.logger.write_error(traceback.format_exc())
  1801             self.logger.write_error(traceback.format_exc())
  1727 
  1802 
  1728         # Did connection success ?
  1803         # Did connection success ?
  1729         if self._connector is None:
  1804         if self._connector is None:
  1730             # Oups.
  1805             # Oups.
  1737             if self.previous_plcstate in ["Started", "Stopped"]:
  1812             if self.previous_plcstate in ["Started", "Stopped"]:
  1738                 if self.DebugAvailable() and self.GetIECProgramsAndVariables():
  1813                 if self.DebugAvailable() and self.GetIECProgramsAndVariables():
  1739                     self.logger.write(_("Debugger ready\n"))
  1814                     self.logger.write(_("Debugger ready\n"))
  1740                     self._connect_debug()
  1815                     self._connect_debug()
  1741                 else:
  1816                 else:
  1742                     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"))
  1743 
  1819 
  1744     def CompareLocalAndRemotePLC(self):
  1820     def CompareLocalAndRemotePLC(self):
  1745         if self._connector is None:
  1821         if self._connector is None:
  1746             return
  1822             return
  1747         # We are now connected. Update button status
  1823         # We are now connected. Update button status
  1748         MD5 = self.GetLastBuildMD5()
  1824         MD5 = self.GetLastBuildMD5()
  1749         # Check remote target PLC correspondance to that md5
  1825         # Check remote target PLC correspondance to that md5
  1750         if MD5 is not None:
  1826         if MD5 is not None:
  1751             if not self._connector.MatchMD5(MD5):
  1827             if not self._connector.MatchMD5(MD5):
  1752                 # self.logger.write_warning(
  1828                 # self.logger.write_warning(
  1753                 #     _("Latest build does not match with target, please transfer.\n"))
  1829                 # _("Latest build does not match with target, please
       
  1830                 # transfer.\n"))
  1754                 self.EnableMethod("_Transfer", True)
  1831                 self.EnableMethod("_Transfer", True)
  1755             else:
  1832             else:
  1756                 # self.logger.write(
  1833                 # self.logger.write(
  1757                 #     _("Latest build matches target, no transfer needed.\n"))
  1834                 #     _("Latest build matches target, no transfer needed.\n"))
  1758                 self.EnableMethod("_Transfer", True)
  1835                 self.EnableMethod("_Transfer", True)
  1781         # Get the last build PLC's
  1858         # Get the last build PLC's
  1782         MD5 = self.GetLastBuildMD5()
  1859         MD5 = self.GetLastBuildMD5()
  1783 
  1860 
  1784         # Check if md5 file is empty : ask user to build PLC
  1861         # Check if md5 file is empty : ask user to build PLC
  1785         if MD5 is None:
  1862         if MD5 is None:
  1786             self.logger.write_error(_("Failed : Must build before transfer.\n"))
  1863             self.logger.write_error(
       
  1864                 _("Failed : Must build before transfer.\n"))
  1787             return False
  1865             return False
  1788 
  1866 
  1789         # Compare PLC project with PLC on target
  1867         # Compare PLC project with PLC on target
  1790         if self._connector.MatchMD5(MD5):
  1868         if self._connector.MatchMD5(MD5):
  1791             self.logger.write(
  1869             self.logger.write(
  1816                     self.AppFrame.LogViewer.ResetLogCounters()
  1894                     self.AppFrame.LogViewer.ResetLogCounters()
  1817                 else:
  1895                 else:
  1818                     self.logger.write_error(_("Transfer failed\n"))
  1896                     self.logger.write_error(_("Transfer failed\n"))
  1819                 self.HidePLCProgress()
  1897                 self.HidePLCProgress()
  1820             else:
  1898             else:
  1821                 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"))
  1822 
  1901 
  1823         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1902         wx.CallAfter(self.UpdateMethodsFromPLCStatus)
  1824 
  1903 
  1825     StatusMethods = [
  1904     StatusMethods = [
  1826         {
  1905         {