beremiz

0852c4682179
Parents 28afed8b1af5
Children 015a803301b9
Adding support for simulation in LPCBeremiz
  • +242 -26
    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()