# HG changeset patch # User laurent # Date 1260374007 -3600 # Node ID 0852c4682179b56326ae77740530e6146cf63b54 # Parent 28afed8b1af5fea757539035b5ddd67fb9d92c82 Adding support for simulation in LPCBeremiz diff -r 28afed8b1af5 -r 0852c4682179 LPCBeremiz.py --- a/LPCBeremiz.py Wed Dec 09 16:52:46 2009 +0100 +++ b/LPCBeremiz.py Wed Dec 09 16:53:27 2009 +0100 @@ -62,7 +62,7 @@ __builtin__.__dict__['_'] = wx.GetTranslation#unicode_translation from Beremiz import * -from plugger import PluginsRoot, PlugTemplate, opjimg +from plugger import PluginsRoot, PlugTemplate, opjimg, connectors from plcopen.structures import LOCATIONDATATYPES from PLCControler import LOCATION_PLUGIN, LOCATION_MODULE, LOCATION_GROUP,\ LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY @@ -355,10 +355,10 @@ class LPCPluginsRoot(PluginsRoot): PluginMethods = [ - {"bitmap" : opjimg("Build"), - "name" : _("Build"), - "tooltip" : _("Build project into build folder"), - "method" : "_build"}, + {"bitmap" : opjimg("Debug"), + "name" : _("Simulate"), + "tooltip" : _("Simulate PLC"), + "method" : "_Simulate"}, {"bitmap" : opjimg("Run"), "name" : _("Run"), "shown" : False, @@ -369,6 +369,10 @@ "shown" : False, "tooltip" : _("Stop Running PLC"), "method" : "_Stop"}, + {"bitmap" : opjimg("Build"), + "name" : _("Build"), + "tooltip" : _("Build project into build folder"), + "method" : "_build"}, {"bitmap" : opjimg("Transfer"), "name" : _("Transfer"), "shown" : False, @@ -383,24 +387,87 @@ self.OnlineMode = 0 self.OnlinePath = None + + self.BuildSimulation = False + self.SimulationBuildPath = None + + self.previous_simulating = False def GetProjectName(self): return self.Project.getname() def GetDefaultTarget(self): target = self.Classes["BeremizRoot_TargetType"]() - target_value = self.Classes["TargetType_LPC"]() - target_value.setBuildPath(self.BuildPath) - target.setcontent({"name": "Makefile", "value": target_value}) + if self.BuildSimulation: + if wx.Platform == '__WXMSW__': + target_name = "Win32" + else: + target_name = "Linux" + target_value = self.Classes["TargetType_%s"%target_name]() + else: + target_name = "LPC" + target_value = self.Classes["TargetType_LPC"]() + target_value.setBuildPath(self.BuildPath) + target.setcontent({"name": target_name, "value": target_value}) return target - + + def _getBuildPath(self): + if self.BuildSimulation: + if self.SimulationBuildPath is None: + self.SimulationBuildPath = os.path.join(tempfile.mkdtemp(), os.path.basename(self.ProjectPath), "build") + return self.SimulationBuildPath + else: + PluginsRoot._getBuildPath(self) + def SetProjectName(self, name): return self.Project.setname(name) def SetOnlineMode(self, mode, path=None): - self.OnlineMode = mode - self.OnlinePath = path - self.UpdateMethodsFromPLCStatus() + if self.OnlineMode != mode: + self.OnlineMode = mode + if self.OnLineMode != 0: + if self._connector is None: + uri = "LPC://%s" % path + try: + self._connector = 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) + + if self._connector is not None: + + if self.OnLineMode == 1: + self.CompareLocalAndRemotePLC() + + # Init with actual PLC status and print it + self.UpdateMethodsFromPLCStatus() + if self.previous_plcstate is not None: + status = _(self.previous_plcstate) + else: + status = "" + self.logger.write(_("PLC is %s\n")%status) + + # Start the status Timer + self.StatusTimer.Start(milliseconds=500, 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")) + + else: + self._connector = None + self.StatusTimer.Stop() + + self.OnlinePath = path + self.UpdateMethodsFromPLCStatus() # Update a PLCOpenEditor Pou variable name def UpdateProjectVariableName(self, old_name, new_name): @@ -450,46 +517,192 @@ self.RefreshPluginsBlockLists() return None - - def SaveProject(self): - self.SaveXMLFile(self.ProjectPath) ############# Real PLC object access ############# def UpdateMethodsFromPLCStatus(self): # Get PLC state : Running or Stopped # TODO : use explicit status instead of boolean + simulating = False if self.OnlineMode == 0: + if self._connector is not None: + simulating = self._connector.GetPLCstatus() == "Started" status = "Disconnected" elif self.OnlineMode == 1: + if self._connector is not None: + simulating = self._connector.GetPLCstatus() == "Started" status = "Connected" - elif self._connector is not None: - status = self._connector.GetPLCstatus() else: - status = "Disconnected" - if(self.previous_plcstate != status): + if self._connector is not None: + status = self._connector.GetPLCstatus() + else: + status = "Disconnected" + if self.previous_plcstate != status or self.previous_simulating != simulating: for args in { - "Started" : [("_build", False), + "Started" : [("_Simulate", False), ("_Run", False), ("_Stop", True), + ("_build", False), ("_Transfer", False)], - "Stopped" : [("_build", False), + "Stopped" : [("_Simulate", False), ("_Run", True), ("_Stop", False), + ("_build", False), ("_Transfer", False)], - "Connected" : [("_build", False), + "Connected" : [("_Simulate", not simulating), ("_Run", False), - ("_Stop", False), + ("_Stop", simulating), + ("_build", False), ("_Transfer", True)], - "Disconnected" :[("_build", True), + "Disconnected" :[("_Simulate", not simulating), ("_Run", False), - ("_Stop", False), + ("_Stop", simulating), + ("_build", True), ("_Transfer", False)], }.get(status,[]): self.ShowMethod(*args) self.previous_plcstate = status + self.previous_simulating = simulating 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 _Simulate(self): + """ + Method called by user to Simulate PLC + """ + uri = "LOCAL://" + try: + self._connector = 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) + return False + + self.BuildSimulation = True + + + 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.BuildSimulation = False + return False + + # Reset variable and program list that are parsed from + # CSV file generated by IEC2C compiler. + self.ResetIECProgramsAndVariables() + + gen_result = self.PlugGenerate_C(buildpath, self.PLCGeneratedLocatedVars) + PlugCFilesAndCFLAGS, PlugLDFLAGS, DoCalls = gen_result[:3] + # if some files have been generated put them in the list with their location + if PlugCFilesAndCFLAGS: + self.LocationCFilesAndCFLAGS = [(self.GetCurrentLocation(), PlugCFilesAndCFLAGS, DoCalls)] + else: + self.LocationCFilesAndCFLAGS = [] + + # plugin asks for some LDFLAGS + if PlugLDFLAGS: + # LDFLAGS can be either string + if type(PlugLDFLAGS)==type(str()): + self.LDFLAGS=[PlugLDFLAGS] + #or list of strings + elif type(PlugLDFLAGS)==type(list()): + self.LDFLAGS=PlugLDFLAGS[:] + else: + self.LDFLAGS=[] + + # Template based part of C code generation + # files are stacked at the beginning, as files of plugin 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_common_main,"plc_common_main.c","Common runtime"), + # declare located variables for simulate in a black box + (self.Generate_plc_declare_locations,"plc_declare_locations.c","Declare Locations")]: + 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 plugin + 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.BuildSimulation = False + return False + + # Get simulation builder + builder = self.GetBuilder() + if builder is None: + self.logger.write_error(_("Fatal : cannot get builder.\n")) + self.BuildSimulation = False + return False + + # Build + try: + if not builder.build() : + self.logger.write_error(_("C Build failed.\n")) + self.BuildSimulation = False + return False + except Exception, exc: + self.logger.write_error(_("C Build crashed !\n")) + self.logger.write_error(traceback.format_exc()) + self.BuildSimulation = False + return False + + data = builder.GetBinaryCode() + if data is not None : + if self._connector.NewPLC(builder.GetBinaryCodeMD5(), data, []): + if self.AppFrame is not None: + self.AppFrame.CloseDebugTabs() + self.AppFrame.RefreshInstancesTree() + self.UnsubscribeAllDebugIECVariable() + self.ProgramTransferred() + self.logger.write(_("Transfer completed successfully.\n")) + else: + self.logger.write_error(_("Transfer failed\n")) + self.BuildSimulation = False + return False + + self._Run() + + self.BuildSimulation = False + + # Start the status Timer + self.StatusTimer.Start(milliseconds=500, oneShot=False) + #------------------------------------------------------------------------------- # LPCBeremiz Class #------------------------------------------------------------------------------- @@ -550,7 +763,10 @@ global frame, lpcberemiz_cmd frame = None self.PluginRoot.ResetAppFrame(lpcberemiz_cmd.Log) - + if self.PluginRoot.OnlineMode == 0: + self.PluginRoot._connector = None + + self.PluginRoot.KillDebugThread() self.KillLocalRuntime() event.Skip() @@ -754,7 +970,7 @@ # Install a exception handle for bug reports AddExceptHook(os.getcwd(),__version__) - frame = LPCBeremiz(None, plugin_root=plugin_root, debug=False) + frame = LPCBeremiz(None, plugin_root=plugin_root, debug=True) plugin_root.SetAppFrame(frame, frame.Log) frame.Show() frame.Raise()