diff -r 82db84fe88ea -r cf3d2b53dd68 ProjectController.py --- a/ProjectController.py Tue May 13 00:43:06 2014 +0200 +++ b/ProjectController.py Wed May 21 18:43:54 2014 +0200 @@ -1,5 +1,5 @@ """ -Beremiz Project Controller +Beremiz Project Controller """ import os,sys,traceback import time @@ -63,14 +63,14 @@ class ProjectController(ConfigTreeNode, PLCControler): """ - This class define Root object of the confnode tree. + This class define Root object of the confnode tree. It is responsible of : - Managing project directory - Building project - Handling PLCOpenEditor controler and view - Loading user confnodes and instanciante them as children - ... - + """ # For root object, available Children Types are modules of the confnode packages. @@ -92,7 +92,7 @@ """+"\n".join(['' + 'type="xsd:boolean" use="optional" default="true"/>' for libname,lib in features.libraries])+""" """) if len(features.libraries)>0 else '') + """ @@ -104,7 +104,7 @@ """ EditorType = ProjectNodeEditor - + def __init__(self, frame, logger): PLCControler.__init__(self) @@ -115,10 +115,10 @@ self.DebugValuesBuffers = [] self.DebugTicks = [] self.SetAppFrame(frame, logger) - + self.iec2c_path = os.path.join(base_folder, "matiec", "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else "")) self.ieclib_path = os.path.join(base_folder, "matiec", "lib") - + # Setup debug information self.IECdebug_datas = {} self.IECdebug_lock = Lock() @@ -147,7 +147,7 @@ if self.DebugTimer: self.DebugTimer.cancel() self.KillDebugThread() - + def LoadLibraries(self): self.Libraries = [] TypeStack=[] @@ -156,7 +156,7 @@ Lib = GetClassImporter(clsname)()(self, libname, TypeStack) TypeStack.append(Lib.GetTypes()) self.Libraries.append(Lib) - + def SetAppFrame(self, frame, logger): self.AppFrame = frame self.logger = logger @@ -164,23 +164,23 @@ if self.DispatchDebugValuesTimer is not None: self.DispatchDebugValuesTimer.Stop() self.DispatchDebugValuesTimer = None - + if frame is not None: - + # Timer to pull PLC status self.StatusTimer = wx.Timer(self.AppFrame, -1) - self.AppFrame.Bind(wx.EVT_TIMER, + self.AppFrame.Bind(wx.EVT_TIMER, self.PullPLCStatusProc, self.StatusTimer) if self._connector is not None: frame.LogViewer.SetLogSource(self._connector) self.StatusTimer.Start(milliseconds=500, oneShot=False) - + # Timer to dispatch debug values to consumers self.DispatchDebugValuesTimer = wx.Timer(self.AppFrame, -1) - self.AppFrame.Bind(wx.EVT_TIMER, + self.AppFrame.Bind(wx.EVT_TIMER, self.DispatchDebugValuesProc, self.DispatchDebugValuesTimer) - + self.RefreshConfNodesBlockLists() def ResetAppFrame(self, logger): @@ -188,7 +188,7 @@ self.AppFrame.Unbind(wx.EVT_TIMER, self.StatusTimer) self.StatusTimer = None self.AppFrame = None - + self.logger = logger def CTNName(self): @@ -205,16 +205,16 @@ def GetIECLibPath(self): return self.ieclib_path - + def GetIEC2cPath(self): return self.iec2c_path - + def GetCurrentLocation(self): return () def GetCurrentName(self): return "" - + def _GetCurrentName(self): return "" @@ -223,10 +223,10 @@ def GetProjectName(self): return os.path.split(self.ProjectPath)[1] - + def GetIconName(self): return "PROJECT" - + def GetDefaultTargetName(self): if wx.Platform == '__WXMSW__': return "Win32" @@ -242,15 +242,15 @@ target_name = self.GetDefaultTargetName() target.setcontent(self.Parser.CreateElement(target_name, "TargetType")) return target - + def GetParamsAttributes(self, path = None): params = ConfigTreeNode.GetParamsAttributes(self, path) if params[0]["name"] == "BeremizRoot": for child in params[0]["children"]: if child["name"] == "TargetType" and child["value"] == '': - child.update(self.GetTarget().getElementInfos("TargetType")) + child.update(self.GetTarget().getElementInfos("TargetType")) return params - + def SetParamsAttribute(self, path, value): if path.startswith("BeremizRoot.TargetType.") and self.BeremizRoot.getTargetType().getcontent() is None: self.BeremizRoot.setTargetType(self.GetTarget()) @@ -258,15 +258,15 @@ if path.startswith("BeremizRoot.Libraries."): wx.CallAfter(self.RefreshConfNodesBlockLists) return res - + # helper func to check project path write permission def CheckProjectPathPerm(self, dosave=True): if CheckPathPerm(self.ProjectPath): return True if self.AppFrame is not None: - dialog = wx.MessageDialog(self.AppFrame, + dialog = wx.MessageDialog(self.AppFrame, _('You must have permission to work on the project\nWork on a project copy ?'), - _('Error'), + _('Error'), wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) answer = dialog.ShowModal() dialog.Destroy() @@ -277,7 +277,7 @@ self.AppFrame.RefreshPageTitles() return True return False - + def _getProjectFilesPath(self, project_path=None): if project_path is not None: return os.path.join(project_path, "project_files") @@ -285,7 +285,7 @@ if not os.path.exists(projectfiles_path): os.mkdir(projectfiles_path) return projectfiles_path - + def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"): self.ProjectAddConfiguration(config_name) self.ProjectAddConfigurationResource(config_name, res_name) @@ -299,7 +299,7 @@ # Verify that chosen folder is empty if not os.path.isdir(ProjectPath) or len(os.listdir(ProjectPath)) > 0: return _("Chosen folder isn't empty. You can't use it for a new project!") - + # Create PLCOpen program self.CreateNewProject( {"projectName": _("Unnamed"), @@ -308,7 +308,7 @@ "companyName": _("Unknown"), "creationDateTime": datetime(*localtime()[:6])}) self.AddProjectDefaultConfiguration() - + # Change XSD into class members self._AddParamsMembers() self.Children = {} @@ -320,7 +320,7 @@ # this will create files base XML files self.SaveProject() return None - + def LoadProject(self, ProjectPath, BuildPath=None): """ Load a project contained in a folder @@ -357,30 +357,30 @@ #Load and init all the children self.LoadChildren() self.RefreshConfNodesBlockLists() - + if os.path.exists(self._getBuildPath()): self.EnableMethod("_Clean", True) if os.path.isfile(self._getIECcodepath()): self.ShowMethod("_showIECcode", True) - + self.UpdateMethodsFromPLCStatus() - + return None - + def RecursiveConfNodeInfos(self, confnode): values = [] for CTNChild in confnode.IECSortedChildren(): values.append( {"name": "%s: %s" % (CTNChild.GetFullIEC_Channel(), - CTNChild.CTNName()), + CTNChild.CTNName()), "tagname": CTNChild.CTNFullName(), - "type": ITEM_CONFNODE, + "type": ITEM_CONFNODE, "confnode": CTNChild, "icon": CTNChild.GetIconName(), "values": self.RecursiveConfNodeInfos(CTNChild)}) return values - + def GetProjectInfos(self): infos = PLCControler.GetProjectInfos(self) configurations = infos["values"].pop(-1) @@ -394,23 +394,23 @@ infos["values"].append(resources) infos["values"].extend(self.RecursiveConfNodeInfos(self)) return infos - + def CloseProject(self): self.ClearChildren() self.ResetAppFrame(None) - + def SaveProject(self, from_project_path=None): if self.CheckProjectPathPerm(False): if from_project_path is not None: old_projectfiles_path = self._getProjectFilesPath(from_project_path) if os.path.isdir(old_projectfiles_path): - shutil.copytree(old_projectfiles_path, + shutil.copytree(old_projectfiles_path, self._getProjectFilesPath(self.ProjectPath)) self.SaveXMLFile(os.path.join(self.ProjectPath, 'plc.xml')) result = self.CTNRequestSave(from_project_path) if result: self.logger.write_error(result) - + def SaveProjectAs(self): # Ask user to choose a path with write permissions if wx.Platform == '__WXMSW__': @@ -444,12 +444,12 @@ LocatedCCodeAndFlags=[] Extras=[] for lib in self.Libraries: - res=lib.Generate_C(buildpath,self._VariablesList,LibIECCflags) + res=lib.Generate_C(buildpath,self._VariablesList,LibIECCflags) LocatedCCodeAndFlags.append(res[:2]) if len(res)>2: Extras.extend(res[2:]) return map(list,zip(*LocatedCCodeAndFlags))+[tuple(Extras)] - + # Update PLCOpenEditor ConfNode Block types from loaded confnodes def RefreshConfNodesBlockLists(self): if getattr(self, "Children", None) is not None: @@ -458,7 +458,7 @@ if self.AppFrame is not None: self.AppFrame.RefreshLibraryPanel() self.AppFrame.RefreshEditor() - + # Update a PLCOpenEditor Pou variable location def UpdateProjectVariableLocation(self, old_leading, new_leading): self.Project.updateElementAddress(old_leading, new_leading) @@ -469,13 +469,13 @@ self.AppFrame.RefreshFileMenu() self.AppFrame.RefreshEditMenu() wx.CallAfter(self.AppFrame.RefreshEditor) - + def GetVariableLocationTree(self): ''' This function is meant to be overridden by confnodes. It should returns an list of dictionaries - + - IEC_type is an IEC type like BOOL/BYTE/SINT/... - location is a string of this variable's location, like "%IX0.0.0" ''' @@ -483,13 +483,13 @@ for child in self.IECSortedChildren(): children.append(child.GetVariableLocationTree()) return children - + def ConfNodePath(self): return os.path.split(__file__)[0] - + def CTNPath(self, CTNName=None): return self.ProjectPath - + def ConfNodeXmlFilePath(self, CTNName=None): return os.path.join(self.CTNPath(CTNName), "beremiz.xml") @@ -515,26 +515,26 @@ # Create a build path in temp folder else: self.DefaultBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build") - + if not os.path.exists(self.DefaultBuildPath): os.makedirs(self.DefaultBuildPath) return self.DefaultBuildPath - + def _getExtraFilesPath(self): return os.path.join(self._getBuildPath(), "extra_files") def _getIECcodepath(self): # define name for IEC code file return os.path.join(self._getBuildPath(), "plc.st") - + def _getIECgeneratedcodepath(self): # define name for IEC generated code file return os.path.join(self._getBuildPath(), "generated_plc.st") - + def _getIECrawcodepath(self): # define name for IEC raw code file return os.path.join(self.CTNPath(), "raw_plc.st") - + def GetLocations(self): locations = [] filepath = os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h") @@ -546,24 +546,29 @@ # This regular expression parses the lines genereated by IEC2C LOCATED_MODEL = re.compile("__LOCATED_VAR\((?P[A-Z]*),(?P[_A-Za-z0-9]*),(?P[QMI])(?:,(?P[XBWDL]))?,(?P[,0-9]*)\)") for line in lines: - # If line match RE, + # If line match RE, result = LOCATED_MODEL.match(line) if result: # Get the resulting dict resdict = result.groupdict() # rewrite string for variadic location as a tuple of integers resdict['LOC'] = tuple(map(int,resdict['LOC'].split(','))) - # set located size to 'X' if not given + # set located size to 'X' if not given if not resdict['SIZE']: resdict['SIZE'] = 'X' # finally store into located variable list locations.append(resdict) return locations - + def GetConfNodeGlobalInstances(self): return self._GlobalInstances() - + def _Generate_SoftPLC(self): + if self._Generate_PLC_ST(): + return self._Compile_ST_to_SoftPLC() + return False + + def _Generate_PLC_ST(self): """ Generate SoftPLC ST/IL/SFC code out of PLCOpenEditor controller, and compile it with IEC2C @param buildpath: path where files should be created @@ -571,9 +576,8 @@ # Update PLCOpenEditor ConfNode Block types before generate ST code self.RefreshConfNodesBlockLists() - + self.logger.write(_("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n")) - buildpath = self._getBuildPath() # ask PLCOpenEditor controller to write ST/IL/SFC code file program, errors, warnings = self.GenerateProgram(self._getIECgeneratedcodepath()) if len(warnings) > 0: @@ -599,22 +603,25 @@ plc_file = open(self._getIECcodepath(), "a") plc_file.write(open(self._getIECgeneratedcodepath(), "r").read()) plc_file.close() - + return True + + def _Compile_ST_to_SoftPLC(self): self.logger.write(_("Compiling IEC Program into C code...\n")) + buildpath = self._getBuildPath() # Now compile IEC code into many C files - # files are listed to stdout, and errors to stderr. + # files are listed to stdout, and errors to stderr. status, result, err_result = ProcessLogger( self.logger, "\"%s\" -f -I \"%s\" -T \"%s\" \"%s\""%( self.iec2c_path, - self.ieclib_path, + self.ieclib_path, buildpath, self._getIECcodepath()), no_stdout=True, no_stderr=True).spin() if status: # Failed ! - + # parse iec2c's error message. if it contains a line number, # then print those lines from the generated IEC file. for err_line in err_result.split('\n'): @@ -624,7 +631,7 @@ if m_result is not None: first_line, first_column, last_line, last_column, error = m_result.groups() first_line, last_line = int(first_line), int(last_line) - + last_section = None f = open(self._getIECcodepath()) @@ -640,10 +647,10 @@ self.logger.write_warning("%04d: %s" % (i, line)) f.close() - + self.logger.write_error(_("Error : IEC to C compiler returned %d\n")%status) return False - + # Now extract C files of stdout C_files = [ fname for fname in result.splitlines() if fname[-2:]==".c" or fname[-2:]==".C" ] # remove those that are not to be compiled because included by others @@ -679,7 +686,7 @@ targetname = self.GetTarget().getcontent().getLocalTag() targetclass = targets.GetBuilder(targetname) - # if target already + # if target already if self._builder is None or not isinstance(self._builder,targetclass): # Get classname instance self._builder = targetclass(self) @@ -703,20 +710,20 @@ # C CODE GENERATION METHODS # ####################################################################### - + def CTNGenerate_C(self, buildpath, locations): """ - Return C code generated by iec2c compiler + Return C code generated by iec2c compiler when _generate_softPLC have been called @param locations: ignored @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND """ - return ([(C_file_name, self.plcCFLAGS) - for C_file_name in self.PLCGeneratedCFiles ], + return ([(C_file_name, self.plcCFLAGS) + for C_file_name in self.PLCGeneratedCFiles ], "", # no ldflags False) # do not expose retreive/publish calls - + def ResetIECProgramsAndVariables(self): """ Reset variable and program list that are parsed from @@ -743,7 +750,7 @@ self._ProgramList = [] self._VariablesList = [] self._IECPathToIdx = {} - + # Separate sections ListGroup = [] for line in open(csvfile,'r').xreadlines(): @@ -754,7 +761,7 @@ elif len(strippedline) > 0 and len(ListGroup) > 0: # append to this section ListGroup[-1].append(strippedline) - + # first section contains programs for line in ListGroup[0]: # Split and Maps each field to dictionnary entries @@ -763,7 +770,7 @@ attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:]) # Push this dictionnary into result. self._ProgramList.append(attrs) - + # second section contains all variables config_FBs = {} for line in ListGroup[1]: @@ -776,7 +783,7 @@ if config_FB: parts = [config_FB] + parts[2:] attrs["C_path"] = '.'.join(parts) - else: + else: attrs["C_path"] = '__'.join(parts[1:]) else: attrs["C_path"] = '__'.join(parts) @@ -788,11 +795,11 @@ IEC_path=attrs["IEC_path"] Idx=int(attrs["num"]) self._IECPathToIdx[IEC_path]=(Idx, attrs["type"]) - + # third section contains ticktime if len(ListGroup) > 2: - self._Ticktime = int(ListGroup[2][0]) - + self._Ticktime = int(ListGroup[2][0]) + except Exception,e: self.logger.write_error(_("Cannot open/parse VARIABLES.csv!\n")) self.logger.write_error(traceback.format_exc()) @@ -818,7 +825,7 @@ "MEM":"extern __IEC_%(type)s_p %(C_path)s;", "OUT":"extern __IEC_%(type)s_p %(C_path)s;", "VAR":"extern __IEC_%(type)s_t %(C_path)s;", - "FB":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v + "FB":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v for v in self._VariablesList if v["C_path"].find('.')<0]), "for_each_variable_do_code":"\n".join([ {"EXT":" (*fp)((void*)&(%(C_path)s),%(type)s_P_ENUM);\n", @@ -836,9 +843,9 @@ "OUT":" return %(type)s_O_ENUM;\n", "VAR":" return %(type)s_ENUM;\n"}[v["vartype"]]%v for v in self._VariablesList if v["vartype"] != "FB" and v["type"] in DebugTypesSize ])} - + return debug_code - + def Generate_plc_main(self): """ Use confnodes layout given in LocationCFilesAndCFLAGS to @@ -848,7 +855,7 @@ # in retreive, publish, init, cleanup locstrs = map(lambda x:"_".join(map(str,x)), [loc for loc,Cfiles,DoCalls in self.LocationCFilesAndCFLAGS if loc and DoCalls]) - + # Generate main, based on template if not self.BeremizRoot.getDisable_Extensions(): plc_main_code = targets.GetCode("plc_main_head") % { @@ -882,7 +889,7 @@ plc_main_code += targets.GetCode("plc_main_tail") return plc_main_code - + def _Build(self): """ Method called by user to (re)build SoftPLC and confnode tree @@ -890,7 +897,7 @@ if self.AppFrame is not None: self.AppFrame.ClearErrors() self._CloseView(self._IECCodeView) - + buildpath = self._getBuildPath() # Eventually create build dir @@ -908,21 +915,52 @@ # If IEC code gen fail, bail out. if not IECGenRes: - self.logger.write_error(_("IEC-61131-3 code generation failed !\n")) + self.logger.write_error(_("PLC code generation failed !\n")) self.ResetBuildMD5() return False # Reset variable and program list that are parsed from # CSV file generated by IEC2C compiler. self.ResetIECProgramsAndVariables() - + + # Collect platform specific C code + # Code and other files from extension + if not self._Generate_runtime(): + return False + + # Get current or fresh builder + builder = self.GetBuilder() + if builder is None: + self.logger.write_error(_("Fatal : cannot get builder.\n")) + self.ResetBuildMD5() + return False + + # Build + try: + if not builder.build() : + self.logger.write_error(_("C Build failed.\n")) + return False + except Exception, exc: + self.logger.write_error(_("C Build crashed !\n")) + self.logger.write_error(traceback.format_exc()) + self.ResetBuildMD5() + return False + + self.logger.write(_("Successfully built.\n")) + # Update GUI status about need for transfer + self.CompareLocalAndRemotePLC() + return True + + def _Generate_runtime(self): + buildpath = self._getBuildPath() + # Generate C code and compilation params from confnode hierarchy try: CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C( - buildpath, + buildpath, self.PLCGeneratedLocatedVars) except Exception, exc: - self.logger.write_error(_("Runtime extensions C code generation failed !\n")) + self.logger.write_error(_("Runtime IO extensions C code generation failed !\n")) self.logger.write_error(traceback.format_exc()) self.ResetBuildMD5() return False @@ -931,7 +969,7 @@ try: LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(buildpath) except Exception, exc: - self.logger.write_error(_("Runtime extensions C code generation failed !\n")) + self.logger.write_error(_("Runtime library extensions C code generation failed !\n")) self.logger.write_error(traceback.format_exc()) self.ResetBuildMD5() return False @@ -939,7 +977,7 @@ self.LocationCFilesAndCFLAGS = CTNLocationCFilesAndCFLAGS + LibCFilesAndCFLAGS self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS ExtraFiles = CTNExtraFiles + LibExtraFiles - + # Get temporary directory path extrafilespath = self._getExtraFilesPath() # Remove old directory @@ -953,7 +991,7 @@ open(fpath, "wb").write(fobject.read()) # Now we can forget ExtraFiles (will close files object) del ExtraFiles - + # Header file for extensions open(os.path.join(buildpath,"beremiz.h"), "w").write(targets.GetHeader()) @@ -978,32 +1016,9 @@ self.logger.write_error(traceback.format_exc()) self.ResetBuildMD5() return False - self.logger.write(_("C code generated successfully.\n")) - - # Get current or fresh builder - builder = self.GetBuilder() - if builder is None: - self.logger.write_error(_("Fatal : cannot get builder.\n")) - self.ResetBuildMD5() - return False - - # Build - try: - if not builder.build() : - self.logger.write_error(_("C Build failed.\n")) - return False - except Exception, exc: - self.logger.write_error(_("C Build crashed !\n")) - self.logger.write_error(traceback.format_exc()) - self.ResetBuildMD5() - return False - - self.logger.write(_("Successfully built.\n")) - # Update GUI status about need for transfer - self.CompareLocalAndRemotePLC() return True - + def ShowError(self, logger, from_location, to_location): chunk_infos = self.GetChunkInfos(from_location, to_location) for infos, (start_row, start_col) in chunk_infos: @@ -1011,7 +1026,7 @@ end = (to_location[0] - start_row, to_location[1] - start_col) if self.AppFrame is not None: self.AppFrame.ShowError(infos, start, end) - + _IECCodeView = None def _showIECcode(self): self._OpenView("IEC code") @@ -1019,20 +1034,20 @@ _IECRawCodeView = None def _editIECrawcode(self): self._OpenView("IEC raw code") - + _ProjectFilesView = None def _OpenProjectFiles(self): self._OpenView("Project Files") - + _FileEditors = {} def _OpenFileEditor(self, filepath): self._OpenView(filepath) - + def _OpenView(self, name=None, onlyopened=False): if name == "IEC code": if self._IECCodeView is None: plc_file = self._getIECcodepath() - + self._IECCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, None, instancepath=name) self._IECCodeView.SetTextSyntax("ALL") self._IECCodeView.SetKeywords(IEC_KEYWORDS) @@ -1043,78 +1058,78 @@ self._IECCodeView.SetText(text = text) self._IECCodeView.SetIcon(GetBitmap("ST")) setattr(self._IECCodeView, "_OnClose", self.OnCloseEditor) - + if self._IECCodeView is not None: self.AppFrame.EditProjectElement(self._IECCodeView, name) - + return self._IECCodeView - + elif name == "IEC raw code": if self._IECRawCodeView is None: controler = MiniTextControler(self._getIECrawcodepath(), self) - + self._IECRawCodeView = IECCodeViewer(self.AppFrame.TabsOpened, "", self.AppFrame, controler, instancepath=name) self._IECRawCodeView.SetTextSyntax("ALL") self._IECRawCodeView.SetKeywords(IEC_KEYWORDS) self._IECRawCodeView.RefreshView() self._IECRawCodeView.SetIcon(GetBitmap("ST")) setattr(self._IECRawCodeView, "_OnClose", self.OnCloseEditor) - + if self._IECRawCodeView is not None: self.AppFrame.EditProjectElement(self._IECRawCodeView, name) - + return self._IECRawCodeView - + elif name == "Project Files": if self._ProjectFilesView is None: self._ProjectFilesView = FileManagementPanel(self.AppFrame.TabsOpened, self, name, self._getProjectFilesPath(), True) - + extensions = [] for extension, name, editor in features.file_editors: if extension not in extensions: extensions.append(extension) - self._ProjectFilesView.SetEditableFileExtensions(extensions) - + self._ProjectFilesView.SetEditableFileExtensions(extensions) + if self._ProjectFilesView is not None: self.AppFrame.EditProjectElement(self._ProjectFilesView, name) - + return self._ProjectFilesView - + elif name is not None and name.find("::") != -1: filepath, editor_name = name.split("::") if not self._FileEditors.has_key(filepath): if os.path.isfile(filepath): file_extension = os.path.splitext(filepath)[1] - + editors = dict([(edit_name, edit_class) for extension, edit_name, edit_class in features.file_editors if extension == file_extension]) - + if editor_name == "": if len(editors) == 1: editor_name = editors.keys()[0] elif len(editors) > 0: names = editors.keys() - dialog = wx.SingleChoiceDialog(self.AppFrame, - _("Select an editor:"), _("Editor selection"), + dialog = wx.SingleChoiceDialog(self.AppFrame, + _("Select an editor:"), _("Editor selection"), names, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) if dialog.ShowModal() == wx.ID_OK: editor_name = names[dialog.GetSelection()] dialog.Destroy() - + if editor_name != "": name = "::".join([filepath, editor_name]) - + editor = editors[editor_name]() self._FileEditors[filepath] = editor(self.AppFrame.TabsOpened, self, name, self.AppFrame) self._FileEditors[filepath].SetIcon(GetBitmap("FILE")) if isinstance(self._FileEditors[filepath], DebugViewer): self._FileEditors[filepath].SetDataProducer(self) - + if self._FileEditors.has_key(filepath): editor = self._FileEditors[filepath] self.AppFrame.EditProjectElement(editor, editor.GetTagName()) - + return self._FileEditors.get(filepath) else: return ConfigTreeNode._OpenView(self, self.CTNName(), onlyopened) @@ -1147,7 +1162,7 @@ if log_count: if self.AppFrame is not None: self.AppFrame.LogViewer.SetLogCounters(log_count) - + def UpdateMethodsFromPLCStatus(self): status = None if self._connector is not None: @@ -1184,16 +1199,16 @@ self.AppFrame.ConnectionStatusBar.SetStatusText( _("Connected to URI: %s") % self.BeremizRoot.getURI_location().strip(), 1) self.AppFrame.ConnectionStatusBar.SetStatusText(_(status), 2) - + def PullPLCStatusProc(self, event): self.UpdateMethodsFromPLCStatus() - + def SnapshotAndResetDebugValuesBuffers(self): - buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers, + buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers, [list() for iec_path in self.TracedIECPath]) ticks, self.DebugTicks = self.DebugTicks, [] return ticks, buffers - + def RegisterDebugVarToConnector(self): self.DebugTimer=None Idxs = [] @@ -1208,10 +1223,10 @@ # This variable is not needed anymore! IECPathsToPop.append(IECPath) elif IECPath != "__tick__": - # Convert + # Convert Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) if Idx is not None: - if IEC_Type in DebugTypesSize: + if IEC_Type in DebugTypesSize: Idxs.append((Idx, IEC_Type, fvalue, IECPath)) else: self.logger.write_warning(_("Debug: Unsupported type to debug '%s'\n")%IEC_Type) @@ -1229,14 +1244,14 @@ self._connector.SetTraceVariablesList([]) self.SnapshotAndResetDebugValuesBuffers() self.IECdebug_lock.release() - + def IsPLCStarted(self): return self.previous_plcstate == "Started" - + def ReArmDebugRegisterTimer(self): if self.DebugTimer is not None: self.DebugTimer.cancel() - + # Prevent to call RegisterDebugVarToConnector when PLC is not started # If an output location var is forced it's leads to segmentation fault in runtime # Links between PLC located variables and real variables are not ready @@ -1250,16 +1265,16 @@ def GetDebugIECVariableType(self, IECPath): Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) return IEC_Type - + def SubscribeDebugIECVariable(self, IECPath, callableobj, buffer_list=False, *args, **kwargs): """ Dispatching use a dictionnary linking IEC variable paths - to a WeakKeyDictionary linking + to a WeakKeyDictionary linking weakly referenced callables to optionnal args """ if IECPath != "__tick__" and not self._IECPathToIdx.has_key(IECPath): return None - + self.IECdebug_lock.acquire() # If no entry exist, create a new one with a fresh WeakKeyDictionary IECdebug_data = self.IECdebug_datas.get(IECPath, None) @@ -1273,13 +1288,13 @@ self.IECdebug_datas[IECPath] = IECdebug_data else: IECdebug_data[4] |= buffer_list - + IECdebug_data[0][callableobj]=(buffer_list, args, kwargs) self.IECdebug_lock.release() - + self.ReArmDebugRegisterTimer() - + return IECdebug_data[1] def UnsubscribeDebugIECVariable(self, IECPath, callableobj): @@ -1292,7 +1307,7 @@ else: IECdebug_data[4] = reduce( lambda x, y: x|y, - [buffer_list for buffer_list,args,kwargs + [buffer_list for buffer_list,args,kwargs in IECdebug_data[0].itervalues()], False) self.IECdebug_lock.release() @@ -1309,33 +1324,33 @@ def ForceDebugIECVariable(self, IECPath, fvalue): if not self.IECdebug_datas.has_key(IECPath): return - + self.IECdebug_lock.acquire() - + # If no entry exist, create a new one with a fresh WeakKeyDictionary IECdebug_data = self.IECdebug_datas.get(IECPath, None) IECdebug_data[2] = "Forced" IECdebug_data[3] = fvalue - + self.IECdebug_lock.release() - + self.ReArmDebugRegisterTimer() - + def ReleaseDebugIECVariable(self, IECPath): if not self.IECdebug_datas.has_key(IECPath): return - + self.IECdebug_lock.acquire() - + # If no entry exist, create a new one with a fresh WeakKeyDictionary IECdebug_data = self.IECdebug_datas.get(IECPath, None) IECdebug_data[2] = "Registered" IECdebug_data[3] = None - + self.IECdebug_lock.release() - + self.ReArmDebugRegisterTimer() - + def CallWeakcallables(self, IECPath, function_name, *cargs): data_tuple = self.IECdebug_datas.get(IECPath, None) if data_tuple is not None: @@ -1415,7 +1430,7 @@ self.CallWeakcallables(IECPath, "NewValues", debug_ticks, values) if len(debug_ticks) > 0: self.CallWeakcallables("__tick__", "NewDataAvailable", debug_ticks) - + delay = time.time() - start_time next_refresh = max(REFRESH_PERIOD - delay, 0.2 * delay) if self.DispatchDebugValuesTimer is not None and self.DebugThread is not None: @@ -1437,7 +1452,7 @@ if self.DispatchDebugValuesTimer is not None: self.DispatchDebugValuesTimer.Stop() - def _connect_debug(self): + def _connect_debug(self): self.previous_plcstate = None if self.AppFrame: self.AppFrame.ResetGraphicViewers() @@ -1448,7 +1463,7 @@ if self.DebugThread is None: self.DebugThread = Thread(target=self.DebugThreadProc) self.DebugThread.start() - + def _Run(self): """ Start PLC @@ -1460,7 +1475,7 @@ else: self.logger.write_error(_("Couldn't start PLC !\n")) wx.CallAfter(self.UpdateMethodsFromPLCStatus) - + def _Stop(self): """ Stop PLC @@ -1470,7 +1485,7 @@ # debugthread should die on his own #self.KillDebugThread() - + wx.CallAfter(self.UpdateMethodsFromPLCStatus) def _SetConnector(self, connector, update_status=True): @@ -1493,7 +1508,7 @@ if self._connector is not None: self.logger.write_error(_("Already connected. Please disconnect\n")) return - + # Get connector uri uri = self.\ BeremizRoot.\ @@ -1512,7 +1527,7 @@ self.logger.write_error(_("Local service discovery failed!\n")) self.logger.write_error(traceback.format_exc()) uri = None - + # Nothing choosed or cancel button if uri is None or answer == wx.ID_CANCEL: self.logger.write_error(_("Connection canceled!\n")) @@ -1529,7 +1544,7 @@ self.AppFrame.RefreshFileMenu() self.AppFrame.RefreshEditMenu() self.AppFrame.RefreshPageTitles() - + # Get connector from uri try: self._SetConnector(connectors.ConnectorFactory(uri, self)) @@ -1547,7 +1562,7 @@ self.ShowMethod("_Transfer", True) self.CompareLocalAndRemotePLC() - + # Init with actual PLC status and print it self.UpdateMethodsFromPLCStatus() if self.previous_plcstate is not None: @@ -1556,7 +1571,7 @@ status = "" #self.logger.write(_("PLC is %s\n")%status) - + if self.previous_plcstate in ["Started","Stopped"]: if self.DebugAvailable() and self.GetIECProgramsAndVariables(): self.logger.write(_("Debugger ready\n")) @@ -1590,12 +1605,12 @@ def _Disconnect(self): self._SetConnector(None) - + def _Transfer(self): - # Get the last build PLC's + # Get the last build PLC's MD5 = self.GetLastBuildMD5() - - # Check if md5 file is empty : ask user to build PLC + + # Check if md5 file is empty : ask user to build PLC if MD5 is None : self.logger.write_error(_("Failed : Must build before transfer.\n")) return False @@ -1609,12 +1624,12 @@ extrafiles = [] for extrafilespath in [self._getExtraFilesPath(), self._getProjectFilesPath()]: - + extrafiles.extend( - [(name, open(os.path.join(extrafilespath, name), + [(name, open(os.path.join(extrafilespath, name), 'rb').read()) \ for name in os.listdir(extrafilespath)]) - + # Send PLC on target builder = self.GetBuilder() if builder is not None: