--- 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 @@ -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. - Managing project directory
- 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(['<xsd:attribute name='+
'"Enable_'+ libname + '_Library" '+
- 'type="xsd:boolean" use="optional" default="true"/>'
+ 'type="xsd:boolean" use="optional" default="true"/>' for libname,lib in features.libraries])+"""
</xsd:complexType>""") if len(features.libraries)>0 else '<xsd:complexType/>') + """
@@ -104,7 +104,7 @@
EditorType = ProjectNodeEditor
def __init__(self, frame, logger):
PLCControler.__init__(self)
@@ -115,10 +115,10 @@
self.DebugValuesBuffers = []
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_lock = Lock()
@@ -147,7 +147,7 @@
@@ -156,7 +156,7 @@
Lib = GetClassImporter(clsname)()(self, libname, TypeStack)
TypeStack.append(Lib.GetTypes())
self.Libraries.append(Lib)
def SetAppFrame(self, frame, logger):
@@ -164,23 +164,23 @@
if self.DispatchDebugValuesTimer is not None:
self.DispatchDebugValuesTimer.Stop()
self.DispatchDebugValuesTimer = 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)
@@ -205,16 +205,16 @@
def GetCurrentLocation(self):
def GetCurrentName(self):
def _GetCurrentName(self):
@@ -223,10 +223,10 @@
def GetProjectName(self):
return os.path.split(self.ProjectPath)[1]
def GetDefaultTargetName(self):
if wx.Platform == '__WXMSW__':
@@ -242,15 +242,15 @@
target_name = self.GetDefaultTargetName()
target.setcontent(self.Parser.CreateElement(target_name, "TargetType"))
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"))
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)
# helper func to check project path write permission
def CheckProjectPathPerm(self, dosave=True):
if CheckPathPerm(self.ProjectPath):
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 ?'),
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
answer = dialog.ShowModal()
@@ -277,7 +277,7 @@
self.AppFrame.RefreshPageTitles()
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)
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!")
{"projectName": _("Unnamed"),
@@ -308,7 +308,7 @@
"companyName": _("Unknown"),
"creationDateTime": datetime(*localtime()[:6])})
self.AddProjectDefaultConfiguration()
# Change XSD into class members
@@ -320,7 +320,7 @@
# this will create files base XML files
def LoadProject(self, ProjectPath, BuildPath=None):
Load a project contained in a folder
@@ -357,30 +357,30 @@
#Load and init all the children
self.RefreshConfNodesBlockLists()
if os.path.exists(self._getBuildPath()):
self.EnableMethod("_Clean", True)
if os.path.isfile(self._getIECcodepath()):
self.ShowMethod("_showIECcode", True)
self.UpdateMethodsFromPLCStatus()
def RecursiveConfNodeInfos(self, confnode):
for CTNChild in confnode.IECSortedChildren():
{"name": "%s: %s" % (CTNChild.GetFullIEC_Channel(),
"tagname": CTNChild.CTNFullName(),
"icon": CTNChild.GetIconName(),
"values": self.RecursiveConfNodeInfos(CTNChild)})
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))
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)
self.logger.write_error(result)
# Ask user to choose a path with write permissions
if wx.Platform == '__WXMSW__':
@@ -444,12 +444,12 @@
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])
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 os.path.split(__file__)[0]
def CTNPath(self, CTNName=None):
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
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")
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<IEC_TYPE>[A-Z]*),(?P<NAME>[_A-Za-z0-9]*),(?P<DIR>[QMI])(?:,(?P<SIZE>[XBWDL]))?,(?P<LOC>[,0-9]*)\)")
result = LOCATED_MODEL.match(line)
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 # finally store into located variable list
locations.append(resdict)
def GetConfNodeGlobalInstances(self):
return self._GlobalInstances()
def _Generate_SoftPLC(self):
+ if self._Generate_PLC_ST(): + return self._Compile_ST_to_SoftPLC() + 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())
@@ -599,22 +603,25 @@
plc_file = open(self._getIECcodepath(), "a")
plc_file.write(open(self._getIECgeneratedcodepath(), "r").read())
+ 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(
"\"%s\" -f -I \"%s\" -T \"%s\" \"%s\""%(
no_stdout=True, no_stderr=True).spin()
# 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 @@
first_line, first_column, last_line, last_column, error = m_result.groups()
first_line, last_line = int(first_line), int(last_line)
f = open(self._getIECcodepath())
@@ -640,10 +647,10 @@
self.logger.write_warning("%04d: %s" % (i, line))
self.logger.write_error(_("Error : IEC to C compiler returned %d\n")%status)
# 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 self._builder is None or not isinstance(self._builder,targetclass):
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 ], False) # do not expose retreive/publish calls
def ResetIECProgramsAndVariables(self):
Reset variable and program list that are parsed from
@@ -743,7 +750,7 @@
for line in open(csvfile,'r').xreadlines():
@@ -754,7 +761,7 @@
elif len(strippedline) > 0 and len(ListGroup) > 0:
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
for line in ListGroup[1]:
@@ -776,7 +783,7 @@
parts = [config_FB] + parts[2:]
attrs["C_path"] = '.'.join(parts)
attrs["C_path"] = '__'.join(parts[1:])
attrs["C_path"] = '__'.join(parts)
@@ -788,11 +795,11 @@
IEC_path=attrs["IEC_path"]
self._IECPathToIdx[IEC_path]=(Idx, attrs["type"])
# third section contains ticktime
- self._Ticktime = int(ListGroup[2][0])
+ self._Ticktime = int(ListGroup[2][0]) 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 ])}
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")
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.
- self.logger.write_error(_("IEC-61131-3 code generation failed !\n"))
+ self.logger.write_error(_("PLC code generation failed !\n")) # 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(): + # Get current or fresh builder + builder = self.GetBuilder() + self.logger.write_error(_("Fatal : cannot get builder.\n")) + if not builder.build() : + self.logger.write_error(_("C Build failed.\n")) + self.logger.write_error(_("C Build crashed !\n")) + self.logger.write_error(traceback.format_exc()) + self.logger.write(_("Successfully built.\n")) + # Update GUI status about need for transfer + self.CompareLocalAndRemotePLC() + def _Generate_runtime(self): + buildpath = self._getBuildPath() # Generate C code and compilation params from confnode hierarchy
CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C(
self.PLCGeneratedLocatedVars)
- 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())
@@ -931,7 +969,7 @@
LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(buildpath)
- 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())
@@ -939,7 +977,7 @@
self.LocationCFilesAndCFLAGS = CTNLocationCFilesAndCFLAGS + LibCFilesAndCFLAGS
self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS
ExtraFiles = CTNExtraFiles + LibExtraFiles
# Get temporary directory path
extrafilespath = self._getExtraFilesPath()
@@ -953,7 +991,7 @@
open(fpath, "wb").write(fobject.read())
# Now we can forget ExtraFiles (will close files object)
# 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.logger.write(_("C code generated successfully.\n"))
- # Get current or fresh builder
- builder = self.GetBuilder()
- self.logger.write_error(_("Fatal : cannot get builder.\n"))
- if not builder.build() :
- self.logger.write_error(_("C Build failed.\n"))
- self.logger.write_error(_("C Build crashed !\n"))
- self.logger.write_error(traceback.format_exc())
- self.logger.write(_("Successfully built.\n"))
- # Update GUI status about need for transfer
- self.CompareLocalAndRemotePLC()
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)
self._OpenView("IEC code")
@@ -1019,20 +1034,20 @@
def _editIECrawcode(self):
self._OpenView("IEC raw code")
def _OpenProjectFiles(self):
self._OpenView("Project Files")
def _OpenFileEditor(self, filepath):
def _OpenView(self, name=None, onlyopened=False):
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)
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)
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])
editor_name = editors.keys()[0]
- 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()]
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)
return ConfigTreeNode._OpenView(self, self.CTNName(), onlyopened)
@@ -1147,7 +1162,7 @@
if self.AppFrame is not None:
self.AppFrame.LogViewer.SetLogCounters(log_count)
def UpdateMethodsFromPLCStatus(self):
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, []
def RegisterDebugVarToConnector(self):
@@ -1208,10 +1223,10 @@
# This variable is not needed anymore!
IECPathsToPop.append(IECPath)
elif IECPath != "__tick__":
Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None))
- if IEC_Type in DebugTypesSize:
+ if IEC_Type in DebugTypesSize: Idxs.append((Idx, IEC_Type, fvalue, IECPath))
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()
return self.previous_plcstate == "Started"
def ReArmDebugRegisterTimer(self):
if self.DebugTimer is not None:
# 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))
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):
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
IECdebug_data[4] |= buffer_list
IECdebug_data[0][callableobj]=(buffer_list, args, kwargs)
self.IECdebug_lock.release()
self.ReArmDebugRegisterTimer()
def UnsubscribeDebugIECVariable(self, IECPath, callableobj):
@@ -1292,7 +1307,7 @@
IECdebug_data[4] = reduce(
- [buffer_list for buffer_list,args,kwargs
+ [buffer_list for buffer_list,args,kwargs in IECdebug_data[0].itervalues()],
self.IECdebug_lock.release()
@@ -1309,33 +1324,33 @@
def ForceDebugIECVariable(self, IECPath, fvalue):
if not self.IECdebug_datas.has_key(IECPath):
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):
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"
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)
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
self.AppFrame.ResetGraphicViewers()
@@ -1448,7 +1463,7 @@
if self.DebugThread is None:
self.DebugThread = Thread(target=self.DebugThreadProc)
@@ -1460,7 +1475,7 @@
self.logger.write_error(_("Couldn't start PLC !\n"))
wx.CallAfter(self.UpdateMethodsFromPLCStatus)
@@ -1470,7 +1485,7 @@
# debugthread should die on his own
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"))
@@ -1512,7 +1527,7 @@
self.logger.write_error(_("Local service discovery failed!\n"))
self.logger.write_error(traceback.format_exc())
# 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()
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 @@
#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 @@
- # 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 self.logger.write_error(_("Failed : Must build before transfer.\n"))
@@ -1609,12 +1624,12 @@
for extrafilespath in [self._getExtraFilesPath(),
self._getProjectFilesPath()]:
- [(name, open(os.path.join(extrafilespath, name),
+ [(name, open(os.path.join(extrafilespath, name), for name in os.listdir(extrafilespath)])
builder = self.GetBuilder()