lpcmanager

Parents 1bcb42bc5783
Children 9780486de8d8
Moving code out of LPCManager, using hg copy, therefore cropping overlapping code.
--- a/LPCManager.py Mon Feb 05 11:24:11 2018 +0100
+++ b/LPCManager.py Mon Feb 05 13:02:21 2018 +0100
@@ -1034,933 +1034,8 @@
slave.CTNRequestSave()
# -------------------------------------------------------------------------------
-# LPCProjectController Class
-# -------------------------------------------------------------------------------
-def mycopytree(src, dst):
- """
- Copy content of a directory to an other, omit hidden files
- @param src: source directory
- @param dst: destination directory
- """
- for i in os.listdir(src):
- if not i.startswith('.'):
- srcpath = os.path.join(src, i)
- dstpath = os.path.join(dst, i)
- if os.path.isdir(srcpath):
- if os.path.exists(dstpath):
- shutil.rmtree(dstpath)
- os.makedirs(dstpath)
- mycopytree(srcpath, dstpath)
- elif os.path.isfile(srcpath):
- shutil.copy2(srcpath, dstpath)
-
-
-[SIMULATION_MODE, TRANSFER_MODE] = range(2)
-
-if arch in PLC_module:
- class LPCProjectNodeEditor(ProjectNodeEditor):
- pass
-else:
- class LPCProjectNodeEditor(ProjectNodeEditor):
- SHOW_PARAMS = False
- ENABLE_REQUIRED = False
-
-_StatusMethods = [
- {"bitmap": "Debug",
- "name": _("Simulate"),
- "tooltip": _("Simulate PLC"),
- "method": "_Simulate"},
- {"bitmap": "Run",
- "name": _("Run"),
- "shown": False,
- "tooltip": _("Start PLC"),
- "method": "_Run"},
- {"bitmap": "Stop",
- "name": _("Stop"),
- "shown": False,
- "tooltip": _("Stop Running PLC"),
- "method": "_Stop"},
- {"bitmap": "Build",
- "name": _("Build"),
- "tooltip": _("Build project into build folder"),
- "method": "_Build"},
- {"bitmap": "Clean",
- "name": _("Clean"),
- "enabled": False,
- "tooltip": _("Clean project build folder"),
- "method": "_Clean"},
- {"bitmap": "Transfer",
- "name": _("Transfer"),
- "shown": False,
- "tooltip": _("Transfer PLC"),
- "method": "_Transfer"},
-]
-_MethodFromPLCState = {
- "Started": [("_Simulate", False),
- ("_Run", False),
- ("_Stop", True),
- ("_Build", True),
- ("_Transfer", True)],
- "Stopped": [("_Simulate", False),
- ("_Run", True),
- ("_Stop", False),
- ("_Build", True),
- ("_Transfer", True)],
- "Connected": [("_Simulate", "not simulating"),
- ("_Run", True),
- ("_Stop", True if arch in PLC_module else "simulating"),
- ("_Build", True),
- ("_Transfer", True)],
- "Disconnected": [("_Simulate", "not simulating"),
- ("_Run", False),
- ("_Stop", False if arch in PLC_module else "simulating"),
- ("_Build", True),
- ("_Transfer", False)],
- "Empty" :[("_Simulate", "not simulating"),
- ("_Run", False),
- ("_Stop", False),
- ("_Build", True),
- ("_Transfer", True)],
-}
-
-if arch in PLC_module:
- _StatusMethods += [
- {"bitmap": "Connect",
- "name": _("Connect"),
- "shown": True,
- "tooltip": _("Connect to the target PLC"),
- "method": "_Connect"},
- {"bitmap": "Disconnect",
- "name": _("Disconnect"),
- "shown": False,
- "tooltip": _("Disconnect from PLC"),
- "method": "_Disconnect"},
- {"bitmap": "UpdateFw",
- "name": _("UpdateFw"),
- "shown": True,
- "tooltip": _("Update the PLC firmware"),
- "method": "_UpdateFw"},
- ]
- _MethodFromPLCState["Disconnected"] += [("_Connect", True),
- ("_Disconnect", False)]
-
- _MethodFromPLCState["Connected"] += [("_Connect", False),
- ("_Disconnect", True)]
-
-class LPCProjectController(ProjectController):
- StatusMethods = _StatusMethods
- # ConfNodeMethods = []
-
- EditorType = LPCProjectNodeEditor
-
- def __init__(self, frame, logger, buildpath):
- self.arch = arch
- self.OrigBuildPath = buildpath
-
- ProjectController.__init__(self, frame, logger)
-
- if havecanfestival:
- self.CTNChildrenTypes += [("LPCBus", LPCBus, "LPC bus"), ("CanOpen", LPCCanOpen, "CanOpen bus")]
- else:
- self.CTNChildrenTypes += [("LPCBus", LPCBus, "LPC bus")]
- self.CTNType = "LPC"
-
- self.OnlineMode = "NORMAL" if arch in PLC_module else "OFF"
- self.LPCConnector = None
- self.ConnectorPath = None
-
- self.CurrentMode = None
- self.previous_mode = None
-
- self.SimulationBuildPath = None
-
- self.AbortTransferTimer = None
-
- # Firmware update running status
- self.firmawreUpadateIsRunning = False
- self.building = False
-
-
- # Bind mouse double click event on URI_location in Beremiz
- self.UriOptions = True
-
- def GetProjectName(self):
- return self.Project.getname()
-
- def GetDefaultTargetName(self):
- if self.CurrentMode == SIMULATION_MODE:
- return ProjectController.GetDefaultTargetName(self)
- else:
- return "LPC"
-
- def GetTarget(self):
- target = ProjectController.GetTarget(self)
- if self.CurrentMode != SIMULATION_MODE and arch not in PLC_module:
- target.getcontent().setBuildPath(self.BuildPath)
- return target
-
- def _getBuildPath(self):
- if self.CurrentMode == SIMULATION_MODE:
- if self.SimulationBuildPath is None:
- self.SimulationBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build")
- return self.SimulationBuildPath
- else:
- return ProjectController._getBuildPath(self)
-
- def ToZIPFile(self):
- # MD5 = self.GetLastBuildMD5()
- try:
- path_export_file = self.BuildPath[:-5] + "\\" + self._builder.exe[:-3] + ".xEye"
- zf = zipfile.ZipFile(path_export_file, mode='w', compression=zipfile.ZIP_DEFLATED)
- for extrafilespath in [self._getExtraFilesPath(),
- self._getProjectFilesPath()]:
- dir = extrafilespath.split("\\")[-1]
- for name in os.listdir(extrafilespath):
- zf.write(extrafilespath + '\\' + str(name), dir + '\\' + str(name), zipfile.ZIP_DEFLATED)
- zf.write(self._builder.exe_path, self._builder.exe)
- zf.write(self.BuildPath + '\\lastbuildPLC.md5', 'lastbuildPLC.md5')
- zf.close()
- self.logger.write(_("Export file is successfully created on location: %s\n") % path_export_file)
- except Exception, e:
- self.logger.write(_("Export file is not created because eror: %s\n") % e)
-
- def _Build(self):
- self._Clean()
- save = self.ProjectTestModified()
- if save:
- self.SaveProject()
- self.AppFrame._Refresh(TITLE, FILEMENU)
- if self.BuildPath is not None:
- mycopytree(self.OrigBuildPath, self.BuildPath)
- if ProjectController._Build(self):
- self.ToZIPFile()
- if save:
- wx.CallAfter(self.AppFrame.RefreshAll)
-
- def SetProjectName(self, name):
- return self.Project.setname(name)
-
- def SetOnlineMode(self, mode, path=None):
- # SetOnlineMode is only for MC8
- if arch in PLC_module:
- return None
-
- mode = mode.upper()
- if self.OnlineMode != mode:
- if mode not in ["OFF", ""]:
- self.OnlineMode = mode
- self.ConnectorPath = path
- uri = "LPC://%s/%s" % (self.OnlineMode, path)
- try:
- self.LPCConnector = connectors.ConnectorFactory(uri, self)
- except Exception, msg:
- self.logger.write_error(_("Exception while connecting %s!\n") % uri)
- self.logger.write_error(traceback.format_exc())
-
- # Did connection success ?
- if self.LPCConnector is None:
- # Oups.
- self.logger.write_error(_("Connection failed to %s!\n") % uri)
-
- else:
- self.OnlineMode = "OFF"
- self.LPCConnector = None
- self.ConnectorPath = None
-
- self.ApplyOnlineMode()
-
-
- def ApplyOnlineMode(self):
- if self.CurrentMode != SIMULATION_MODE:
- self.KillDebugThread()
-
- self._SetConnector(self.LPCConnector)
-
- # Init with actual PLC status and print it
- self.UpdateMethodsFromPLCStatus()
-
- if self.LPCConnector is not None and self.OnlineMode == "APPLICATION":
-
- self.CompareLocalAndRemotePLC()
-
- if self.previous_plcstate is not None:
- status = _(self.previous_plcstate)
- else:
- status = ""
- self.logger.write(_("PLC is %s\n") % status)
-
- # if self.StatusTimer and not self.StatusTimer.IsRunning():
- # # Start the status Timer
- # self.StatusTimer.Start(milliseconds=2000, oneShot=False)
-
- if self.previous_plcstate == "Started":
- if self.DebugAvailable() and self.GetIECProgramsAndVariables():
- self.logger.write(_("Debug connect matching running PLC\n"))
- self._connect_debug()
- else:
- self.logger.write_warning(_("Debug do not match PLC - stop/transfert/start to re-enable\n"))
-
- elif self.StatusTimer and self.StatusTimer.IsRunning():
- self.StatusTimer.Stop()
-
- if self.CurrentMode == TRANSFER_MODE:
-
- if self.OnlineMode == "BOOTLOADER":
- self.BeginTransfer()
-
- elif self.OnlineMode == "APPLICATION":
- self.CurrentMode = None
- self.AbortTransferTimer.Stop()
- self.AbortTransferTimer = None
-
- self.logger.write(_("PLC transferred successfully\n"))
-
- # Update a PLCOpenEditor Pou variable location
- def UpdateProjectVariableLocation(self, old_leading, new_leading):
- self.Project.updateElementAddress(old_leading, new_leading)
- self.BufferProject()
-
- # Update a PLCOpenEditor Pou variable name
- def UpdateProjectVariableName(self, old_name, new_name):
- self.Project.updateElementName(old_name, new_name)
- self.BufferProject()
-
- def RemoveProjectVariableByAddress(self, address):
- self.Project.removeVariableByAddress(address)
- self.BufferProject()
-
- def RemoveProjectVariableByFilter(self, leading):
- self.Project.removeVariableByFilter(leading)
- self.BufferProject()
-
- def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"):
- ProjectController.AddProjectDefaultConfiguration(self, config_name, res_name)
-
- self.SetEditedResourceInfos(
- self.ComputeConfigurationResourceName(config_name, res_name),
- [{"Name": "main_task",
- "Triggering": "Cyclic",
- "Interval": "T#50ms",
- "Priority": 0}],
- [])
-
- def LoadProject(self, ProjectPath, BuildPath=None):
- """
- Load a project contained in a folder
- @param ProjectPath: path of the project folder
- """
- if os.path.basename(ProjectPath) == "":
- ProjectPath = os.path.dirname(ProjectPath)
-
- # Verify that project contains a PLCOpen program
- plc_file = os.path.join(ProjectPath, "plc.xml")
- if os.path.isfile(plc_file):
- # Load PLCOpen file
- result = self.OpenXMLFile(plc_file)
- if result:
- return result
- else:
- self.CreateNewProject({"companyName": "",
- "productName": "",
- "productVersion": "",
- "projectName": "",
- "pageSize": (0, 0),
- "scaling": {}})
- if len(self.GetProjectConfigNames()) == 0:
- self.AddProjectDefaultConfiguration()
-
- # Change XSD into class members
- self._AddParamsMembers()
- self.Children = {}
-
- # Keep track of the root confnode (i.e. project path)
- self.ProjectPath = ProjectPath
-
- self.BuildPath = self._getBuildPath()
- if self.OrigBuildPath is not None:
- mycopytree(self.OrigBuildPath, self.BuildPath)
-
- # If dir have already be made, and file exist
- if os.path.isdir(self.CTNPath()) and os.path.isfile(self.ConfNodeXmlFilePath()):
- # Load the confnode.xml file into parameters members
- result = self.LoadXMLParams()
- if result:
- return result
- # Load and init all the children
- self.LoadChildren()
-
- canopen_child = self.GetChildByName("CanOpen")
- if havecanfestival and canopen_child is None:
- canopen = self.CTNAddChild("CanOpen", "CanOpen", 0)
- canopen.LoadChildren()
- canopen.CTNRequestSave()
-
- elif not havecanfestival and canopen_child is not None:
- canopen_child.CTNRemove()
-
- if self.CTNTestModified():
- self.SaveProject()
-
- if wx.GetApp() is None:
- self.RefreshConfNodesBlockLists()
- else:
- wx.CallAfter(self.RefreshConfNodesBlockLists)
-
- if arch in PLC_module:
- self.SetParamsAttribute('BeremizRoot.TargetType', 'MC9')
-
- return None
-
- def IsPLCStarted(self):
- return self.previous_plcstate == "Started" or self.previous_mode == SIMULATION_MODE
-
- def ShowMethod(self, name, val):
- simulating = self.CurrentMode == SIMULATION_MODE
- if type(val) == str:
- if val.endswith("simulating"):
- if val.startswith("not"):
- val = not simulating
- else:
- val = simulating
-
- ProjectController.ShowMethod(self, name, val)
-
- def UpdateMethodsFromPLCStatus(self):
- simulating = self.CurrentMode == SIMULATION_MODE
- if self.OnlineMode == "OFF":
- if simulating:
- status, log_count = self._connector.GetPLCstatus()
- self.UpdatePLCLog(log_count)
- status = "Disconnected"
- elif self.OnlineMode == "BOOTLOADER":
- status = "Connected"
- else:
- if self._connector is not None:
- status, log_count = self._connector.GetPLCstatus()
- if status == "Disconnected":
- self._SetConnector(None, False)
- else:
- self.UpdatePLCLog(log_count)
- else:
- status = "Disconnected"
- if self.previous_plcstate != status or self.previous_mode != self.CurrentMode:
- for args in _MethodFromPLCState.get(status, []):
- self.ShowMethod(*args)
- self.previous_plcstate = status
- self.previous_mode = self.CurrentMode
- if self.AppFrame is not None:
- self.AppFrame.RefreshStatusToolBar()
- connection_text = _("Connected to: ")
- status_text = ""
- if simulating:
- connection_text += _("Simulation")
- status_text += _("ON")
- if status == "Disconnected":
- if not simulating:
- self.AppFrame.ConnectionStatusBar.SetStatusText(_(status), 1)
- self.AppFrame.ConnectionStatusBar.SetStatusText('', 2)
- else:
- self.AppFrame.ConnectionStatusBar.SetStatusText(connection_text, 1)
- self.AppFrame.ConnectionStatusBar.SetStatusText(status_text, 2)
- else:
- if simulating:
- connection_text += " (%s)"
- status_text += " (%s)"
- else:
- connection_text += "%s"
- status_text += "%s"
- self.AppFrame.ConnectionStatusBar.SetStatusText(connection_text % self.ConnectorPath, 1)
- self.AppFrame.ConnectionStatusBar.SetStatusText(status_text % _(status), 2)
- return True
- return False
-
- def Generate_plc_declare_locations(self):
- """
- Declare used locations in order to simulatePLC in a black box
- """
- return """#include "iec_types_all.h"
-
-#define __LOCATED_VAR(type, name, ...) \
-type beremiz_##name;\
-type *name = &beremiz_##name;
-
-#include "LOCATED_VARIABLES.h"
-
-#undef __LOCATED_VAR
-
-"""
-
- def Generate_lpc_retain_array_sim(self):
- """
- Support for retain array in Simulation
- """
- return """/* Support for retain array */
-#define USER_RETAIN_ARRAY_SIZE 2000
-#define NUM_OF_COLS 3
-unsigned char readOK = 0;
-unsigned int foundIndex = USER_RETAIN_ARRAY_SIZE;
-unsigned int retainArray[USER_RETAIN_ARRAY_SIZE][NUM_OF_COLS];
-
-unsigned int __GetRetainData(unsigned char READ, unsigned int INDEX, unsigned int COLUMN)
-{
- if(READ == 1)
- {
- if((0<=INDEX) && (INDEX<USER_RETAIN_ARRAY_SIZE) && (0<=COLUMN) && (COLUMN<NUM_OF_COLS))
- {
- readOK = 1;
- return retainArray[INDEX][COLUMN];
- }
- }
-
- readOK = 0;
- return 0;
-}
-
-unsigned char __SetRetainData(unsigned char WRITE, unsigned int INDEX, unsigned int WORD1, unsigned int WORD2, unsigned int WORD3)
-{
- if(WRITE == 1)
- {
- if((0<=INDEX) && (INDEX<USER_RETAIN_ARRAY_SIZE))
- {
- retainArray[INDEX][0] = WORD1;
- retainArray[INDEX][1] = WORD2;
- retainArray[INDEX][2] = WORD3;
- return 1;
- }
- }
-
- return 0;
-}
-
-unsigned char __FindRetainData(unsigned char SEARCH, unsigned int START_IDX, unsigned int END_IDX, unsigned int WORD1, unsigned int WORD2, unsigned int WORD3)
-{
- unsigned int i;
-
- if((SEARCH==1) && (0<=START_IDX) && (START_IDX<USER_RETAIN_ARRAY_SIZE) && (START_IDX<=END_IDX) && (END_IDX<USER_RETAIN_ARRAY_SIZE))
- {
- for(i=START_IDX;i<=END_IDX;i++)
- {
- if((retainArray[i][0] == WORD1) && (retainArray[i][1] == WORD2) && (retainArray[i][2] == WORD3))
- {
- foundIndex = i;
- return 1;
- }
- }
- }
-
- foundIndex = USER_RETAIN_ARRAY_SIZE; /* Data not found => return index that is out of array bounds */
- return 0;
-}
-
-/* Since Beremiz debugger doesn't like pointer-by-reference stuff or global varibles, separate function is a must */
-unsigned char __GetReadStatus(unsigned char dummy)
-{
- return readOK;
-}
-
-unsigned int __GetFoundIndex(unsigned char dummy)
-{
- return foundIndex;
-}
-"""
-
- def _Simulate(self):
- """
- Method called by user to Simulate PLC
- """
- uri = "LOCAL://"
- try:
- self._SetConnector(connectors.ConnectorFactory(uri, self))
- except Exception, msg:
- self.logger.write_error(_("Exception while connecting %s!\n") % uri)
- self.logger.write_error(traceback.format_exc())
-
- # Did connection success ?
- if self._connector is None:
- # Oups.
- self.logger.write_error(_("Connection failed to %s!\n") % uri)
- self.StopSimulation()
- return False
-
- self.CurrentMode = SIMULATION_MODE
-
- buildpath = self._getBuildPath()
-
- # Eventually create build dir
- if not os.path.exists(buildpath):
- os.makedirs(buildpath)
-
- # Generate SoftPLC IEC code
- IECGenRes = self._Generate_SoftPLC()
-
- # If IEC code gen fail, bail out.
- if not IECGenRes:
- self.logger.write_error(_("IEC-61131-3 code generation failed !\n"))
- self.StopSimulation()
- return False
-
- # Reset variable and program list that are parsed from
- # CSV file generated by IEC2C compiler.
- self.ResetIECProgramsAndVariables()
-
- gen_result = self.CTNGenerate_C(buildpath, self.PLCGeneratedLocatedVars)
- CTNCFilesAndCFLAGS, CTNLDFLAGS, DoCalls = gen_result[:3]
- # if some files have been generated put them in the list with their location
- if CTNCFilesAndCFLAGS:
- self.LocationCFilesAndCFLAGS = [(self.GetCurrentLocation(), CTNCFilesAndCFLAGS, DoCalls)]
- else:
- self.LocationCFilesAndCFLAGS = []
-
- # confnode asks for some LDFLAGS
- if CTNLDFLAGS:
- # LDFLAGS can be either string
- if type(CTNLDFLAGS) == type(str()):
- self.LDFLAGS = [CTNLDFLAGS]
- # or list of strings
- elif type(CTNLDFLAGS) == type(list()):
- self.LDFLAGS = CTNLDFLAGS[:]
- else:
- self.LDFLAGS = []
-
- # Header file for extensions
- open(os.path.join(buildpath, "beremiz.h"), "w").write(targets.GetHeader())
-
- # Template based part of C code generation
- # files are stacked at the beginning, as files of confnode tree root
- for generator, filename, name in [
- # debugger code
- (self.Generate_plc_debugger, "plc_debugger.c", "Debugger"),
- # init/cleanup/retrieve/publish, run and align code
- (self.Generate_plc_main, "plc_main.c", "Common runtime"),
- # declare located variables for simulate in a black box
- (self.Generate_plc_declare_locations, "plc_declare_locations.c", "Declare Locations"),
- # declare located variables for simulate in a black box
- (self.Generate_lpc_retain_array_sim, "lpc_retain_array_sim.c", "Retain Array for Simulation")]:
- try:
- # Do generate
- code = generator()
- if code is None:
- raise
- code_path = os.path.join(buildpath, filename)
- open(code_path, "w").write(code)
- # Insert this file as first file to be compiled at root confnode
- self.LocationCFilesAndCFLAGS[0][1].insert(0, (code_path, self.plcCFLAGS))
- except Exception, exc:
- self.logger.write_error(name + _(" generation failed !\n"))
- self.logger.write_error(traceback.format_exc())
- self.StopSimulation()
- return False
-
- # Get simulation builder
- builder = self.GetBuilder()
- if builder is None:
- self.logger.write_error(_("Fatal : cannot get builder.\n"))
- self.StopSimulation()
- return False
-
- # Build
- try:
- if not builder.build():
- self.logger.write_error(_("C Build failed.\n"))
- self.StopSimulation()
- return False
- except Exception, exc:
- self.logger.write_error(_("C Build crashed !\n"))
- self.logger.write_error(traceback.format_exc())
- self.StopSimulation()
- return False
-
- data = builder.GetBinaryCode()
- if data is not None:
- if self._connector.NewPLC(builder.GetBinaryCodeMD5(), data, []):
- self.UnsubscribeAllDebugIECVariable()
- self.ProgramTransferred()
- if self.AppFrame is not None:
- self.AppFrame.CloseObsoleteDebugTabs()
- self.AppFrame.RefreshPouInstanceVariablesPanel()
- self.logger.write(_("Transfer completed successfully.\n"))
- else:
- self.logger.write_error(_("Transfer failed\n"))
- self.StopSimulation()
- return False
-
- self._Run()
-
- if not self.StatusTimer.IsRunning():
- # Start the status Timer
- self.StatusTimer.Start(milliseconds=500, oneShot=False)
-
- def StopSimulation(self):
- self.CurrentMode = None
- self._SetConnector(None, False)
- self.ApplyOnlineMode()
-
- def _Stop(self):
- ProjectController._Stop(self)
-
- if self.CurrentMode == SIMULATION_MODE:
- self.StopSimulation()
-
- def CompareLocalAndRemotePLC(self):
- # if self.LPCConnector is None:
- # return
- if self._connector is None:
- return
- # We are now connected. Update button status
- MD5 = self.GetLastBuildMD5()
- # Check remote target PLC correspondance to that md5
- # if MD5 is not None and self.LPCConnector.MatchMD5(MD5):
- if MD5 is not None and self._connector.MatchMD5(MD5):
- # warns controller that program match
- self.ProgramTransferred()
-
- def ResetBuildMD5(self):
- builder = self.GetBuilder()
- if builder is not None:
- builder.ResetBinaryCodeMD5(*([] if arch in PLC_module else [self.OnlineMode]))
-
- def GetLastBuildMD5(self):
- builder = self.GetBuilder()
- if builder is not None:
- return builder.GetBinaryCodeMD5(*([] if arch in PLC_module else [self.OnlineMode]))
- else:
- return None
-
- def _Clean(self, building = False):
- self._CloseView(self._IECCodeView)
- runtime_list = fnmatch.filter(os.listdir(self._getBuildPath()), 'runtime_*')
- if os.path.isdir(os.path.join(self._getBuildPath())) and os.path.isfile(
- os.path.join(self._getBuildPath(), "hmi.py")) or runtime_list != []:
- self.logger.write(_("Cleaning the build directory\n"))
- # self.logger.write(_(str(os.path.join(self._getBuildPath(), "hmi.py"))))
- for file in runtime_list:
- os.remove(os.path.join(self._getBuildPath(), file))
- if os.path.isfile(os.path.join(self._getBuildPath(), "hmi.py")):
- os.remove(os.path.join(self._getBuildPath(), "hmi.py"))
- else:
- if not building:
- self.logger.write_error(_("Build directory already clean\n"))
- self.ShowMethod("_showIECcode", False)
- self.EnableMethod("_Clean", False)
- # kill the builder
- self._builder = None
- self.CompareLocalAndRemotePLC()
-
- def _Transfer(self):
- if self.OnlineMode == "NORMAL":
- if self.IsPLCStarted():
- dialog = wx.MessageDialog(self.AppFrame, "You must stop the PLC before transfer. Do you want to stop it now and transfer?", style=wx.YES_NO|wx.CENTRE)
- if dialog.ShowModal() == wx.ID_YES:
- self._Stop()
- else:
- return
- ProjectController._Transfer(self)
- return
- if self.CurrentMode is None and self.OnlineMode != "OFF":
- self.CurrentMode = TRANSFER_MODE
-
- if ProjectController._Build(self):
-
- ID_ABORTTRANSFERTIMER = wx.NewId()
- self.AbortTransferTimer = wx.Timer(self.AppFrame, ID_ABORTTRANSFERTIMER)
- self.AppFrame.Bind(wx.EVT_TIMER, self.AbortTransfer, self.AbortTransferTimer)
-
- if self.OnlineMode == "BOOTLOADER":
- self.BeginTransfer()
-
- else:
- self.logger.write(_("Resetting PLC\n"))
- # self.StatusTimer.Stop()
- self.LPCConnector.ResetPLC()
- self.AbortTransferTimer.Start(milliseconds=5000, oneShot=True)
-
- else:
- self.CurrentMode = None
-
- def BeginTransfer(self):
- self.logger.write(_("Start PLC transfer\n"))
-
- self.AbortTransferTimer.Stop()
- ProjectController._Transfer(self)
- self.AbortTransferTimer.Start(milliseconds=5000, oneShot=True)
-
- def AbortTransfer(self, event):
- self.logger.write_warning(_("Timeout waiting PLC to recover\n"))
-
- self.CurrentMode = None
- self.AbortTransferTimer.Stop()
- self.AbortTransferTimer = None
- event.Skip()
-
- old_DebugThreadProc = ProjectController.DebugThreadProc
- def LPCManager_DebugThreadProc(self, checking=False):
- if checking:
- state, ls = self._connector.GetTraceVariables()
- while state == "Stopped":
- state, ls = self._connector.GetTraceVariables()
- time.sleep(0.1)
-
- self.old_DebugThreadProc()
-
- def _connect_debug(self, checking = False):
- self.previous_plcstate = None
- if self.AppFrame:
- self.AppFrame.ResetGraphicViewers()
- self.RegisterDebugVarToConnector()
- if self.DispatchDebugValuesTimer is not None:
- self.DispatchDebugValuesTimer.Start(
- int(REFRESH_PERIOD * 1000), oneShot=True)
- if self.DebugThread is None:
- # self.DebugThread = Thread(target=self.DebugThreadProc)
- self.DebugThread = Thread(target=self.LPCManager_DebugThreadProc, args=(checking, ))
- self.DebugThread.setDaemon(True)
- self.DebugThread.start()
-
- def _Run(self):
- """
- Start PLC
- """
- if self.GetIECProgramsAndVariables():
- self._connector.StartPLC()
- self.logger.write(_("Starting PLC\n"))
- self._connect_debug(True)
- else:
- self.logger.write_error(_("Couldn't start PLC !\n"))
- self.UpdateMethodsFromPLCStatus()
-
- def _Disconnect(self):
- self._SetConnector(None)
- self.KillDebugThread()
-
- def _UpdateFw(self):
- """
- Method called by user to flash the firmware of the PLC
- """
- from FirmwareUpdateDialog import FirmwareUpdateDialog
- from dialogs import DiscoveryDialog
- from HostFirmwareUpdater import HostFirmwareUpdater
-
- if self.firmawreUpadateIsRunning == True:
- self.logger.write_error(_("Firmware update is already running!\n"))
- return
-
- self.firmawreUpadateIsRunning = True
- self.logger.write(_("Firmware update started\n"))
-
- # Launch the firmware selection dialog
- dialog = FirmwareUpdateDialog(self.AppFrame)
- answer = dialog.ShowModal()
- imageFilePath = dialog.GetFirmwareImageFile()
- updateType = dialog.GetFirmwareUpdateType()
- chunksSize = dialog.GetChunksSize()
- reboot = dialog.GetReboot()
- dialog.Destroy()
-
- if answer == wx.ID_CANCEL:
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
-
- if imageFilePath == None or imageFilePath == "":
- self.logger.write_error(_("No firmware image file selected!\n"))
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
- else:
- self.logger.write(_("Firmware image file: %s\n") % imageFilePath)
-
- if updateType == 1:
- self.logger.write(_("Firmware update type: Linux kernel image\n"))
- elif updateType == 2:
- self.logger.write(_("Firmware update type: Linux DTB image\n"))
- elif updateType == 3:
- self.logger.write(_("Firmware update type: Root file system image\n"))
- else:
- self.logger.write_error(_("Unknown firmware update type!\n"))
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
-
- if chunksSize < 1024 or chunksSize > 1024 * 1024:
- self.logger.write_error(_("Bad chunks size : %d KiB!\n") % chunksSize)
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
- else:
- self.logger.write(_("Chunks size : %d KiB\n") % chunksSize)
-
- self.logger.write(_("Reboot after update : %s\n") % ("Yes" if reboot else "No"))
- # Get the target PLC URI
- # Check if an uri is already configured in the Beremiz project
- uri = self.BeremizRoot.getURI_location()
- if uri is not None and uri != "":
- self.logger.write(_("PLC URI configured in the Beremiz project will be used: %s\n") % uri)
- else:
- # PLC URI is not configured in the Beremiz project
- # Launch Service Discovery dialog
- self.logger.write(_("PLC URI is not configured in the Beremiz project. Launching the Discover dialog\n"))
- dialog = DiscoveryDialog(self.AppFrame)
- answer = dialog.ShowModal()
- uri = dialog.GetURI()
- dialog.Destroy()
-
- # Nothing choosed or cancel button
- if uri is None or answer == wx.ID_CANCEL:
- self.logger.write_error(_("Connection canceled!\n"))
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
-
- # Get connector from uri
- self._connector = None
- try:
- self._SetConnector(connectors.ConnectorFactory(uri, self))
- except Exception:
- self.logger.write_error(_("Exception while connecting %s!\n") % uri)
- self.logger.write_error(traceback.format_exc())
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
-
- # Did connection success ?
- if self._connector is None:
- # Oups.
- self.logger.write_error(_("Connection failed to %s!\n") % uri)
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
- else:
- self.logger.write(_("Connected.\n"))
-
- # Last confirmation before firmware update
- answer = wx.MessageBox(_('Are you sure to launch the firmware update for the selected PLC?'),
- _('Firmware Update'), wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT,
- self.AppFrame)
- if answer != wx.YES:
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
- # Some cosmetics
- self.AppFrame.Refresh()
- self.AppFrame.Update()
-
- # Lauch the firmaware update on the remote PLC
- updater = HostFirmwareUpdater(self._connector, imageFilePath, updateType, chunksSize, reboot, self.logger)
- try:
- updater.update()
- except Exception as e:
- self.logger.write_error(str(e) + "\n")
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- self._Disconnect()
- return
- self._Disconnect()
- self.firmawreUpadateIsRunning = False
- return True
-
-
-from threading import Thread, Timer, Semaphore, Lock
+from threading import Thread, Semaphore, Lock
import cmd
build_lock = Lock()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LPCProjectController.py Mon Feb 05 13:02:21 2018 +0100
@@ -0,0 +1,929 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import fnmatch
+import shutil
+import zipfile
+import tempfile
+
+def mycopytree(src, dst):
+ """
+ Copy content of a directory to an other, omit hidden files
+ @param src: source directory
+ @param dst: destination directory
+ """
+ for i in os.listdir(src):
+ if not i.startswith('.'):
+ srcpath = os.path.join(src, i)
+ dstpath = os.path.join(dst, i)
+ if os.path.isdir(srcpath):
+ if os.path.exists(dstpath):
+ shutil.rmtree(dstpath)
+ os.makedirs(dstpath)
+ mycopytree(srcpath, dstpath)
+ elif os.path.isfile(srcpath):
+ shutil.copy2(srcpath, dstpath)
+
+
+[SIMULATION_MODE, TRANSFER_MODE] = range(2)
+
+if arch in PLC_module:
+ class LPCProjectNodeEditor(ProjectNodeEditor):
+ pass
+else:
+ class LPCProjectNodeEditor(ProjectNodeEditor):
+ SHOW_PARAMS = False
+ ENABLE_REQUIRED = False
+
+_StatusMethods = [
+ {"bitmap": "Debug",
+ "name": _("Simulate"),
+ "tooltip": _("Simulate PLC"),
+ "method": "_Simulate"},
+ {"bitmap": "Run",
+ "name": _("Run"),
+ "shown": False,
+ "tooltip": _("Start PLC"),
+ "method": "_Run"},
+ {"bitmap": "Stop",
+ "name": _("Stop"),
+ "shown": False,
+ "tooltip": _("Stop Running PLC"),
+ "method": "_Stop"},
+ {"bitmap": "Build",
+ "name": _("Build"),
+ "tooltip": _("Build project into build folder"),
+ "method": "_Build"},
+ {"bitmap": "Clean",
+ "name": _("Clean"),
+ "enabled": False,
+ "tooltip": _("Clean project build folder"),
+ "method": "_Clean"},
+ {"bitmap": "Transfer",
+ "name": _("Transfer"),
+ "shown": False,
+ "tooltip": _("Transfer PLC"),
+ "method": "_Transfer"},
+]
+_MethodFromPLCState = {
+ "Started": [("_Simulate", False),
+ ("_Run", False),
+ ("_Stop", True),
+ ("_Build", True),
+ ("_Transfer", True)],
+ "Stopped": [("_Simulate", False),
+ ("_Run", True),
+ ("_Stop", False),
+ ("_Build", True),
+ ("_Transfer", True)],
+ "Connected": [("_Simulate", "not simulating"),
+ ("_Run", True),
+ ("_Stop", True if arch in PLC_module else "simulating"),
+ ("_Build", True),
+ ("_Transfer", True)],
+ "Disconnected": [("_Simulate", "not simulating"),
+ ("_Run", False),
+ ("_Stop", False if arch in PLC_module else "simulating"),
+ ("_Build", True),
+ ("_Transfer", False)],
+ "Empty" :[("_Simulate", "not simulating"),
+ ("_Run", False),
+ ("_Stop", False),
+ ("_Build", True),
+ ("_Transfer", True)],
+}
+
+if arch in PLC_module:
+ _StatusMethods += [
+ {"bitmap": "Connect",
+ "name": _("Connect"),
+ "shown": True,
+ "tooltip": _("Connect to the target PLC"),
+ "method": "_Connect"},
+ {"bitmap": "Disconnect",
+ "name": _("Disconnect"),
+ "shown": False,
+ "tooltip": _("Disconnect from PLC"),
+ "method": "_Disconnect"},
+ {"bitmap": "UpdateFw",
+ "name": _("UpdateFw"),
+ "shown": True,
+ "tooltip": _("Update the PLC firmware"),
+ "method": "_UpdateFw"},
+ ]
+ _MethodFromPLCState["Disconnected"] += [("_Connect", True),
+ ("_Disconnect", False)]
+
+ _MethodFromPLCState["Connected"] += [("_Connect", False),
+ ("_Disconnect", True)]
+
+class LPCProjectController(ProjectController):
+ StatusMethods = _StatusMethods
+ # ConfNodeMethods = []
+
+ EditorType = LPCProjectNodeEditor
+
+ def __init__(self, frame, logger, buildpath):
+ self.arch = arch
+ self.OrigBuildPath = buildpath
+
+ ProjectController.__init__(self, frame, logger)
+
+ if havecanfestival:
+ self.CTNChildrenTypes += [("LPCBus", LPCBus, "LPC bus"), ("CanOpen", LPCCanOpen, "CanOpen bus")]
+ else:
+ self.CTNChildrenTypes += [("LPCBus", LPCBus, "LPC bus")]
+ self.CTNType = "LPC"
+
+ self.OnlineMode = "NORMAL" if arch in PLC_module else "OFF"
+ self.LPCConnector = None
+ self.ConnectorPath = None
+
+ self.CurrentMode = None
+ self.previous_mode = None
+
+ self.SimulationBuildPath = None
+
+ self.AbortTransferTimer = None
+
+ # Firmware update running status
+ self.firmawreUpadateIsRunning = False
+ self.building = False
+
+
+ # Bind mouse double click event on URI_location in Beremiz
+ self.UriOptions = True
+
+ def GetProjectName(self):
+ return self.Project.getname()
+
+ def GetDefaultTargetName(self):
+ if self.CurrentMode == SIMULATION_MODE:
+ return ProjectController.GetDefaultTargetName(self)
+ else:
+ return "LPC"
+
+ def GetTarget(self):
+ target = ProjectController.GetTarget(self)
+ if self.CurrentMode != SIMULATION_MODE and arch not in PLC_module:
+ target.getcontent().setBuildPath(self.BuildPath)
+ return target
+
+ def _getBuildPath(self):
+ if self.CurrentMode == SIMULATION_MODE:
+ if self.SimulationBuildPath is None:
+ self.SimulationBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build")
+ return self.SimulationBuildPath
+ else:
+ return ProjectController._getBuildPath(self)
+
+ def ToZIPFile(self):
+ # MD5 = self.GetLastBuildMD5()
+ try:
+ path_export_file = self.BuildPath[:-5] + "\\" + self._builder.exe[:-3] + ".xEye"
+ zf = zipfile.ZipFile(path_export_file, mode='w', compression=zipfile.ZIP_DEFLATED)
+ for extrafilespath in [self._getExtraFilesPath(),
+ self._getProjectFilesPath()]:
+ dir = extrafilespath.split("\\")[-1]
+ for name in os.listdir(extrafilespath):
+ zf.write(extrafilespath + '\\' + str(name), dir + '\\' + str(name), zipfile.ZIP_DEFLATED)
+ zf.write(self._builder.exe_path, self._builder.exe)
+ zf.write(self.BuildPath + '\\lastbuildPLC.md5', 'lastbuildPLC.md5')
+ zf.close()
+ self.logger.write(_("Export file is successfully created on location: %s\n") % path_export_file)
+ except Exception, e:
+ self.logger.write(_("Export file is not created because eror: %s\n") % e)
+
+ def _Build(self):
+ self._Clean()
+ save = self.ProjectTestModified()
+ if save:
+ self.SaveProject()
+ self.AppFrame._Refresh(TITLE, FILEMENU)
+ if self.BuildPath is not None:
+ mycopytree(self.OrigBuildPath, self.BuildPath)
+ if ProjectController._Build(self):
+ self.ToZIPFile()
+ if save:
+ wx.CallAfter(self.AppFrame.RefreshAll)
+
+ def SetProjectName(self, name):
+ return self.Project.setname(name)
+
+ def SetOnlineMode(self, mode, path=None):
+ # SetOnlineMode is only for MC8
+ if arch in PLC_module:
+ return None
+
+ mode = mode.upper()
+ if self.OnlineMode != mode:
+ if mode not in ["OFF", ""]:
+ self.OnlineMode = mode
+ self.ConnectorPath = path
+ uri = "LPC://%s/%s" % (self.OnlineMode, path)
+ try:
+ self.LPCConnector = connectors.ConnectorFactory(uri, self)
+ except Exception, msg:
+ self.logger.write_error(_("Exception while connecting %s!\n") % uri)
+ self.logger.write_error(traceback.format_exc())
+
+ # Did connection success ?
+ if self.LPCConnector is None:
+ # Oups.
+ self.logger.write_error(_("Connection failed to %s!\n") % uri)
+
+ else:
+ self.OnlineMode = "OFF"
+ self.LPCConnector = None
+ self.ConnectorPath = None
+
+ self.ApplyOnlineMode()
+
+
+ def ApplyOnlineMode(self):
+ if self.CurrentMode != SIMULATION_MODE:
+ self.KillDebugThread()
+
+ self._SetConnector(self.LPCConnector)
+
+ # Init with actual PLC status and print it
+ self.UpdateMethodsFromPLCStatus()
+
+ if self.LPCConnector is not None and self.OnlineMode == "APPLICATION":
+
+ self.CompareLocalAndRemotePLC()
+
+ if self.previous_plcstate is not None:
+ status = _(self.previous_plcstate)
+ else:
+ status = ""
+ self.logger.write(_("PLC is %s\n") % status)
+
+ # if self.StatusTimer and not self.StatusTimer.IsRunning():
+ # # Start the status Timer
+ # self.StatusTimer.Start(milliseconds=2000, oneShot=False)
+
+ if self.previous_plcstate == "Started":
+ if self.DebugAvailable() and self.GetIECProgramsAndVariables():
+ self.logger.write(_("Debug connect matching running PLC\n"))
+ self._connect_debug()
+ else:
+ self.logger.write_warning(_("Debug do not match PLC - stop/transfert/start to re-enable\n"))
+
+ elif self.StatusTimer and self.StatusTimer.IsRunning():
+ self.StatusTimer.Stop()
+
+ if self.CurrentMode == TRANSFER_MODE:
+
+ if self.OnlineMode == "BOOTLOADER":
+ self.BeginTransfer()
+
+ elif self.OnlineMode == "APPLICATION":
+ self.CurrentMode = None
+ self.AbortTransferTimer.Stop()
+ self.AbortTransferTimer = None
+
+ self.logger.write(_("PLC transferred successfully\n"))
+
+ # Update a PLCOpenEditor Pou variable location
+ def UpdateProjectVariableLocation(self, old_leading, new_leading):
+ self.Project.updateElementAddress(old_leading, new_leading)
+ self.BufferProject()
+
+ # Update a PLCOpenEditor Pou variable name
+ def UpdateProjectVariableName(self, old_name, new_name):
+ self.Project.updateElementName(old_name, new_name)
+ self.BufferProject()
+
+ def RemoveProjectVariableByAddress(self, address):
+ self.Project.removeVariableByAddress(address)
+ self.BufferProject()
+
+ def RemoveProjectVariableByFilter(self, leading):
+ self.Project.removeVariableByFilter(leading)
+ self.BufferProject()
+
+ def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"):
+ ProjectController.AddProjectDefaultConfiguration(self, config_name, res_name)
+
+ self.SetEditedResourceInfos(
+ self.ComputeConfigurationResourceName(config_name, res_name),
+ [{"Name": "main_task",
+ "Triggering": "Cyclic",
+ "Interval": "T#50ms",
+ "Priority": 0}],
+ [])
+
+ def LoadProject(self, ProjectPath, BuildPath=None):
+ """
+ Load a project contained in a folder
+ @param ProjectPath: path of the project folder
+ """
+ if os.path.basename(ProjectPath) == "":
+ ProjectPath = os.path.dirname(ProjectPath)
+
+ # Verify that project contains a PLCOpen program
+ plc_file = os.path.join(ProjectPath, "plc.xml")
+ if os.path.isfile(plc_file):
+ # Load PLCOpen file
+ result = self.OpenXMLFile(plc_file)
+ if result:
+ return result
+ else:
+ self.CreateNewProject({"companyName": "",
+ "productName": "",
+ "productVersion": "",
+ "projectName": "",
+ "pageSize": (0, 0),
+ "scaling": {}})
+ if len(self.GetProjectConfigNames()) == 0:
+ self.AddProjectDefaultConfiguration()
+
+ # Change XSD into class members
+ self._AddParamsMembers()
+ self.Children = {}
+
+ # Keep track of the root confnode (i.e. project path)
+ self.ProjectPath = ProjectPath
+
+ self.BuildPath = self._getBuildPath()
+ if self.OrigBuildPath is not None:
+ mycopytree(self.OrigBuildPath, self.BuildPath)
+
+ # If dir have already be made, and file exist
+ if os.path.isdir(self.CTNPath()) and os.path.isfile(self.ConfNodeXmlFilePath()):
+ # Load the confnode.xml file into parameters members
+ result = self.LoadXMLParams()
+ if result:
+ return result
+ # Load and init all the children
+ self.LoadChildren()
+
+ canopen_child = self.GetChildByName("CanOpen")
+ if havecanfestival and canopen_child is None:
+ canopen = self.CTNAddChild("CanOpen", "CanOpen", 0)
+ canopen.LoadChildren()
+ canopen.CTNRequestSave()
+
+ elif not havecanfestival and canopen_child is not None:
+ canopen_child.CTNRemove()
+
+ if self.CTNTestModified():
+ self.SaveProject()
+
+ if wx.GetApp() is None:
+ self.RefreshConfNodesBlockLists()
+ else:
+ wx.CallAfter(self.RefreshConfNodesBlockLists)
+
+ if arch in PLC_module:
+ self.SetParamsAttribute('BeremizRoot.TargetType', 'MC9')
+
+ return None
+
+ def IsPLCStarted(self):
+ return self.previous_plcstate == "Started" or self.previous_mode == SIMULATION_MODE
+
+ def ShowMethod(self, name, val):
+ simulating = self.CurrentMode == SIMULATION_MODE
+ if type(val) == str:
+ if val.endswith("simulating"):
+ if val.startswith("not"):
+ val = not simulating
+ else:
+ val = simulating
+
+ ProjectController.ShowMethod(self, name, val)
+
+ def UpdateMethodsFromPLCStatus(self):
+ simulating = self.CurrentMode == SIMULATION_MODE
+ if self.OnlineMode == "OFF":
+ if simulating:
+ status, log_count = self._connector.GetPLCstatus()
+ self.UpdatePLCLog(log_count)
+ status = "Disconnected"
+ elif self.OnlineMode == "BOOTLOADER":
+ status = "Connected"
+ else:
+ if self._connector is not None:
+ status, log_count = self._connector.GetPLCstatus()
+ if status == "Disconnected":
+ self._SetConnector(None, False)
+ else:
+ self.UpdatePLCLog(log_count)
+ else:
+ status = "Disconnected"
+ if self.previous_plcstate != status or self.previous_mode != self.CurrentMode:
+ for args in _MethodFromPLCState.get(status, []):
+ self.ShowMethod(*args)
+ self.previous_plcstate = status
+ self.previous_mode = self.CurrentMode
+ if self.AppFrame is not None:
+ self.AppFrame.RefreshStatusToolBar()
+ connection_text = _("Connected to: ")
+ status_text = ""
+ if simulating:
+ connection_text += _("Simulation")
+ status_text += _("ON")
+ if status == "Disconnected":
+ if not simulating:
+ self.AppFrame.ConnectionStatusBar.SetStatusText(_(status), 1)
+ self.AppFrame.ConnectionStatusBar.SetStatusText('', 2)
+ else:
+ self.AppFrame.ConnectionStatusBar.SetStatusText(connection_text, 1)
+ self.AppFrame.ConnectionStatusBar.SetStatusText(status_text, 2)
+ else:
+ if simulating:
+ connection_text += " (%s)"
+ status_text += " (%s)"
+ else:
+ connection_text += "%s"
+ status_text += "%s"
+ self.AppFrame.ConnectionStatusBar.SetStatusText(connection_text % self.ConnectorPath, 1)
+ self.AppFrame.ConnectionStatusBar.SetStatusText(status_text % _(status), 2)
+ return True
+ return False
+
+ def Generate_plc_declare_locations(self):
+ """
+ Declare used locations in order to simulatePLC in a black box
+ """
+ return """#include "iec_types_all.h"
+
+#define __LOCATED_VAR(type, name, ...) \
+type beremiz_##name;\
+type *name = &beremiz_##name;
+
+#include "LOCATED_VARIABLES.h"
+
+#undef __LOCATED_VAR
+
+"""
+
+ def Generate_lpc_retain_array_sim(self):
+ """
+ Support for retain array in Simulation
+ """
+ return """/* Support for retain array */
+#define USER_RETAIN_ARRAY_SIZE 2000
+#define NUM_OF_COLS 3
+unsigned char readOK = 0;
+unsigned int foundIndex = USER_RETAIN_ARRAY_SIZE;
+unsigned int retainArray[USER_RETAIN_ARRAY_SIZE][NUM_OF_COLS];
+
+unsigned int __GetRetainData(unsigned char READ, unsigned int INDEX, unsigned int COLUMN)
+{
+ if(READ == 1)
+ {
+ if((0<=INDEX) && (INDEX<USER_RETAIN_ARRAY_SIZE) && (0<=COLUMN) && (COLUMN<NUM_OF_COLS))
+ {
+ readOK = 1;
+ return retainArray[INDEX][COLUMN];
+ }
+ }
+
+ readOK = 0;
+ return 0;
+}
+
+unsigned char __SetRetainData(unsigned char WRITE, unsigned int INDEX, unsigned int WORD1, unsigned int WORD2, unsigned int WORD3)
+{
+ if(WRITE == 1)
+ {
+ if((0<=INDEX) && (INDEX<USER_RETAIN_ARRAY_SIZE))
+ {
+ retainArray[INDEX][0] = WORD1;
+ retainArray[INDEX][1] = WORD2;
+ retainArray[INDEX][2] = WORD3;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+unsigned char __FindRetainData(unsigned char SEARCH, unsigned int START_IDX, unsigned int END_IDX, unsigned int WORD1, unsigned int WORD2, unsigned int WORD3)
+{
+ unsigned int i;
+
+ if((SEARCH==1) && (0<=START_IDX) && (START_IDX<USER_RETAIN_ARRAY_SIZE) && (START_IDX<=END_IDX) && (END_IDX<USER_RETAIN_ARRAY_SIZE))
+ {
+ for(i=START_IDX;i<=END_IDX;i++)
+ {
+ if((retainArray[i][0] == WORD1) && (retainArray[i][1] == WORD2) && (retainArray[i][2] == WORD3))
+ {
+ foundIndex = i;
+ return 1;
+ }
+ }
+ }
+
+ foundIndex = USER_RETAIN_ARRAY_SIZE; /* Data not found => return index that is out of array bounds */
+ return 0;
+}
+
+/* Since Beremiz debugger doesn't like pointer-by-reference stuff or global varibles, separate function is a must */
+unsigned char __GetReadStatus(unsigned char dummy)
+{
+ return readOK;
+}
+
+unsigned int __GetFoundIndex(unsigned char dummy)
+{
+ return foundIndex;
+}
+"""
+
+ def _Simulate(self):
+ """
+ Method called by user to Simulate PLC
+ """
+ uri = "LOCAL://"
+ try:
+ self._SetConnector(connectors.ConnectorFactory(uri, self))
+ except Exception, msg:
+ self.logger.write_error(_("Exception while connecting %s!\n") % uri)
+ self.logger.write_error(traceback.format_exc())
+
+ # Did connection success ?
+ if self._connector is None:
+ # Oups.
+ self.logger.write_error(_("Connection failed to %s!\n") % uri)
+ self.StopSimulation()
+ return False
+
+ self.CurrentMode = SIMULATION_MODE
+
+ buildpath = self._getBuildPath()
+
+ # Eventually create build dir
+ if not os.path.exists(buildpath):
+ os.makedirs(buildpath)
+
+ # Generate SoftPLC IEC code
+ IECGenRes = self._Generate_SoftPLC()
+
+ # If IEC code gen fail, bail out.
+ if not IECGenRes:
+ self.logger.write_error(_("IEC-61131-3 code generation failed !\n"))
+ self.StopSimulation()
+ return False
+
+ # Reset variable and program list that are parsed from
+ # CSV file generated by IEC2C compiler.
+ self.ResetIECProgramsAndVariables()
+
+ gen_result = self.CTNGenerate_C(buildpath, self.PLCGeneratedLocatedVars)
+ CTNCFilesAndCFLAGS, CTNLDFLAGS, DoCalls = gen_result[:3]
+ # if some files have been generated put them in the list with their location
+ if CTNCFilesAndCFLAGS:
+ self.LocationCFilesAndCFLAGS = [(self.GetCurrentLocation(), CTNCFilesAndCFLAGS, DoCalls)]
+ else:
+ self.LocationCFilesAndCFLAGS = []
+
+ # confnode asks for some LDFLAGS
+ if CTNLDFLAGS:
+ # LDFLAGS can be either string
+ if type(CTNLDFLAGS) == type(str()):
+ self.LDFLAGS = [CTNLDFLAGS]
+ # or list of strings
+ elif type(CTNLDFLAGS) == type(list()):
+ self.LDFLAGS = CTNLDFLAGS[:]
+ else:
+ self.LDFLAGS = []
+
+ # Header file for extensions
+ open(os.path.join(buildpath, "beremiz.h"), "w").write(targets.GetHeader())
+
+ # Template based part of C code generation
+ # files are stacked at the beginning, as files of confnode tree root
+ for generator, filename, name in [
+ # debugger code
+ (self.Generate_plc_debugger, "plc_debugger.c", "Debugger"),
+ # init/cleanup/retrieve/publish, run and align code
+ (self.Generate_plc_main, "plc_main.c", "Common runtime"),
+ # declare located variables for simulate in a black box
+ (self.Generate_plc_declare_locations, "plc_declare_locations.c", "Declare Locations"),
+ # declare located variables for simulate in a black box
+ (self.Generate_lpc_retain_array_sim, "lpc_retain_array_sim.c", "Retain Array for Simulation")]:
+ try:
+ # Do generate
+ code = generator()
+ if code is None:
+ raise
+ code_path = os.path.join(buildpath, filename)
+ open(code_path, "w").write(code)
+ # Insert this file as first file to be compiled at root confnode
+ self.LocationCFilesAndCFLAGS[0][1].insert(0, (code_path, self.plcCFLAGS))
+ except Exception, exc:
+ self.logger.write_error(name + _(" generation failed !\n"))
+ self.logger.write_error(traceback.format_exc())
+ self.StopSimulation()
+ return False
+
+ # Get simulation builder
+ builder = self.GetBuilder()
+ if builder is None:
+ self.logger.write_error(_("Fatal : cannot get builder.\n"))
+ self.StopSimulation()
+ return False
+
+ # Build
+ try:
+ if not builder.build():
+ self.logger.write_error(_("C Build failed.\n"))
+ self.StopSimulation()
+ return False
+ except Exception, exc:
+ self.logger.write_error(_("C Build crashed !\n"))
+ self.logger.write_error(traceback.format_exc())
+ self.StopSimulation()
+ return False
+
+ data = builder.GetBinaryCode()
+ if data is not None:
+ if self._connector.NewPLC(builder.GetBinaryCodeMD5(), data, []):
+ self.UnsubscribeAllDebugIECVariable()
+ self.ProgramTransferred()
+ if self.AppFrame is not None:
+ self.AppFrame.CloseObsoleteDebugTabs()
+ self.AppFrame.RefreshPouInstanceVariablesPanel()
+ self.logger.write(_("Transfer completed successfully.\n"))
+ else:
+ self.logger.write_error(_("Transfer failed\n"))
+ self.StopSimulation()
+ return False
+
+ self._Run()
+
+ if not self.StatusTimer.IsRunning():
+ # Start the status Timer
+ self.StatusTimer.Start(milliseconds=500, oneShot=False)
+
+ def StopSimulation(self):
+ self.CurrentMode = None
+ self._SetConnector(None, False)
+ self.ApplyOnlineMode()
+
+ def _Stop(self):
+ ProjectController._Stop(self)
+
+ if self.CurrentMode == SIMULATION_MODE:
+ self.StopSimulation()
+
+ def CompareLocalAndRemotePLC(self):
+ # if self.LPCConnector is None:
+ # return
+ if self._connector is None:
+ return
+ # We are now connected. Update button status
+ MD5 = self.GetLastBuildMD5()
+ # Check remote target PLC correspondance to that md5
+ # if MD5 is not None and self.LPCConnector.MatchMD5(MD5):
+ if MD5 is not None and self._connector.MatchMD5(MD5):
+ # warns controller that program match
+ self.ProgramTransferred()
+
+ def ResetBuildMD5(self):
+ builder = self.GetBuilder()
+ if builder is not None:
+ builder.ResetBinaryCodeMD5(*([] if arch in PLC_module else [self.OnlineMode]))
+
+ def GetLastBuildMD5(self):
+ builder = self.GetBuilder()
+ if builder is not None:
+ return builder.GetBinaryCodeMD5(*([] if arch in PLC_module else [self.OnlineMode]))
+ else:
+ return None
+
+ def _Clean(self, building = False):
+ self._CloseView(self._IECCodeView)
+ runtime_list = fnmatch.filter(os.listdir(self._getBuildPath()), 'runtime_*')
+ if os.path.isdir(os.path.join(self._getBuildPath())) and os.path.isfile(
+ os.path.join(self._getBuildPath(), "hmi.py")) or runtime_list != []:
+ self.logger.write(_("Cleaning the build directory\n"))
+ # self.logger.write(_(str(os.path.join(self._getBuildPath(), "hmi.py"))))
+ for file in runtime_list:
+ os.remove(os.path.join(self._getBuildPath(), file))
+ if os.path.isfile(os.path.join(self._getBuildPath(), "hmi.py")):
+ os.remove(os.path.join(self._getBuildPath(), "hmi.py"))
+ else:
+ if not building:
+ self.logger.write_error(_("Build directory already clean\n"))
+ self.ShowMethod("_showIECcode", False)
+ self.EnableMethod("_Clean", False)
+ # kill the builder
+ self._builder = None
+ self.CompareLocalAndRemotePLC()
+
+ def _Transfer(self):
+ if self.OnlineMode == "NORMAL":
+ if self.IsPLCStarted():
+ dialog = wx.MessageDialog(self.AppFrame, "You must stop the PLC before transfer. Do you want to stop it now and transfer?", style=wx.YES_NO|wx.CENTRE)
+ if dialog.ShowModal() == wx.ID_YES:
+ self._Stop()
+ else:
+ return
+ ProjectController._Transfer(self)
+ return
+ if self.CurrentMode is None and self.OnlineMode != "OFF":
+ self.CurrentMode = TRANSFER_MODE
+
+ if ProjectController._Build(self):
+
+ ID_ABORTTRANSFERTIMER = wx.NewId()
+ self.AbortTransferTimer = wx.Timer(self.AppFrame, ID_ABORTTRANSFERTIMER)
+ self.AppFrame.Bind(wx.EVT_TIMER, self.AbortTransfer, self.AbortTransferTimer)
+
+ if self.OnlineMode == "BOOTLOADER":
+ self.BeginTransfer()
+
+ else:
+ self.logger.write(_("Resetting PLC\n"))
+ # self.StatusTimer.Stop()
+ self.LPCConnector.ResetPLC()
+ self.AbortTransferTimer.Start(milliseconds=5000, oneShot=True)
+
+ else:
+ self.CurrentMode = None
+
+ def BeginTransfer(self):
+ self.logger.write(_("Start PLC transfer\n"))
+
+ self.AbortTransferTimer.Stop()
+ ProjectController._Transfer(self)
+ self.AbortTransferTimer.Start(milliseconds=5000, oneShot=True)
+
+ def AbortTransfer(self, event):
+ self.logger.write_warning(_("Timeout waiting PLC to recover\n"))
+
+ self.CurrentMode = None
+ self.AbortTransferTimer.Stop()
+ self.AbortTransferTimer = None
+ event.Skip()
+
+ old_DebugThreadProc = ProjectController.DebugThreadProc
+ def LPCManager_DebugThreadProc(self, checking=False):
+ if checking:
+ state, ls = self._connector.GetTraceVariables()
+ while state == "Stopped":
+ state, ls = self._connector.GetTraceVariables()
+ time.sleep(0.1)
+
+ self.old_DebugThreadProc()
+
+ def _connect_debug(self, checking = False):
+ self.previous_plcstate = None
+ if self.AppFrame:
+ self.AppFrame.ResetGraphicViewers()
+ self.RegisterDebugVarToConnector()
+ if self.DispatchDebugValuesTimer is not None:
+ self.DispatchDebugValuesTimer.Start(
+ int(REFRESH_PERIOD * 1000), oneShot=True)
+ if self.DebugThread is None:
+ # self.DebugThread = Thread(target=self.DebugThreadProc)
+ self.DebugThread = Thread(target=self.LPCManager_DebugThreadProc, args=(checking, ))
+ self.DebugThread.setDaemon(True)
+ self.DebugThread.start()
+
+ def _Run(self):
+ """
+ Start PLC
+ """
+ if self.GetIECProgramsAndVariables():
+ self._connector.StartPLC()
+ self.logger.write(_("Starting PLC\n"))
+ self._connect_debug(True)
+ else:
+ self.logger.write_error(_("Couldn't start PLC !\n"))
+ self.UpdateMethodsFromPLCStatus()
+
+ def _Disconnect(self):
+ self._SetConnector(None)
+ self.KillDebugThread()
+
+ def _UpdateFw(self):
+ """
+ Method called by user to flash the firmware of the PLC
+ """
+ from FirmwareUpdateDialog import FirmwareUpdateDialog
+ from dialogs import DiscoveryDialog
+ from HostFirmwareUpdater import HostFirmwareUpdater
+
+ if self.firmawreUpadateIsRunning == True:
+ self.logger.write_error(_("Firmware update is already running!\n"))
+ return
+
+ self.firmawreUpadateIsRunning = True
+ self.logger.write(_("Firmware update started\n"))
+
+ # Launch the firmware selection dialog
+ dialog = FirmwareUpdateDialog(self.AppFrame)
+ answer = dialog.ShowModal()
+ imageFilePath = dialog.GetFirmwareImageFile()
+ updateType = dialog.GetFirmwareUpdateType()
+ chunksSize = dialog.GetChunksSize()
+ reboot = dialog.GetReboot()
+ dialog.Destroy()
+
+ if answer == wx.ID_CANCEL:
+ self.logger.write_error(_("Firmware update canceled!\n"))
+ self.firmawreUpadateIsRunning = False
+ return
+
+ if imageFilePath == None or imageFilePath == "":
+ self.logger.write_error(_("No firmware image file selected!\n"))
+ self.logger.write_error(_("Firmware update canceled!\n"))
+ self.firmawreUpadateIsRunning = False
+ return
+ else:
+ self.logger.write(_("Firmware image file: %s\n") % imageFilePath)
+
+ if updateType == 1:
+ self.logger.write(_("Firmware update type: Linux kernel image\n"))
+ elif updateType == 2:
+ self.logger.write(_("Firmware update type: Linux DTB image\n"))
+ elif updateType == 3:
+ self.logger.write(_("Firmware update type: Root file system image\n"))
+ else:
+ self.logger.write_error(_("Unknown firmware update type!\n"))
+ self.logger.write_error(_("Firmware update canceled!\n"))
+ self.firmawreUpadateIsRunning = False
+ return
+
+ if chunksSize < 1024 or chunksSize > 1024 * 1024:
+ self.logger.write_error(_("Bad chunks size : %d KiB!\n") % chunksSize)
+ self.logger.write_error(_("Firmware update canceled!\n"))
+ self.firmawreUpadateIsRunning = False
+ return
+ else:
+ self.logger.write(_("Chunks size : %d KiB\n") % chunksSize)
+
+ self.logger.write(_("Reboot after update : %s\n") % ("Yes" if reboot else "No"))
+ # Get the target PLC URI
+ # Check if an uri is already configured in the Beremiz project
+ uri = self.BeremizRoot.getURI_location()
+ if uri is not None and uri != "":
+ self.logger.write(_("PLC URI configured in the Beremiz project will be used: %s\n") % uri)
+ else:
+ # PLC URI is not configured in the Beremiz project
+ # Launch Service Discovery dialog
+ self.logger.write(_("PLC URI is not configured in the Beremiz project. Launching the Discover dialog\n"))
+ dialog = DiscoveryDialog(self.AppFrame)
+ answer = dialog.ShowModal()
+ uri = dialog.GetURI()
+ dialog.Destroy()
+
+ # Nothing choosed or cancel button
+ if uri is None or answer == wx.ID_CANCEL:
+ self.logger.write_error(_("Connection canceled!\n"))
+ self.logger.write_error(_("Firmware update canceled!\n"))
+ self.firmawreUpadateIsRunning = False
+ return
+
+ # Get connector from uri
+ self._connector = None
+ try:
+ self._SetConnector(connectors.ConnectorFactory(uri, self))
+ except Exception:
+ self.logger.write_error(_("Exception while connecting %s!\n") % uri)
+ self.logger.write_error(traceback.format_exc())
+ self.logger.write_error(_("Firmware update canceled!\n"))
+ self.firmawreUpadateIsRunning = False
+ return
+
+ # Did connection success ?
+ if self._connector is None:
+ # Oups.
+ self.logger.write_error(_("Connection failed to %s!\n") % uri)
+ self.logger.write_error(_("Firmware update canceled!\n"))
+ self.firmawreUpadateIsRunning = False
+ return
+ else:
+ self.logger.write(_("Connected.\n"))
+
+ # Last confirmation before firmware update
+ answer = wx.MessageBox(_('Are you sure to launch the firmware update for the selected PLC?'),
+ _('Firmware Update'), wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT,
+ self.AppFrame)
+ if answer != wx.YES:
+ self.logger.write_error(_("Firmware update canceled!\n"))
+ self.firmawreUpadateIsRunning = False
+ return
+ # Some cosmetics
+ self.AppFrame.Refresh()
+ self.AppFrame.Update()
+
+ # Lauch the firmaware update on the remote PLC
+ updater = HostFirmwareUpdater(self._connector, imageFilePath, updateType, chunksSize, reboot, self.logger)
+ try:
+ updater.update()
+ except Exception as e:
+ self.logger.write_error(str(e) + "\n")
+ self.logger.write_error(_("Firmware update canceled!\n"))
+ self.firmawreUpadateIsRunning = False
+ self._Disconnect()
+ return
+ self._Disconnect()
+ self.firmawreUpadateIsRunning = False
+ return True
--- a/StdoutPseudoFile.py Mon Feb 05 11:24:11 2018 +0100
+++ b/StdoutPseudoFile.py Mon Feb 05 13:02:21 2018 +0100
@@ -1,1966 +1,6 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-import sys
-import os
-
-# On Windows install schema, we compute path to beremiz
-# relative to path to python folder (sys.path[0] in that case)
-# note: beware that wx import messes up sys.path[0]
-_dist_folder = os.path.split(sys.path[0])[0]
-_beremiz_folder = os.path.join(_dist_folder, "beremiz")
-# Then we add it to sys.path, to make "import Beremiz" possible
-sys.path.append(_beremiz_folder)
-
-import fnmatch
-import shutil
-import socket
-import threading
-import zipfile
-import tempfile
-import getopt
-import __builtin__
-from types import StringType, UnicodeType
-import time
-import wx
-from util.BitmapLibrary import AddBitmapFolder
-
-# Path of directory containing current python file
-_lpcmanager_path = os.path.split(__file__)[0]
-
-# XXX Where is MC8 ?
-PLC_GOT_module = ['GOT', 'GOT_111', 'GOT_131']
-PLC_MC9_module = ['MC9']
-PLC_module = PLC_MC9_module + PLC_GOT_module
-
-# XXX get ride of global arch
-arch = None
-
-class LPCManagerLauncher(BeremizIDELauncher):
- def __init__(self):
- BeremizIDELauncher.__init__(self)
- self.arch = None
- self.port = None
- self.extensions = [os.path.join(_lpcmanager_path, "extention.py")]
- self.modules.extend([
- "LPCBeremiz",
- "StdoutPseudoFile",
- "LPCProjectController",
- "LPCCommand"])
-
- def Usage(self):
- print("\nUsage of LPCManager.py :")
- print("\n %s Projectpath Buildpath port arch\n" % sys.argv[0])
-
- def ProcessCommandLineArgs(self):
- global arch
- # Command line arguments parsing
- try:
- opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
- except getopt.GetoptError:
- # print help information and exit:
- self.Usage()
- sys.exit(2)
-
- # asking for help causes exit
- for o, a in opts:
- if o in ("-h", "--help"):
- self.Usage()
- sys.exit()
-
- if len(args) != 4 :
- self.Usage()
- sys.exit()
- else:
- self.projectOpen = args[0]
- self.buildpath = args[1]
- try:
- self.port = int(args[2])
- except:
- self.Usage()
- sys.exit()
- self.arch = args[3]
- arch = self.arch
-
- # overload with exacltly same code, but this is intended.
- # we want extensions to use globals of this module, not Beremiz.py
- def globals(self):
- return globals()
-
- def CreateUI(self):
- CMDpipe = StdoutPseudoFile(self.port)
-
- if self.projectOpen is not None:
- self.projectOpen = self.BeremizIDE.DecodeFileSystemPath(self.projectOpen, False)
-
- CTR = self.LPCProjectController.LPCProjectController(
- None, CMDpipe, self.buildpath)
- if self.projectOpen is not None and os.path.isdir(self.projectOpen):
- result = CTR.LoadProject(self.projectOpen)
- if result:
- CMDpipe.write("Error: Invalid project directory", result)
- else:
- CMDpipe.write("Error: No such file or directory")
-
- lpcberemiz_cmd = LPCCommand(CTR, CMDpipe)
-
- cmd_thread = Thread(target=lpcberemiz_cmd.cmdloop)
- cmd_thread.start()
- # TODO: join() when exiting
-
- self.frame = self.LPCBeremiz.LPCBeremiz(None, ctr=CTR)
-
- def ShowUI(self):
- # the "Show" command from composer does it instead
- pass
-
- def CreateApplication(self):
- # BEREMIZ_DEBUG file detection in beremiz isn't enough (module scope)
- # here we set BMZ_DBG interpreter-wise, so that submodules can use it.
- __builtin__.__dict__["BMZ_DBG"] = os.path.exists("LPC_DEBUG")
-
- # Create app usual way
- BeremizIDELauncher.CreateApplication(self)
-
- # Add LPCmanager's image folder to searched ones.
- AddBitmapFolder(os.path.join(_lpcmanager_path, "images"))
-
-
-import connectors
-from LPCconnector import LPC_connector_factory
-from LPCconnector.PYRO import MW_PYRO_connector_factory
-from LPCconnector.WAMP import MWWAMP_connector_factory
-
-import targets
-from LPCtarget import LPC_target
-
-targets.targets["LPC"] = {"xsd": os.path.join(_lpcmanager_path, "LPCtarget", "XSD"),
- "class": lambda: LPC_target,
- "code": {os.path.join(_lpcmanager_path, "LPCtarget", "plc_LPC_main.c")}}
-targets.toolchains["makefile"] = os.path.join(_lpcmanager_path, "LPCtarget", "XSD_toolchain_makefile")
-targets.targets["MC9"] = {"xsd": os.path.join(_lpcmanager_path, "MC9target", "XSD"),
- "class": targets.targets["Xenomai"]["class"],
- "code": {"plc_MC9_main.c": targets.targets["Xenomai"]["code"]["plc_Xenomai_main.c"],
- "plc_MC9_main_retain.c": os.path.join(_lpcmanager_path,
- "MC9target", "plc_MC9_main_retain.c")}}
-from BeremizIDE import *
-from ProjectController import ProjectController
-from ConfigTreeNode import ConfigTreeNode
-from editors.ProjectNodeEditor import ProjectNodeEditor
-from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
-from editors.CodeFileEditor import VariablesTable
-from editors.CodeFileEditor import VariablesEditor
-from editors.CodeFileEditor import CodeEditor
-from controls import ProjectPropertiesPanel
-from controls.SearchResultPanel import SearchResultPanel
-# from controls.LogViewer import LogViewer
-from WampOptionsEditor import WampOptionsEditor
-from VariableExporter import VariableWriter
-from PLCControler import PLCControler, ITEMS_UNEDITABLE, ITEM_POU
-from wx.lib.scrolledpanel import ScrolledPanel
-from types import MethodType
-from IDEFrame import IDEFrame
-from dialogs import SearchInProjectDialog
-from controls import TextCtrlAutoComplete
-from py_ext import PythonFileCTNMixin
-from plcopen.structures import TestIdentifier, IDENTIFIER_MODEL
-
-def CTNGenerate_C(self, buildpath, locations):
- # location string for that CTN
- location_str = "_".join(map(lambda x: str(x),
- self.GetCurrentLocation()))
- configname = self.GetCTRoot().GetProjectConfigNames()[0]
-
- pyextname = self.CTNName()
- varinfos = map(lambda variable: {
- "name": variable.getname(),
- "desc": repr(variable.getdesc()),
- "onchangecode": '"' + variable.getonchange() + \
- "('" + variable.getname() + "')\"" \
- if variable.getonchange() else '""',
- "onchange": repr(variable.getonchange()) \
- if variable.getonchange() else None,
- "opts": repr(variable.getopts()),
- "configname": configname.upper(),
- "uppername": variable.getname().upper(),
- "IECtype": variable.gettype(),
- "initial": repr(variable.getinitial()),
- "pyextname": pyextname},
- self.CodeFile.variables.variable)
- # python side PLC global variables access stub
- globalstubs = "\n".join(["""\
-_%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\
- TypeTranslator["%(IECtype)s"]
-_PySafeGetPLCGlob_%(name)s = PLCBinary.__SafeGetPLCGlob_%(name)s
-_PySafeGetPLCGlob_%(name)s.restype = None
-_PySafeGetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)]
-_PySafeSetPLCGlob_%(name)s = PLCBinary.__SafeSetPLCGlob_%(name)s
-_PySafeSetPLCGlob_%(name)s.restype = None
-_PySafeSetPLCGlob_%(name)s.argtypes = [ctypes.POINTER(_%(name)s_ctype)]
-_%(pyextname)sGlobalsDesc.append((
- "%(name)s",
- "%(IECtype)s",
- %(initial)s,
- %(desc)s,
- %(onchange)s,
- %(opts)s))
-""" % varinfo
- for varinfo in varinfos])
-
- # Runtime calls (start, stop, init, and cleanup)
- rtcalls = ""
- for section in self.SECTIONS_NAMES:
- if section != "globals":
- rtcalls += "def _runtime_%s_%s():\n" % (location_str, section)
- sectiontext = self.GetSection(section).strip()
- if sectiontext:
- rtcalls += ' ' + \
- sectiontext.replace('\n', '\n ') + "\n\n"
- else:
- rtcalls += " pass\n\n"
-
- globalsection = self.GetSection("globals")
-
- PyFileContent = """\
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-## Code generated by Beremiz python mixin confnode
-##
-
-## Code for PLC global variable access
-from targets.typemapping import TypeTranslator
-import ctypes
-_%(pyextname)sGlobalsDesc = []
-__ext_name__ = "%(pyextname)s"
-PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc ))
-%(globalstubs)s
-
-## User code in "global" scope
-%(globalsection)s
-
-## Beremiz python runtime calls
-%(rtcalls)s
-
-del __ext_name__
-
-""" % locals()
-
- # write generated content to python file
- runtimefile_path = os.path.join(buildpath,
- "runtime_%s.py" % location_str)
- runtimefile = open(runtimefile_path, 'w')
- runtimefile.write(PyFileContent.encode('utf-8'))
- runtimefile.close()
-
- # C code for safe global variables access
-
- vardecfmt = """\
-extern __IEC_%(IECtype)s_t %(configname)s__%(uppername)s;
-IEC_%(IECtype)s __%(name)s_rbuffer = __INIT_%(IECtype)s;
-IEC_%(IECtype)s __%(name)s_wbuffer;
-long __%(name)s_rlock = 0;
-long __%(name)s_wlock = 0;
-int __%(name)s_wbuffer_written = 0;
-void __SafeGetPLCGlob_%(name)s(IEC_%(IECtype)s *pvalue){
- while(AtomicCompareExchange(&__%(name)s_rlock, 0, 1));
- *pvalue = __%(name)s_rbuffer;
- AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
-}
-void __SafeSetPLCGlob_%(name)s(IEC_%(IECtype)s *value){
- while(AtomicCompareExchange(&__%(name)s_wlock, 0, 1));
- __%(name)s_wbuffer = *value;
- __%(name)s_wbuffer_written = 1;
- AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
-}
-
-"""
-
- vardeconchangefmt = """\
-PYTHON_POLL* __%(name)s_notifier;
-"""
-
- varretfmt = """\
- if(!AtomicCompareExchange(&__%(name)s_wlock, 0, 1)){
- if(__%(name)s_wbuffer_written == 1){
- %(configname)s__%(uppername)s.value = __%(name)s_wbuffer;
- __%(name)s_wbuffer_written = 0;
- }
- AtomicCompareExchange((long*)&__%(name)s_wlock, 1, 0);
- }
-"""
- varpubfmt = """\
- if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
- __%(name)s_rbuffer = __GET_VAR(%(configname)s__%(uppername)s);
- AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
- }
-"""
-
- varpubonchangefmt = """\
- if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
- IEC_%(IECtype)s tmp = __GET_VAR(%(configname)s__%(uppername)s);
- if(__%(name)s_rbuffer != tmp){
- __%(name)s_rbuffer = %(configname)s__%(uppername)s.value;
- PYTHON_POLL_body__(__%(name)s_notifier);
- }
- AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
- }
-"""
- varinitonchangefmt = """\
- __%(name)s_notifier = __GET_GLOBAL_ON%(uppername)sCHANGE();
- __SET_VAR(__%(name)s_notifier->,TRIG,,__BOOL_LITERAL(TRUE));
- __SET_VAR(__%(name)s_notifier->,CODE,,__STRING_LITERAL(%(onchangelen)d,%(onchangecode)s));
-"""
- vardec = "\n".join([(vardecfmt + vardeconchangefmt
- if varinfo["onchange"] else vardecfmt) % varinfo
- for varinfo in varinfos])
- varret = "\n".join([varretfmt % varinfo for varinfo in varinfos])
- varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else
- varpubfmt) % varinfo
- for varinfo in varinfos])
- varinit = "\n".join([varinitonchangefmt % dict(
- onchangelen=len(varinfo["onchangecode"]), **varinfo)
- for varinfo in varinfos if varinfo["onchange"]])
-
- # TODO : use config name obtained from model instead of default
- # "config.h". User cannot change config name, but project imported
- # or created in older beremiz vesion could use different name.
- PyCFileContent = """\
-/*
- * Code generated by Beremiz py_ext confnode
- * for safe global variables access
- */
-#include "iec_types_all.h"
-#include "POUS.h"
-#include "config.h"
-#include "beremiz.h"
-
-/* User variables reference */
-%(vardec)s
-
-/* Beremiz confnode functions */
-int __init_%(location_str)s(int argc,char **argv){
-%(varinit)s
- return 0;
-}
-
-void __cleanup_%(location_str)s(void){
-}
-
-void __retrieve_%(location_str)s(void){
-%(varret)s
-}
-
-void __publish_%(location_str)s(void){
-%(varpub)s
-}
-""" % locals()
-
- Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str)
- pycfile = open(Gen_PyCfile_path, 'w')
- pycfile.write(PyCFileContent)
- pycfile.close()
-
- matiec_CFLAGS = '"-I%s"' % os.path.abspath(
- self.GetCTRoot().GetIECLibPath())
-
- return ([(Gen_PyCfile_path, matiec_CFLAGS)],
- "",
- True,
- ("runtime_%s.py" % location_str, file(runtimefile_path, "rb")))
-PythonFileCTNMixin.CTNGenerate_C = CTNGenerate_C
-
-def ResetSearchResults(self):
- self.Highlights = []
- self.SearchParams = None
- self.SearchResults = None
- self.VariableSearchResults = []
- self.CurrentFindHighlight = None
- self.index = 0
-CodeEditor.ResetSearchResults = ResetSearchResults
-
-from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD
-[STC_CODE_ERROR, STC_CODE_SEARCH_RESULT,
- STC_CODE_SECTION] = range(15, 18)
-
-HIGHLIGHT_TYPES = {
- ERROR_HIGHLIGHT: STC_CODE_ERROR,
- SEARCH_RESULT_HIGHLIGHT: STC_CODE_SEARCH_RESULT,
-}
-def FindVariable(self, direction, search_params, variables):
- first = False
- if self.SearchParams != search_params:
- first = True
- self.SearchParams = search_params
- if len(self.VariableSearchResults) > 0:
- (r, c) = self.VariableSearchResults[self.index]
- variables.VariablesGrid.SetCellBackgroundColour(r, c, SEARCH_RESULT_HIGHLIGHT[1])
- grid = variables.VariablesGrid
- cols = grid.GetNumberCols()
- rows = grid.GetNumberRows()
- self.VariableSearchResults = []
- for row in range(rows):
- for col in range(cols):
- value = variables.VariablesGrid.GetCellValue(row, col)
- search_value = search_params['find_pattern']
- if search_params['case_sensitive'] == False:
- search_value = search_value.lower()
- value = value.lower()
- if search_value in value:
- self.VariableSearchResults.append((row, col))
- self.index = 0
- if len(self.VariableSearchResults) > 0:
- (r, c) = self.VariableSearchResults[self.index]
- variables.VariablesGrid.SetCellBackgroundColour(r, c, SEARCH_RESULT_HIGHLIGHT[1])
- if not first:
- self.index += direction
- self.index = self.index % len(self.VariableSearchResults)
- (r, c) = self.VariableSearchResults[self.index]
- variables.VariablesGrid.SetCellBackgroundColour(r, c, SEARCH_RESULT_HIGHLIGHT[0])
- variables.VariablesGrid.MakeCellVisible(r, c)
- variables.VariablesGrid.ForceRefresh()
-CodeEditor.FindVariable = FindVariable
-
-def SearchInPyfile(self, criteria):
- result = []
- from xml.dom import minidom
- dir_list = next(os.walk(self.ProjectPath))[1]
- for dir in dir_list:
- if dir not in ["build", "CanOpen@CanOpen", "project_files"]:
- path = os.path.join(self.ProjectPath, dir, 'pyfile.xml')
- variablelist = []
- if os.path.exists(path):
- pyfile = minidom.parse(path)
- variablelist = pyfile.getElementsByTagName('variable')
- for s in variablelist:
- if criteria["find_pattern"] in s.attributes['name'].value:
- result.append(dir)
- return result
-PLCControler.SearchInPyfile = SearchInPyfile
-
-def OnSearchInProjectMenu(self, event):
- dialog = SearchInProjectDialog(self)
- if dialog.ShowModal() == wx.ID_OK:
- criteria = dialog.GetCriteria()
- if len(criteria) > 0:
- result = self.Controler.SearchInProject(criteria)
- pyresult = self.Controler.SearchInPyfile(criteria)
- self.ClearSearchResults()
- self.SearchResultPanel.SetSearchResults(criteria, result, pyresult)
- self.SearchResultPanel.AddPyFileResults(pyresult)
- self.SelectTab(self.SearchResultPanel)
-IDEFrame.OnSearchInProjectMenu = OnSearchInProjectMenu
-
-def GenerateProjectTreeBranch(self, root, infos, item_alone=False):
- to_delete = []
- item_name = infos["name"]
- if infos["type"] in ITEMS_UNEDITABLE:
- if len(infos["values"]) == 1:
- return self.GenerateProjectTreeBranch(root, infos["values"][0], True)
- item_name = _(item_name)
- self.ProjectTree.SetItemText(root, item_name)
- self.ProjectTree.SetPyData(root, infos)
- highlight_colours = self.Highlights.get(infos.get("tagname", None), (wx.WHITE, wx.BLACK))
- self.ProjectTree.SetItemBackgroundColour(root, highlight_colours[0])
- self.ProjectTree.SetItemTextColour(root, highlight_colours[1])
- self.ProjectTree.SetItemExtraImage(root, None)
- if infos["type"] == ITEM_POU:
- self.ProjectTree.SetItemImage(root,
- self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])])
- if item_alone:
- self.ProjectTree.SetItemExtraImage(root, self.Controler.GetPouType(infos["name"]))
- elif infos.has_key("icon") and infos["icon"] is not None:
- icon_name = infos["icon"]
- if not self.TreeImageDict.has_key(icon_name):
- self.TreeImageDict[icon_name] = self.TreeImageList.Add(GetBitmap(icon_name))
- self.ProjectTree.SetItemImage(root, self.TreeImageDict[icon_name])
- elif self.TreeImageDict.has_key(infos["type"]):
- self.ProjectTree.SetItemImage(root, self.TreeImageDict[infos["type"]])
-
- item, root_cookie = self.ProjectTree.GetFirstChild(root)
- for values in infos["values"]:
- if values["type"] not in ITEMS_UNEDITABLE or len(values["values"]) > 0:
- if item is None or not item.IsOk():
- item = self.ProjectTree.AppendItem(root, "")
- item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
- self.GenerateProjectTreeBranch(item, values)
- item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
- while item is not None and item.IsOk():
- to_delete.append(item)
- item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
- for item in to_delete:
- self.ProjectTree.Delete(item)
-IDEFrame.GenerateProjectTreeBranch = GenerateProjectTreeBranch
-
-defaultSearchResultPanelInit = SearchResultPanel.__init__
-def SearchResultPanelInit(self, parent, window):
- defaultSearchResultPanelInit(self, parent, window)
- self.TreeImageDict["py_file"] = self.TreeImageList.Add(GetBitmap("py_file"))
- self.TreeImageDict["wx_glade"] = self.TreeImageList.Add(GetBitmap("wx_glade"))
-SearchResultPanel.__init__ = SearchResultPanelInit
-
-def SetSearchResults(self, criteria, search_results, py_results):
- self.Criteria = criteria
- self.SearchResults = {}
- self.ElementsOrder = []
- self.PySearchResults = py_results
-
- for infos, start, end, text in search_results:
- if infos[0] not in self.ElementsOrder:
- self.ElementsOrder.append(infos[0])
-
- results = self.SearchResults.setdefault(infos[0], [])
- results.append((infos, start, end, text))
-
- self.RefreshView()
-SearchResultPanel.SetSearchResults = SetSearchResults
-
-def AddPyFileResults(self, search_results):
- distinct_list = list(set(search_results))
- for result in distinct_list:
- name = result.split('@')
- matches = search_results.count(result)
- if name[1] == "py_ext":
- root = self.SearchResultsTree.GetRootItem()
- self.SearchResultsTree.AppendItem(root, name[0], image=self.TreeImageDict["py_file"])
- else:
- root = self.SearchResultsTree.GetRootItem()
- self.SearchResultsTree.AppendItem(root, name[0], image=self.TreeImageDict["wx_glade"])
- if matches > 1:
- text = _("(%d matches)") % search_results.count(result)
- start_idx, end_idx = 0, len(text)
- style = wx.TextAttr(wx.Colour(0, 127, 174))
- text_ctrl_style = wx.BORDER_NONE | wx.TE_READONLY | wx.TE_RICH2
- if wx.Platform != '__WXMSW__' or len(text.splitlines()) > 1:
- text_ctrl_style |= wx.TE_MULTILINE
- text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0),
- value=text, style=text_ctrl_style)
- width, height = text_ctrl.GetTextExtent(text)
- text_ctrl.SetClientSize(wx.Size(width + 1, height))
- text_ctrl.SetBackgroundColour(self.SearchResultsTree.GetBackgroundColour())
- child = self.SearchResultsTree.GetLastChild(root)
- text_ctrl.SetInsertionPoint(0)
- text_ctrl.SetStyle(start_idx, end_idx, style)
- self.SearchResultsTree.SetItemWindow(child, text_ctrl)
-SearchResultPanel.AddPyFileResults = AddPyFileResults
-
-
-def ResetSearchResults(self):
- self.Criteria = None
- self.ElementsOrder = []
- self.SearchResults = {}
- self.PySearchResults = []
- self.RefreshView()
-SearchResultPanel.ResetSearchResults = ResetSearchResults
-
-
-def GetPythonTextCtrlDClickFunction(self, item):
- for type in self.ParentWindow.CTR.Children.values():
- for name in type:
- if name.BaseParams.attrib['Name'] == item:
- name._OpenView()
- selected = self.ParentWindow.TabsOpened.GetSelection()
- if selected != -1:
- window = self.ParentWindow.TabsOpened.GetPage(selected)
- window.CodeEditor.FindVariable(1, {'find_pattern': self.Criteria['find_pattern'],
- 'regular_expression': self.Criteria['regular_expression'],
- 'pattern': self.Criteria['pattern'],
- 'case_sensitive': self.Criteria['case_sensitive'],
- 'filter': self.Criteria['filter']}, window.VariablesPanel)
- for (r, c) in window.CodeEditor.VariableSearchResults:
- window.VariablesPanel.VariablesGrid.SetCellBackgroundColour(r, c,
- SEARCH_RESULT_HIGHLIGHT[0])
-SearchResultPanel.GetPythonTextCtrlDClickFunction = GetPythonTextCtrlDClickFunction
-
-
-def OnSearchResultsTreeItemActivated(self, event):
- self.ShowSearchResults(event.GetItem())
- if event.GetItem()._data is None:
- self.GetPythonTextCtrlDClickFunction(event.GetItem()._text)
- event.Skip()
-SearchResultPanel.OnSearchResultsTreeItemActivated = OnSearchResultsTreeItemActivated
-
-defaultProjectPropertiesPanelInit = ProjectPropertiesPanel.__init__
-def OurProjectPropertiesPanelInit(self, parent, controller=None, window=None, enable_required=True):
-
- REQUIRED_PARAMS = ["projectName", "productName", "productVersion", "companyName"]
- [TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE,
- POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES
- ] = range(10)
-
- def create_project_panel(self):
- self.ProjectPanel = ScrolledPanel(self, -1, style=wx.TAB_TRAVERSAL)
- self.ProjectPanel.SetAutoLayout(1)
- self.ProjectPanel.SetupScrolling()
- self.AddPage(self.ProjectPanel, _("Project"))
-
- projectpanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=5, vgap=15)
- projectpanel_sizer.AddGrowableCol(1)
- self.ProjectPanel.SetSizer(projectpanel_sizer)
-
- self.AddSizerParams(self.ProjectPanel, projectpanel_sizer,
- [("projectName", _('Project Name (required):')),
- ("projectVersion", _('Project Version (optional):')),
- ("productName", _('Product Name (required):')),
- ("productVersion", _('Product Version (required):')),
- ("productRelease", _('Product Release (optional):'))])
-
- self.AddPage(self.ProjectPanel, _("Project"))
-
- def create_author_panel(self):
- self.AuthorPanel = ScrolledPanel(self, -1, style=wx.TAB_TRAVERSAL)
- self.AuthorPanel.SetAutoLayout(1)
- self.AuthorPanel.SetupScrolling()
-
- authorpanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=4, vgap=15)
- authorpanel_sizer.AddGrowableCol(1)
- self.AuthorPanel.SetSizer(authorpanel_sizer)
-
- self.AddSizerParams(self.AuthorPanel, authorpanel_sizer,
- [("companyName", _('Company Name (required):')),
- ("companyURL", _('Company URL (optional):')),
- ("authorName", _('Author Name (optional):')),
- ("organization", _('Organization (optional):'))])
-
- self.AddPage(self.AuthorPanel, _("Author"))
-
- def create_graphic_panel(self):
- self.GraphicsPanel = ScrolledPanel(self, -1, style=wx.TAB_TRAVERSAL)
- self.GraphicsPanel.SetAutoLayout(1)
- self.GraphicsPanel.SetupScrolling()
-
- graphicpanel_sizer = wx.FlexGridSizer(cols=1, hgap=5, rows=4, vgap=5)
- graphicpanel_sizer.AddGrowableCol(0)
- graphicpanel_sizer.AddGrowableRow(3)
- self.GraphicsPanel.SetSizer(graphicpanel_sizer)
-
- pageSize_st = wx.StaticText(self.GraphicsPanel,
- label=_('Page Size (optional):'))
- graphicpanel_sizer.AddWindow(pageSize_st, border=10,
- flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP | wx.LEFT | wx.RIGHT)
-
- pageSize_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5)
- pageSize_sizer.AddGrowableCol(1)
- graphicpanel_sizer.AddSizer(pageSize_sizer, border=10,
- flag=wx.GROW | wx.LEFT | wx.RIGHT)
-
- for name, label in [('PageWidth', _('Width:')), ('PageHeight', _('Height:'))]:
- st = wx.StaticText(self.GraphicsPanel, label=label)
- pageSize_sizer.AddWindow(st, border=12,
- flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT)
-
- sp = wx.SpinCtrl(self.GraphicsPanel,
- min=0, max=2 ** 16, style=wx.TE_PROCESS_ENTER)
- setattr(self, name, sp)
- callback = self.GetPageSizeChangedFunction(sp, name)
- self.Bind(wx.EVT_TEXT_ENTER, callback, sp)
- sp.Bind(wx.EVT_KILL_FOCUS, callback)
- pageSize_sizer.AddWindow(sp, flag=wx.GROW)
-
- scaling_st = wx.StaticText(self.GraphicsPanel,
- label=_('Grid Resolution:'))
- graphicpanel_sizer.AddWindow(scaling_st, border=10,
- flag=wx.GROW | wx.LEFT | wx.RIGHT)
-
- scaling_nb = wx.Notebook(self.GraphicsPanel)
- graphicpanel_sizer.AddWindow(scaling_nb, border=10,
- flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT)
-
- self.Scalings = {}
- for language, translation in [("FBD", _("FBD")), ("LD", _("LD")), ("SFC", _("SFC"))]:
- scaling_panel = wx.Panel(scaling_nb, style=wx.TAB_TRAVERSAL)
- scalingpanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5)
- scalingpanel_sizer.AddGrowableCol(1)
- scaling_panel.SetSizer(scalingpanel_sizer)
-
- scaling_controls = []
- for idx, (name, label) in enumerate([('XScale', _('Horizontal:')),
- ('YScale', _('Vertical:'))]):
- if idx == 0:
- border = wx.TOP
- else:
- border = wx.BOTTOM
-
- st = wx.StaticText(scaling_panel, label=label)
- scalingpanel_sizer.AddWindow(st, border=10,
- flag=wx.ALIGN_CENTER_VERTICAL | border | wx.LEFT)
-
- sp = wx.SpinCtrl(scaling_panel,
- min=0, max=2 ** 16, style=wx.TE_PROCESS_ENTER)
- scaling_controls.append(sp)
- callback = self.GetScalingChangedFunction(sp, language, name)
- self.Bind(wx.EVT_TEXT_ENTER, callback, sp)
- sp.Bind(wx.EVT_KILL_FOCUS, callback)
- scalingpanel_sizer.AddWindow(sp, border=10,
- flag=wx.GROW | border | wx.RIGHT)
-
- self.Scalings[language] = scaling_controls
- scaling_nb.AddPage(scaling_panel, translation)
-
- self.AddPage(self.GraphicsPanel, _("Graphics"))
-
- def create_miscellaneous_panel(self):
- self.MiscellaneousPanel = ScrolledPanel(id=-1, parent=self,
- name='MiscellaneousPanel', pos=wx.Point(0, 0),
- size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL)
- self.MiscellaneousPanel.SetAutoLayout(1)
- self.MiscellaneousPanel.SetupScrolling()
-
- miscellaneouspanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=15)
- miscellaneouspanel_sizer.AddGrowableCol(1)
- miscellaneouspanel_sizer.AddGrowableRow(1)
- self.MiscellaneousPanel.SetSizer(miscellaneouspanel_sizer)
-
- language_label = wx.StaticText(self.MiscellaneousPanel,
- label=_('Language (optional):'))
- miscellaneouspanel_sizer.AddWindow(language_label, border=10,
- flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP | wx.LEFT)
-
- self.Language = wx.ComboBox(self.MiscellaneousPanel,
- style=wx.CB_READONLY)
- self.Bind(wx.EVT_COMBOBOX, self.OnLanguageChanged, self.Language)
- miscellaneouspanel_sizer.AddWindow(self.Language, border=10,
- flag=wx.GROW | wx.TOP | wx.RIGHT)
-
- description_label = wx.StaticText(self.MiscellaneousPanel,
- label=_('Content Description (optional):'))
- miscellaneouspanel_sizer.AddWindow(description_label, border=10,
- flag=wx.BOTTOM | wx.LEFT)
-
- self.ContentDescription = wx.TextCtrl(self.MiscellaneousPanel,
- style=wx.TE_MULTILINE | wx.TE_PROCESS_ENTER)
- self.Bind(wx.EVT_TEXT_ENTER, self.OnContentDescriptionChanged,
- self.ContentDescription)
- self.ContentDescription.Bind(wx.EVT_KILL_FOCUS,
- self.OnContentDescriptionChanged)
- miscellaneouspanel_sizer.AddWindow(self.ContentDescription, border=10,
- flag=wx.GROW | wx.BOTTOM | wx.RIGHT)
-
- self.AddPage(self.MiscellaneousPanel, _("Miscellaneous"))
-
- wx.Notebook.__init__(self, parent, size=wx.Size(500, 300))
-
- self.Controller = controller
- self.ParentWindow = window
- self.Values = None
-
- create_project_panel(self)
-
- create_author_panel(self)
-
- create_graphic_panel(self)
-
- create_miscellaneous_panel(self)
-
- for param in REQUIRED_PARAMS:
- getattr(self, param).Enable(enable_required)
- languages = ["", "en-US", "fr-FR", "zh-CN", "ru-RU"]
- for language in languages:
- self.Language.Append(language)
-ProjectPropertiesPanel.__init__ = OurProjectPropertiesPanelInit
-
-
-def LeftClick(self, event):
- if event.GetCol() == self.grid.GetNumberCols()-1:
- options = [self.grid.GetCellValue(event.GetRow(), event.GetCol()), self.grid.GetCellValue(event.GetRow(), event.GetCol()-1)]
- desc = self.grid.GetCellValue(event.GetRow(), event.GetCol()-2)
- if hasattr(self, "dialog"):
- self.dialog.SetOptions(options, desc)
- answer = self.dialog.ShowModal()
- #self.dialog.SetOptions(options, desc)
- else:
- self.dialog = WampOptionsEditor(self.Parent.Parent, options, desc)
- answer = self.dialog.ShowModal()
- opt,OnChange,value,description = self.dialog.GetOptions()
- if answer == wx.ID_OK:
- self.grid.SetCellValue(event.GetRow(), event.GetCol(), str(opt))
- if OnChange:
- self.grid.SetCellValue(event.GetRow(), event.GetCol()-1, value)
- self.grid.SetCellValue(event.GetRow(), event.GetCol()-2, description)
- self.Parent.RefreshModel()
-
- else:
- event.Skip()
-VariablesTable.LeftClick = LeftClick
-
-def VariablesEditorSetCollSize(self):
- ColSizes = [20, 150] + [130] * (len(self.VariablesDefaultValue) - 2) + [300]
- for col in range(self.Table.GetNumberCols()):
- self.VariablesGrid.SetColSize(col, ColSizes[col])
-VariablesEditor.VariablesEditorSetCollSize = VariablesEditorSetCollSize
-
-def _updateColAttrs(self, grid):
- """
- wxGrid -> update the column attributes to add the
- appropriate renderer given the column name.
-
- Otherwise default to the default renderer.
- """
- typelist = None
- accesslist = None
- self.grid = grid
- for row in range(self.GetNumberRows()):
- for col in range(self.GetNumberCols()):
- editor = None
- renderer = None
- colname = self.GetColLabelValue(col, False)
-
- if colname in ["Name", "Initial", "Description", "OnChange"]:
- editor = wx.grid.GridCellTextEditor()
- elif colname == "Class":
- editor = wx.grid.GridCellChoiceEditor()
- editor.SetParameters("input,memory,output")
- elif colname == "Type":
- pass
- else:
- grid.SetReadOnly(row, col, True)
-
- grid.SetCellEditor(row, col, editor)
- grid.SetCellRenderer(row, col, renderer)
-
- grid.SetCellBackgroundColour(row, col, wx.WHITE)
-
- self.grid.SetRowMinimalHeight
- # updated column width, with function VariablesEditorSetCollSize added in VariablesEditor class
- self.Parent.VariablesEditorSetCollSize()
- # added left click option on grid, with function LeftClick added in VariablesTable class
- self.grid.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.LeftClick)
-VariablesTable._updateColAttrs = _updateColAttrs
-
-"""Zakomentirano ker je ze v beremiz-u"""
-# def OnCleanButton(self, event):
-# if self.LogSource is not None:
-# rez = self.LogSource.ResetLogCount()
-# if not rez:
-# self.GrandParent.CTR.logger.write_warning("Can not reset log messages!\n")
-# self.ResetLogMessages()
-# self.RefreshView()
-# event.Skip()
-# LogViewer.OnCleanButton = OnCleanButton
-
-from PLCControler import PLCControler, LOCATION_MODULE, LOCATION_GROUP
-
-defaultGenerateNewName = PLCControler.GenerateNewName
-def newGenerateNewName(self, tagname, name, format, start_idx=0, exclude={}, debug=False):
- if tagname:
- return defaultGenerateNewName(self, tagname, name, format, start_idx=0, exclude={}, debug=False)
- else:
- names = exclude.copy()
- i = start_idx
- while name is None or names.get(name.upper(), False):
- name = (format%i)
- i += 1
- return name
-PLCControler.GenerateNewName = newGenerateNewName
-
-
-havecanfestival = False
-# try:
-from canfestival import RootClass as CanOpenRootClass
-from canfestival.canfestival import _SlaveCTN, _NodeListCTN, NodeManager
-from canfestival.NetworkEditor import NetworkEditor
-from canfestival.SlaveEditor import SlaveEditor
-
-havecanfestival = True
-# except:
-# havecanfestival = False
-
-SCROLLBAR_UNIT = 10
-ID_EXPORT = 7500
-WINDOW_COLOUR = wx.Colour(240, 240, 240)
-TITLE_COLOUR = wx.Colour(200, 200, 220)
-CHANGED_TITLE_COLOUR = wx.Colour(220, 200, 220)
-CHANGED_WINDOW_COLOUR = wx.Colour(255, 240, 240)
-
-if wx.Platform == '__WXMSW__':
- faces = {'times': 'Times New Roman',
- 'mono': 'Courier New',
- 'helv': 'Arial',
- 'other': 'Comic Sans MS',
- 'size': 16,
- }
-else:
- faces = {'times': 'Times',
- 'mono': 'Courier',
- 'helv': 'Helvetica',
- 'other': 'new century schoolbook',
- 'size': 18,
- }
-
-# ConfTreeNodeEditor.SHOW_BASE_PARAMS = False
-
-# -------------------------------------------------------------------------------
-# CANFESTIVAL CONFNODE HACK
-# -------------------------------------------------------------------------------
-from canfestival import canfestival
-defaultGetCFLAGS = canfestival.local_canfestival_config.getCFLAGS
-defaultGetLDFLAGS = canfestival.local_canfestival_config.getLDFLAGS
-
-def getCFLAGS(*args):
- return str(defaultGetCFLAGS(*args))
-
-def getLDFLAGS(*args):
- return str(defaultGetLDFLAGS(*args))
-
-canfestival.local_canfestival_config.getCFLAGS = getCFLAGS
-canfestival.local_canfestival_config.getLDFLAGS = getLDFLAGS
-
-import LPCBus as LPCBus_mod
-
-if arch in PLC_module:
- LPCBus_mod.LPCarch = "MC9"
- LPCBus_mod.arch = arch
-else:
- LPCBus_mod.LPCarch = arch
- LPCBus_mod.arch = arch
-
-from LPCBus import *
-
-# -------------------------------------------------------------------------------
-# LPC CanFestival ConfNode Class
-# -------------------------------------------------------------------------------
-
-if havecanfestival:
- DEFAULT_SETTINGS = {
- "CAN_Baudrate": "125K",
- "Slave_NodeId": 2,
- "Master_NodeId": 1,
- }
-
-
-class LPCSlaveEditor(SlaveEditor):
- # SHOW_BASE_PARAMS = False
- pass
-
-
-class LPCCanOpenSlave(_SlaveCTN):
- XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
- <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <xsd:element name="CanFestivalSlaveNode">
- <xsd:complexType>
- <xsd:attribute name="CAN_Baudrate" type="xsd:string" use="optional" default="%(CAN_Baudrate)s"/>
- <xsd:attribute name="NodeId" type="xsd:integer" use="optional" default="%(Slave_NodeId)d"/>
- <xsd:attribute name="Sync_Align" type="xsd:integer" use="optional" default="0"/>
- <xsd:attribute name="Sync_Align_Ratio" use="optional" default="50">
- <xsd:simpleType>
- <xsd:restriction base="xsd:integer">
- <xsd:minInclusive value="1"/>
- <xsd:maxInclusive value="99"/>
- </xsd:restriction>
- </xsd:simpleType>
- </xsd:attribute>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- """ % DEFAULT_SETTINGS
-
- EditorType = LPCSlaveEditor
-
- def __init__(self):
- # TODO change netname when name change
- NodeManager.__init__(self)
- odfilepath = self.GetSlaveODPath()
- if (os.path.isfile(odfilepath)):
- self.OpenFileInCurrent(odfilepath)
- else:
- self.CreateNewNode("SlaveNode", # Name - will be changed at build time
- 0x00, # NodeID - will be changed at build time
- "slave", # Type
- "", # description
- "None", # profile
- "", # prfile filepath
- "heartbeat", # NMT
- []) # options
- self.OnCTNSave()
-
- def GetCanDevice(self):
- return str(self.BaseParams.getIEC_Channel())
-
- ConfNodeMethods = [
- {"bitmap": "NetworkEdit",
- "name": _("Edit slave"),
- "tooltip": _("Edit CanOpen slave with ObjdictEdit"),
- "method": "_OpenView"},
- ] + _SlaveCTN.ConfNodeMethods
-
-
-class LPCNetworkEditor(NetworkEditor):
- # SHOW_BASE_PARAMS = False
- pass
-
-
-class LPCCanOpenMaster(_NodeListCTN):
- XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
- <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <xsd:element name="CanFestivalNode">
- <xsd:complexType>
- <xsd:attribute name="CAN_Baudrate" type="xsd:string" use="optional" default="%(CAN_Baudrate)s"/>
- <xsd:attribute name="NodeId" type="xsd:integer" use="optional" default="%(Master_NodeId)d"/>
- <xsd:attribute name="Sync_TPDOs" type="xsd:boolean" use="optional" default="true"/>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- """ % DEFAULT_SETTINGS
-
- EditorType = LPCNetworkEditor
-
- def GetCanDevice(self):
- return str(self.BaseParams.getIEC_Channel())
-
- ConfNodeMethods = [
- {"bitmap": "NetworkEdit",
- "name": _("Edit network"),
- "tooltip": _("Edit CanOpen Network with NetworkEdit"),
- "method": "_OpenView"},
- ] + _NodeListCTN.ConfNodeMethods
-
-
-class LPCCanOpen(CanOpenRootClass):
- XSD = None
- CTNChildrenTypes = [("CanOpenNode", LPCCanOpenMaster, "CanOpen Master"),
- ("CanOpenSlave", LPCCanOpenSlave, "CanOpen Slave")]
-
- def GetCanDriver(self):
- return None
-
- def LoadChildren(self):
- ConfigTreeNode.LoadChildren(self)
-
- if self.GetChildByName("Master") is None:
- master = self.CTNAddChild("Master", "CanOpenNode", 0)
- # master.BaseParams.setEnabled(False)
- master.CTNRequestSave()
-
- if self.GetChildByName("Slave") is None:
- slave = self.CTNAddChild("Slave", "CanOpenSlave", 1)
- # slave.BaseParams.setEnabled(False)
- slave.CTNRequestSave()
-
-# -------------------------------------------------------------------------------
-# LPCProjectController Class
-# -------------------------------------------------------------------------------
-
-def mycopytree(src, dst):
- """
- Copy content of a directory to an other, omit hidden files
- @param src: source directory
- @param dst: destination directory
- """
- for i in os.listdir(src):
- if not i.startswith('.'):
- srcpath = os.path.join(src, i)
- dstpath = os.path.join(dst, i)
- if os.path.isdir(srcpath):
- if os.path.exists(dstpath):
- shutil.rmtree(dstpath)
- os.makedirs(dstpath)
- mycopytree(srcpath, dstpath)
- elif os.path.isfile(srcpath):
- shutil.copy2(srcpath, dstpath)
-
-
-[SIMULATION_MODE, TRANSFER_MODE] = range(2)
-
-if arch in PLC_module:
- class LPCProjectNodeEditor(ProjectNodeEditor):
- pass
-else:
- class LPCProjectNodeEditor(ProjectNodeEditor):
- SHOW_PARAMS = False
- ENABLE_REQUIRED = False
-
-_StatusMethods = [
- {"bitmap": "Debug",
- "name": _("Simulate"),
- "tooltip": _("Simulate PLC"),
- "method": "_Simulate"},
- {"bitmap": "Run",
- "name": _("Run"),
- "shown": False,
- "tooltip": _("Start PLC"),
- "method": "_Run"},
- {"bitmap": "Stop",
- "name": _("Stop"),
- "shown": False,
- "tooltip": _("Stop Running PLC"),
- "method": "_Stop"},
- {"bitmap": "Build",
- "name": _("Build"),
- "tooltip": _("Build project into build folder"),
- "method": "_Build"},
- {"bitmap": "Clean",
- "name": _("Clean"),
- "enabled": False,
- "tooltip": _("Clean project build folder"),
- "method": "_Clean"},
- {"bitmap": "Transfer",
- "name": _("Transfer"),
- "shown": False,
- "tooltip": _("Transfer PLC"),
- "method": "_Transfer"},
-]
-_MethodFromPLCState = {
- "Started": [("_Simulate", False),
- ("_Run", False),
- ("_Stop", True),
- ("_Build", True),
- ("_Transfer", True)],
- "Stopped": [("_Simulate", False),
- ("_Run", True),
- ("_Stop", False),
- ("_Build", True),
- ("_Transfer", True)],
- "Connected": [("_Simulate", "not simulating"),
- ("_Run", True),
- ("_Stop", True if arch in PLC_module else "simulating"),
- ("_Build", True),
- ("_Transfer", True)],
- "Disconnected": [("_Simulate", "not simulating"),
- ("_Run", False),
- ("_Stop", False if arch in PLC_module else "simulating"),
- ("_Build", True),
- ("_Transfer", False)],
- "Empty" :[("_Simulate", "not simulating"),
- ("_Run", False),
- ("_Stop", False),
- ("_Build", True),
- ("_Transfer", True)],
-}
-
-if arch in PLC_module:
- _StatusMethods += [
- {"bitmap": "Connect",
- "name": _("Connect"),
- "shown": True,
- "tooltip": _("Connect to the target PLC"),
- "method": "_Connect"},
- {"bitmap": "Disconnect",
- "name": _("Disconnect"),
- "shown": False,
- "tooltip": _("Disconnect from PLC"),
- "method": "_Disconnect"},
- {"bitmap": "UpdateFw",
- "name": _("UpdateFw"),
- "shown": True,
- "tooltip": _("Update the PLC firmware"),
- "method": "_UpdateFw"},
- ]
- _MethodFromPLCState["Disconnected"] += [("_Connect", True),
- ("_Disconnect", False)]
-
- _MethodFromPLCState["Connected"] += [("_Connect", False),
- ("_Disconnect", True)]
-
-class LPCProjectController(ProjectController):
- StatusMethods = _StatusMethods
- # ConfNodeMethods = []
-
- EditorType = LPCProjectNodeEditor
-
- def __init__(self, frame, logger, buildpath):
- self.arch = arch
- self.OrigBuildPath = buildpath
-
- ProjectController.__init__(self, frame, logger)
-
- if havecanfestival:
- self.CTNChildrenTypes += [("LPCBus", LPCBus, "LPC bus"), ("CanOpen", LPCCanOpen, "CanOpen bus")]
- else:
- self.CTNChildrenTypes += [("LPCBus", LPCBus, "LPC bus")]
- self.CTNType = "LPC"
-
- self.OnlineMode = "NORMAL" if arch in PLC_module else "OFF"
- self.LPCConnector = None
- self.ConnectorPath = None
-
- self.CurrentMode = None
- self.previous_mode = None
-
- self.SimulationBuildPath = None
-
- self.AbortTransferTimer = None
-
- # Firmware update running status
- self.firmawreUpadateIsRunning = False
- self.building = False
-
-
- # Bind mouse double click event on URI_location in Beremiz
- self.UriOptions = True
-
- def GetProjectName(self):
- return self.Project.getname()
-
- def GetDefaultTargetName(self):
- if self.CurrentMode == SIMULATION_MODE:
- return ProjectController.GetDefaultTargetName(self)
- else:
- return "LPC"
-
- def GetTarget(self):
- target = ProjectController.GetTarget(self)
- if self.CurrentMode != SIMULATION_MODE and arch not in PLC_module:
- target.getcontent().setBuildPath(self.BuildPath)
- return target
-
- def _getBuildPath(self):
- if self.CurrentMode == SIMULATION_MODE:
- if self.SimulationBuildPath is None:
- self.SimulationBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build")
- return self.SimulationBuildPath
- else:
- return ProjectController._getBuildPath(self)
-
- def ToZIPFile(self):
- # MD5 = self.GetLastBuildMD5()
- try:
- path_export_file = self.BuildPath[:-5] + "\\" + self._builder.exe[:-3] + ".xEye"
- zf = zipfile.ZipFile(path_export_file, mode='w', compression=zipfile.ZIP_DEFLATED)
- for extrafilespath in [self._getExtraFilesPath(),
- self._getProjectFilesPath()]:
- dir = extrafilespath.split("\\")[-1]
- for name in os.listdir(extrafilespath):
- zf.write(extrafilespath + '\\' + str(name), dir + '\\' + str(name), zipfile.ZIP_DEFLATED)
- zf.write(self._builder.exe_path, self._builder.exe)
- zf.write(self.BuildPath + '\\lastbuildPLC.md5', 'lastbuildPLC.md5')
- zf.close()
- self.logger.write(_("Export file is successfully created on location: %s\n") % path_export_file)
- except Exception, e:
- self.logger.write(_("Export file is not created because eror: %s\n") % e)
-
- def _Build(self):
- self._Clean()
- save = self.ProjectTestModified()
- if save:
- self.SaveProject()
- self.AppFrame._Refresh(TITLE, FILEMENU)
- if self.BuildPath is not None:
- mycopytree(self.OrigBuildPath, self.BuildPath)
- if ProjectController._Build(self):
- self.ToZIPFile()
- if save:
- wx.CallAfter(self.AppFrame.RefreshAll)
-
- def SetProjectName(self, name):
- return self.Project.setname(name)
-
- def SetOnlineMode(self, mode, path=None):
- # SetOnlineMode is only for MC8
- if arch in PLC_module:
- return None
-
- mode = mode.upper()
- if self.OnlineMode != mode:
- if mode not in ["OFF", ""]:
- self.OnlineMode = mode
- self.ConnectorPath = path
- uri = "LPC://%s/%s" % (self.OnlineMode, path)
- try:
- self.LPCConnector = connectors.ConnectorFactory(uri, self)
- except Exception, msg:
- self.logger.write_error(_("Exception while connecting %s!\n") % uri)
- self.logger.write_error(traceback.format_exc())
-
- # Did connection success ?
- if self.LPCConnector is None:
- # Oups.
- self.logger.write_error(_("Connection failed to %s!\n") % uri)
-
- else:
- self.OnlineMode = "OFF"
- self.LPCConnector = None
- self.ConnectorPath = None
-
- self.ApplyOnlineMode()
-
-
- def ApplyOnlineMode(self):
- if self.CurrentMode != SIMULATION_MODE:
- self.KillDebugThread()
-
- self._SetConnector(self.LPCConnector)
-
- # Init with actual PLC status and print it
- self.UpdateMethodsFromPLCStatus()
-
- if self.LPCConnector is not None and self.OnlineMode == "APPLICATION":
-
- self.CompareLocalAndRemotePLC()
-
- if self.previous_plcstate is not None:
- status = _(self.previous_plcstate)
- else:
- status = ""
- self.logger.write(_("PLC is %s\n") % status)
-
- # if self.StatusTimer and not self.StatusTimer.IsRunning():
- # # Start the status Timer
- # self.StatusTimer.Start(milliseconds=2000, oneShot=False)
-
- if self.previous_plcstate == "Started":
- if self.DebugAvailable() and self.GetIECProgramsAndVariables():
- self.logger.write(_("Debug connect matching running PLC\n"))
- self._connect_debug()
- else:
- self.logger.write_warning(_("Debug do not match PLC - stop/transfert/start to re-enable\n"))
-
- elif self.StatusTimer and self.StatusTimer.IsRunning():
- self.StatusTimer.Stop()
-
- if self.CurrentMode == TRANSFER_MODE:
-
- if self.OnlineMode == "BOOTLOADER":
- self.BeginTransfer()
-
- elif self.OnlineMode == "APPLICATION":
- self.CurrentMode = None
- self.AbortTransferTimer.Stop()
- self.AbortTransferTimer = None
-
- self.logger.write(_("PLC transferred successfully\n"))
-
- # Update a PLCOpenEditor Pou variable location
- def UpdateProjectVariableLocation(self, old_leading, new_leading):
- self.Project.updateElementAddress(old_leading, new_leading)
- self.BufferProject()
-
- # Update a PLCOpenEditor Pou variable name
- def UpdateProjectVariableName(self, old_name, new_name):
- self.Project.updateElementName(old_name, new_name)
- self.BufferProject()
-
- def RemoveProjectVariableByAddress(self, address):
- self.Project.removeVariableByAddress(address)
- self.BufferProject()
-
- def RemoveProjectVariableByFilter(self, leading):
- self.Project.removeVariableByFilter(leading)
- self.BufferProject()
-
- def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"):
- ProjectController.AddProjectDefaultConfiguration(self, config_name, res_name)
-
- self.SetEditedResourceInfos(
- self.ComputeConfigurationResourceName(config_name, res_name),
- [{"Name": "main_task",
- "Triggering": "Cyclic",
- "Interval": "T#50ms",
- "Priority": 0}],
- [])
-
- def LoadProject(self, ProjectPath, BuildPath=None):
- """
- Load a project contained in a folder
- @param ProjectPath: path of the project folder
- """
- if os.path.basename(ProjectPath) == "":
- ProjectPath = os.path.dirname(ProjectPath)
-
- # Verify that project contains a PLCOpen program
- plc_file = os.path.join(ProjectPath, "plc.xml")
- if os.path.isfile(plc_file):
- # Load PLCOpen file
- result = self.OpenXMLFile(plc_file)
- if result:
- return result
- else:
- self.CreateNewProject({"companyName": "",
- "productName": "",
- "productVersion": "",
- "projectName": "",
- "pageSize": (0, 0),
- "scaling": {}})
- if len(self.GetProjectConfigNames()) == 0:
- self.AddProjectDefaultConfiguration()
-
- # Change XSD into class members
- self._AddParamsMembers()
- self.Children = {}
-
- # Keep track of the root confnode (i.e. project path)
- self.ProjectPath = ProjectPath
-
- self.BuildPath = self._getBuildPath()
- if self.OrigBuildPath is not None:
- mycopytree(self.OrigBuildPath, self.BuildPath)
-
- # If dir have already be made, and file exist
- if os.path.isdir(self.CTNPath()) and os.path.isfile(self.ConfNodeXmlFilePath()):
- # Load the confnode.xml file into parameters members
- result = self.LoadXMLParams()
- if result:
- return result
- # Load and init all the children
- self.LoadChildren()
-
- canopen_child = self.GetChildByName("CanOpen")
- if havecanfestival and canopen_child is None:
- canopen = self.CTNAddChild("CanOpen", "CanOpen", 0)
- canopen.LoadChildren()
- canopen.CTNRequestSave()
-
- elif not havecanfestival and canopen_child is not None:
- canopen_child.CTNRemove()
-
- if self.CTNTestModified():
- self.SaveProject()
-
- if wx.GetApp() is None:
- self.RefreshConfNodesBlockLists()
- else:
- wx.CallAfter(self.RefreshConfNodesBlockLists)
-
- if arch in PLC_module:
- self.SetParamsAttribute('BeremizRoot.TargetType', 'MC9')
-
- return None
-
- def IsPLCStarted(self):
- return self.previous_plcstate == "Started" or self.previous_mode == SIMULATION_MODE
-
- def ShowMethod(self, name, val):
- simulating = self.CurrentMode == SIMULATION_MODE
- if type(val) == str:
- if val.endswith("simulating"):
- if val.startswith("not"):
- val = not simulating
- else:
- val = simulating
-
- ProjectController.ShowMethod(self, name, val)
-
- def UpdateMethodsFromPLCStatus(self):
- simulating = self.CurrentMode == SIMULATION_MODE
- if self.OnlineMode == "OFF":
- if simulating:
- status, log_count = self._connector.GetPLCstatus()
- self.UpdatePLCLog(log_count)
- status = "Disconnected"
- elif self.OnlineMode == "BOOTLOADER":
- status = "Connected"
- else:
- if self._connector is not None:
- status, log_count = self._connector.GetPLCstatus()
- if status == "Disconnected":
- self._SetConnector(None, False)
- else:
- self.UpdatePLCLog(log_count)
- else:
- status = "Disconnected"
- if self.previous_plcstate != status or self.previous_mode != self.CurrentMode:
- for args in _MethodFromPLCState.get(status, []):
- self.ShowMethod(*args)
- self.previous_plcstate = status
- self.previous_mode = self.CurrentMode
- if self.AppFrame is not None:
- self.AppFrame.RefreshStatusToolBar()
- connection_text = _("Connected to: ")
- status_text = ""
- if simulating:
- connection_text += _("Simulation")
- status_text += _("ON")
- if status == "Disconnected":
- if not simulating:
- self.AppFrame.ConnectionStatusBar.SetStatusText(_(status), 1)
- self.AppFrame.ConnectionStatusBar.SetStatusText('', 2)
- else:
- self.AppFrame.ConnectionStatusBar.SetStatusText(connection_text, 1)
- self.AppFrame.ConnectionStatusBar.SetStatusText(status_text, 2)
- else:
- if simulating:
- connection_text += " (%s)"
- status_text += " (%s)"
- else:
- connection_text += "%s"
- status_text += "%s"
- self.AppFrame.ConnectionStatusBar.SetStatusText(connection_text % self.ConnectorPath, 1)
- self.AppFrame.ConnectionStatusBar.SetStatusText(status_text % _(status), 2)
- return True
- return False
-
- def Generate_plc_declare_locations(self):
- """
- Declare used locations in order to simulatePLC in a black box
- """
- return """#include "iec_types_all.h"
-
-#define __LOCATED_VAR(type, name, ...) \
-type beremiz_##name;\
-type *name = &beremiz_##name;
-
-#include "LOCATED_VARIABLES.h"
-
-#undef __LOCATED_VAR
-
-"""
-
- def Generate_lpc_retain_array_sim(self):
- """
- Support for retain array in Simulation
- """
- return """/* Support for retain array */
-#define USER_RETAIN_ARRAY_SIZE 2000
-#define NUM_OF_COLS 3
-unsigned char readOK = 0;
-unsigned int foundIndex = USER_RETAIN_ARRAY_SIZE;
-unsigned int retainArray[USER_RETAIN_ARRAY_SIZE][NUM_OF_COLS];
-
-unsigned int __GetRetainData(unsigned char READ, unsigned int INDEX, unsigned int COLUMN)
-{
- if(READ == 1)
- {
- if((0<=INDEX) && (INDEX<USER_RETAIN_ARRAY_SIZE) && (0<=COLUMN) && (COLUMN<NUM_OF_COLS))
- {
- readOK = 1;
- return retainArray[INDEX][COLUMN];
- }
- }
-
- readOK = 0;
- return 0;
-}
-
-unsigned char __SetRetainData(unsigned char WRITE, unsigned int INDEX, unsigned int WORD1, unsigned int WORD2, unsigned int WORD3)
-{
- if(WRITE == 1)
- {
- if((0<=INDEX) && (INDEX<USER_RETAIN_ARRAY_SIZE))
- {
- retainArray[INDEX][0] = WORD1;
- retainArray[INDEX][1] = WORD2;
- retainArray[INDEX][2] = WORD3;
- return 1;
- }
- }
-
- return 0;
-}
-
-unsigned char __FindRetainData(unsigned char SEARCH, unsigned int START_IDX, unsigned int END_IDX, unsigned int WORD1, unsigned int WORD2, unsigned int WORD3)
-{
- unsigned int i;
-
- if((SEARCH==1) && (0<=START_IDX) && (START_IDX<USER_RETAIN_ARRAY_SIZE) && (START_IDX<=END_IDX) && (END_IDX<USER_RETAIN_ARRAY_SIZE))
- {
- for(i=START_IDX;i<=END_IDX;i++)
- {
- if((retainArray[i][0] == WORD1) && (retainArray[i][1] == WORD2) && (retainArray[i][2] == WORD3))
- {
- foundIndex = i;
- return 1;
- }
- }
- }
-
- foundIndex = USER_RETAIN_ARRAY_SIZE; /* Data not found => return index that is out of array bounds */
- return 0;
-}
-
-/* Since Beremiz debugger doesn't like pointer-by-reference stuff or global varibles, separate function is a must */
-unsigned char __GetReadStatus(unsigned char dummy)
-{
- return readOK;
-}
-
-unsigned int __GetFoundIndex(unsigned char dummy)
-{
- return foundIndex;
-}
-"""
-
- def _Simulate(self):
- """
- Method called by user to Simulate PLC
- """
- uri = "LOCAL://"
- try:
- self._SetConnector(connectors.ConnectorFactory(uri, self))
- except Exception, msg:
- self.logger.write_error(_("Exception while connecting %s!\n") % uri)
- self.logger.write_error(traceback.format_exc())
-
- # Did connection success ?
- if self._connector is None:
- # Oups.
- self.logger.write_error(_("Connection failed to %s!\n") % uri)
- self.StopSimulation()
- return False
-
- self.CurrentMode = SIMULATION_MODE
-
- buildpath = self._getBuildPath()
-
- # Eventually create build dir
- if not os.path.exists(buildpath):
- os.makedirs(buildpath)
-
- # Generate SoftPLC IEC code
- IECGenRes = self._Generate_SoftPLC()
-
- # If IEC code gen fail, bail out.
- if not IECGenRes:
- self.logger.write_error(_("IEC-61131-3 code generation failed !\n"))
- self.StopSimulation()
- return False
-
- # Reset variable and program list that are parsed from
- # CSV file generated by IEC2C compiler.
- self.ResetIECProgramsAndVariables()
-
- gen_result = self.CTNGenerate_C(buildpath, self.PLCGeneratedLocatedVars)
- CTNCFilesAndCFLAGS, CTNLDFLAGS, DoCalls = gen_result[:3]
- # if some files have been generated put them in the list with their location
- if CTNCFilesAndCFLAGS:
- self.LocationCFilesAndCFLAGS = [(self.GetCurrentLocation(), CTNCFilesAndCFLAGS, DoCalls)]
- else:
- self.LocationCFilesAndCFLAGS = []
-
- # confnode asks for some LDFLAGS
- if CTNLDFLAGS:
- # LDFLAGS can be either string
- if type(CTNLDFLAGS) == type(str()):
- self.LDFLAGS = [CTNLDFLAGS]
- # or list of strings
- elif type(CTNLDFLAGS) == type(list()):
- self.LDFLAGS = CTNLDFLAGS[:]
- else:
- self.LDFLAGS = []
-
- # Header file for extensions
- open(os.path.join(buildpath, "beremiz.h"), "w").write(targets.GetHeader())
-
- # Template based part of C code generation
- # files are stacked at the beginning, as files of confnode tree root
- for generator, filename, name in [
- # debugger code
- (self.Generate_plc_debugger, "plc_debugger.c", "Debugger"),
- # init/cleanup/retrieve/publish, run and align code
- (self.Generate_plc_main, "plc_main.c", "Common runtime"),
- # declare located variables for simulate in a black box
- (self.Generate_plc_declare_locations, "plc_declare_locations.c", "Declare Locations"),
- # declare located variables for simulate in a black box
- (self.Generate_lpc_retain_array_sim, "lpc_retain_array_sim.c", "Retain Array for Simulation")]:
- try:
- # Do generate
- code = generator()
- if code is None:
- raise
- code_path = os.path.join(buildpath, filename)
- open(code_path, "w").write(code)
- # Insert this file as first file to be compiled at root confnode
- self.LocationCFilesAndCFLAGS[0][1].insert(0, (code_path, self.plcCFLAGS))
- except Exception, exc:
- self.logger.write_error(name + _(" generation failed !\n"))
- self.logger.write_error(traceback.format_exc())
- self.StopSimulation()
- return False
-
- # Get simulation builder
- builder = self.GetBuilder()
- if builder is None:
- self.logger.write_error(_("Fatal : cannot get builder.\n"))
- self.StopSimulation()
- return False
-
- # Build
- try:
- if not builder.build():
- self.logger.write_error(_("C Build failed.\n"))
- self.StopSimulation()
- return False
- except Exception, exc:
- self.logger.write_error(_("C Build crashed !\n"))
- self.logger.write_error(traceback.format_exc())
- self.StopSimulation()
- return False
-
- data = builder.GetBinaryCode()
- if data is not None:
- if self._connector.NewPLC(builder.GetBinaryCodeMD5(), data, []):
- self.UnsubscribeAllDebugIECVariable()
- self.ProgramTransferred()
- if self.AppFrame is not None:
- self.AppFrame.CloseObsoleteDebugTabs()
- self.AppFrame.RefreshPouInstanceVariablesPanel()
- self.logger.write(_("Transfer completed successfully.\n"))
- else:
- self.logger.write_error(_("Transfer failed\n"))
- self.StopSimulation()
- return False
-
- self._Run()
-
- if not self.StatusTimer.IsRunning():
- # Start the status Timer
- self.StatusTimer.Start(milliseconds=500, oneShot=False)
-
- def StopSimulation(self):
- self.CurrentMode = None
- self._SetConnector(None, False)
- self.ApplyOnlineMode()
-
- def _Stop(self):
- ProjectController._Stop(self)
-
- if self.CurrentMode == SIMULATION_MODE:
- self.StopSimulation()
-
- def CompareLocalAndRemotePLC(self):
- # if self.LPCConnector is None:
- # return
- if self._connector is None:
- return
- # We are now connected. Update button status
- MD5 = self.GetLastBuildMD5()
- # Check remote target PLC correspondance to that md5
- # if MD5 is not None and self.LPCConnector.MatchMD5(MD5):
- if MD5 is not None and self._connector.MatchMD5(MD5):
- # warns controller that program match
- self.ProgramTransferred()
-
- def ResetBuildMD5(self):
- builder = self.GetBuilder()
- if builder is not None:
- builder.ResetBinaryCodeMD5(*([] if arch in PLC_module else [self.OnlineMode]))
-
- def GetLastBuildMD5(self):
- builder = self.GetBuilder()
- if builder is not None:
- return builder.GetBinaryCodeMD5(*([] if arch in PLC_module else [self.OnlineMode]))
- else:
- return None
-
- def _Clean(self, building = False):
- self._CloseView(self._IECCodeView)
- runtime_list = fnmatch.filter(os.listdir(self._getBuildPath()), 'runtime_*')
- if os.path.isdir(os.path.join(self._getBuildPath())) and os.path.isfile(
- os.path.join(self._getBuildPath(), "hmi.py")) or runtime_list != []:
- self.logger.write(_("Cleaning the build directory\n"))
- # self.logger.write(_(str(os.path.join(self._getBuildPath(), "hmi.py"))))
- for file in runtime_list:
- os.remove(os.path.join(self._getBuildPath(), file))
- if os.path.isfile(os.path.join(self._getBuildPath(), "hmi.py")):
- os.remove(os.path.join(self._getBuildPath(), "hmi.py"))
- else:
- if not building:
- self.logger.write_error(_("Build directory already clean\n"))
- self.ShowMethod("_showIECcode", False)
- self.EnableMethod("_Clean", False)
- # kill the builder
- self._builder = None
- self.CompareLocalAndRemotePLC()
-
- def _Transfer(self):
- if self.OnlineMode == "NORMAL":
- if self.IsPLCStarted():
- dialog = wx.MessageDialog(self.AppFrame, "You must stop the PLC before transfer. Do you want to stop it now and transfer?", style=wx.YES_NO|wx.CENTRE)
- if dialog.ShowModal() == wx.ID_YES:
- self._Stop()
- else:
- return
- ProjectController._Transfer(self)
- return
- if self.CurrentMode is None and self.OnlineMode != "OFF":
- self.CurrentMode = TRANSFER_MODE
-
- if ProjectController._Build(self):
-
- ID_ABORTTRANSFERTIMER = wx.NewId()
- self.AbortTransferTimer = wx.Timer(self.AppFrame, ID_ABORTTRANSFERTIMER)
- self.AppFrame.Bind(wx.EVT_TIMER, self.AbortTransfer, self.AbortTransferTimer)
-
- if self.OnlineMode == "BOOTLOADER":
- self.BeginTransfer()
-
- else:
- self.logger.write(_("Resetting PLC\n"))
- # self.StatusTimer.Stop()
- self.LPCConnector.ResetPLC()
- self.AbortTransferTimer.Start(milliseconds=5000, oneShot=True)
-
- else:
- self.CurrentMode = None
-
- def BeginTransfer(self):
- self.logger.write(_("Start PLC transfer\n"))
-
- self.AbortTransferTimer.Stop()
- ProjectController._Transfer(self)
- self.AbortTransferTimer.Start(milliseconds=5000, oneShot=True)
-
- def AbortTransfer(self, event):
- self.logger.write_warning(_("Timeout waiting PLC to recover\n"))
-
- self.CurrentMode = None
- self.AbortTransferTimer.Stop()
- self.AbortTransferTimer = None
- event.Skip()
-
- old_DebugThreadProc = ProjectController.DebugThreadProc
- def LPCManager_DebugThreadProc(self, checking=False):
- if checking:
- state, ls = self._connector.GetTraceVariables()
- while state == "Stopped":
- state, ls = self._connector.GetTraceVariables()
- time.sleep(0.1)
-
- self.old_DebugThreadProc()
-
- def _connect_debug(self, checking = False):
- self.previous_plcstate = None
- if self.AppFrame:
- self.AppFrame.ResetGraphicViewers()
- self.RegisterDebugVarToConnector()
- if self.DispatchDebugValuesTimer is not None:
- self.DispatchDebugValuesTimer.Start(
- int(REFRESH_PERIOD * 1000), oneShot=True)
- if self.DebugThread is None:
- # self.DebugThread = Thread(target=self.DebugThreadProc)
- self.DebugThread = Thread(target=self.LPCManager_DebugThreadProc, args=(checking, ))
- self.DebugThread.setDaemon(True)
- self.DebugThread.start()
-
- def _Run(self):
- """
- Start PLC
- """
- if self.GetIECProgramsAndVariables():
- self._connector.StartPLC()
- self.logger.write(_("Starting PLC\n"))
- self._connect_debug(True)
- else:
- self.logger.write_error(_("Couldn't start PLC !\n"))
- self.UpdateMethodsFromPLCStatus()
-
- def _Disconnect(self):
- self._SetConnector(None)
- self.KillDebugThread()
-
- def _UpdateFw(self):
- """
- Method called by user to flash the firmware of the PLC
- """
- from FirmwareUpdateDialog import FirmwareUpdateDialog
- from dialogs import DiscoveryDialog
- from HostFirmwareUpdater import HostFirmwareUpdater
-
- if self.firmawreUpadateIsRunning == True:
- self.logger.write_error(_("Firmware update is already running!\n"))
- return
-
- self.firmawreUpadateIsRunning = True
- self.logger.write(_("Firmware update started\n"))
-
- # Launch the firmware selection dialog
- dialog = FirmwareUpdateDialog(self.AppFrame)
- answer = dialog.ShowModal()
- imageFilePath = dialog.GetFirmwareImageFile()
- updateType = dialog.GetFirmwareUpdateType()
- chunksSize = dialog.GetChunksSize()
- reboot = dialog.GetReboot()
- dialog.Destroy()
-
- if answer == wx.ID_CANCEL:
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
-
- if imageFilePath == None or imageFilePath == "":
- self.logger.write_error(_("No firmware image file selected!\n"))
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
- else:
- self.logger.write(_("Firmware image file: %s\n") % imageFilePath)
-
- if updateType == 1:
- self.logger.write(_("Firmware update type: Linux kernel image\n"))
- elif updateType == 2:
- self.logger.write(_("Firmware update type: Linux DTB image\n"))
- elif updateType == 3:
- self.logger.write(_("Firmware update type: Root file system image\n"))
- else:
- self.logger.write_error(_("Unknown firmware update type!\n"))
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
-
- if chunksSize < 1024 or chunksSize > 1024 * 1024:
- self.logger.write_error(_("Bad chunks size : %d KiB!\n") % chunksSize)
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
- else:
- self.logger.write(_("Chunks size : %d KiB\n") % chunksSize)
-
- self.logger.write(_("Reboot after update : %s\n") % ("Yes" if reboot else "No"))
- # Get the target PLC URI
- # Check if an uri is already configured in the Beremiz project
- uri = self.BeremizRoot.getURI_location()
- if uri is not None and uri != "":
- self.logger.write(_("PLC URI configured in the Beremiz project will be used: %s\n") % uri)
- else:
- # PLC URI is not configured in the Beremiz project
- # Launch Service Discovery dialog
- self.logger.write(_("PLC URI is not configured in the Beremiz project. Launching the Discover dialog\n"))
- dialog = DiscoveryDialog(self.AppFrame)
- answer = dialog.ShowModal()
- uri = dialog.GetURI()
- dialog.Destroy()
-
- # Nothing choosed or cancel button
- if uri is None or answer == wx.ID_CANCEL:
- self.logger.write_error(_("Connection canceled!\n"))
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
-
- # Get connector from uri
- self._connector = None
- try:
- self._SetConnector(connectors.ConnectorFactory(uri, self))
- except Exception:
- self.logger.write_error(_("Exception while connecting %s!\n") % uri)
- self.logger.write_error(traceback.format_exc())
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
-
- # Did connection success ?
- if self._connector is None:
- # Oups.
- self.logger.write_error(_("Connection failed to %s!\n") % uri)
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
- else:
- self.logger.write(_("Connected.\n"))
-
- # Last confirmation before firmware update
- answer = wx.MessageBox(_('Are you sure to launch the firmware update for the selected PLC?'),
- _('Firmware Update'), wx.YES_NO | wx.CENTRE | wx.NO_DEFAULT,
- self.AppFrame)
- if answer != wx.YES:
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- return
- # Some cosmetics
- self.AppFrame.Refresh()
- self.AppFrame.Update()
-
- # Lauch the firmaware update on the remote PLC
- updater = HostFirmwareUpdater(self._connector, imageFilePath, updateType, chunksSize, reboot, self.logger)
- try:
- updater.update()
- except Exception as e:
- self.logger.write_error(str(e) + "\n")
- self.logger.write_error(_("Firmware update canceled!\n"))
- self.firmawreUpadateIsRunning = False
- self._Disconnect()
- return
- self._Disconnect()
- self.firmawreUpadateIsRunning = False
- return True
-
-
+import socket
class StdoutPseudoFile:
def __init__(self, port):
@@ -2008,37 +48,3 @@
def isatty(self):
return False
-
-from threading import Thread, Timer, Semaphore, Lock
-import cmd
-
-build_lock = Lock()
-wx_eval_lock = Semaphore(0)
-eval_res = None
-
-
-def wx_evaluator(callable, *args, **kwargs):
- global eval_res
- eval_res = None
- try:
- eval_res = callable(*args, **kwargs)
- finally:
- wx_eval_lock.release()
-
-
-def evaluator(callable, *args, **kwargs):
- global eval_res
- wx.CallAfter(wx_evaluator, callable, *args, **kwargs)
- wx_eval_lock.acquire()
- return eval_res
-
-
-# Command log for debug, for viewing from wxInspector
-if BMZ_DBG:
- __builtins__.cmdlog = []
-
-
-if __name__ == '__main__':
-
- lpcmanager = LPCManagerLauncher()
- lpcmanager.Start()