__version__ = "$Revision$"
import os, sys, getopt, tempfile
from types import TupleType, StringType, UnicodeType
_lpcmanager_path = os.path.split(__file__)[0]
_dist_folder = os.path.split(sys.path[0])[0]
_beremiz_folder = os.path.join(_dist_folder, "beremiz")
sys.path.append(_beremiz_folder)
return os.path.join(CWD, *args)
if __name__ == '__main__':
print "\nUsage of LPCManager.py :"
print "\n %s Projectpath Buildpath port [arch]\n" % sys.argv[0]
opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
except getopt.GetoptError:
# print help information and exit:
if o in ("-h", "--help"):
if len(args) < 3 or len(args) > 4:
PLC_module = {'MC9', 'GOT'}
__builtin__.__dict__["BMZ_DBG"] = os.path.exists("LPC_DEBUG")
app = wx.PySimpleApp(redirect=BMZ_DBG)
app.SetAppName('beremiz')
wx.InitAllImageHandlers()
from util.misc import InstallLocalRessources
InstallLocalRessources(_beremiz_folder)
_lpcmanager_path = os.path.split(__file__)[0]
from POULibrary import POULibrary
class PLCLibraryLPC(POULibrary):
def GetLibraryPath(self):
return os.path.join(_lpcmanager_path + '/Pous/', "pousLPC.xml")
class PLCLibraryGOT(POULibrary):
def GetLibraryPath(self):
return os.path.join(_lpcmanager_path + '/Pous/', "pousGOT.xml")
class PLCLibraryRTC(POULibrary):
def GetLibraryPath(self):
return os.path.join(_lpcmanager_path + '/Pous/', "pousRTC.xml")
features.libraries=[('Native', 'NativeLib.NativeLibrary')]
features.libraries += [('RTC', lambda: PLCLibraryRTC),('Python', 'py_ext.PythonLibrary')]
features.libraries += [('RTC', lambda: PLCLibraryRTC),('GOT', lambda: PLCLibraryGOT),('Python', 'py_ext.PythonLibrary')]
features.libraries += [('LPC', lambda: PLCLibraryLPC)]
features.catalog.pop([i for i, x in enumerate(features.catalog) if x[0].startswith('svg')][0])
wxglade_hmi_index = [i for i, x in enumerate(features.catalog) if x[0].startswith('wxglade_hmi')][0]
old_wxglade_hmi = features.catalog[wxglade_hmi_index]
mwwxglade_hmi = ('wxglade_hmi', _('WxGlade GUI'), _('Add a simple WxGlade based GUI.'), 'MWWxglade_hmi.mwwxglade_hmi.WxGladeHMI')
features.catalog[wxglade_hmi_index] = mwwxglade_hmi
from LPCconnector import LPC_connector_factory
from LPCconnector.PYRO import MW_PYRO_connector_factory
from LPCconnector.WAMP import MWWAMP_connector_factory
connectors.connectors["LPC"] = lambda: LPC_connector_factory
connectors.connectors["PYRO"] = lambda: MW_PYRO_connector_factory
connectors.connectors["WAMP"] = lambda: MWWAMP_connector_factory
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 ProjectController import ProjectController
from ConfigTreeNode import ConfigTreeNode
from editors.ProjectNodeEditor import ProjectNodeEditor
from editors.CodeFileEditor import VariablesTable
from WampOptionsEditor import WampOptionsEditor
def LeftClick(self, event):
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)
dialog = WampOptionsEditor(self.Parent.Parent, options, desc)
answer = dialog.ShowModal()
opt,OnChange,value,description = dialog.GetOptions()
self.grid.SetCellValue(event.GetRow(), event.GetCol(), str(opt))
self.grid.SetCellValue(event.GetRow(), event.GetCol()-1, value)
self.grid.SetCellValue(event.GetRow(), event.GetCol()-2, description)
self.Parent.RefreshModel()
VariablesTable.LeftClick = LeftClick
def _updateColAttrs(self, grid):
wxGrid -> update the column attributes to add the
appropriate renderer given the column name.
Otherwise default to the default renderer.
for row in range(self.GetNumberRows()):
for col in range(self.GetNumberCols()):
colname = self.GetColLabelValue(col, False)
if colname in ["Name", "Initial", "Description", "OnChange"]:
editor = wx.grid.GridCellTextEditor()
editor = wx.grid.GridCellChoiceEditor()
editor.SetParameters("input,memory,output")
grid.SetReadOnly(row, col, True)
grid.SetCellEditor(row, col, editor)
grid.SetCellRenderer(row, col, renderer)
grid.SetCellBackgroundColour(row, col, wx.WHITE)
self.ResizeRow(grid, row)
self.grid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.LeftClick)
VariablesTable._updateColAttrs = _updateColAttrs
from PLCControler import PLCControler, LOCATION_MODULE, LOCATION_GROUP, \
LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
from IDEFrame import IDEFrame
from dialogs import ProjectDialog
from controls import TextCtrlAutoComplete
defaultGenerateNewName = PLCControler.GenerateNewName
def newGenerateNewName(self, tagname, name, format, start_idx=0, exclude={}, debug=False):
defaultGenerateNewName(self, tagname, name, format, start_idx=0, exclude={}, debug=False)
while name is None or names.get(name.upper(), False):
PLCControler.GenerateNewName = newGenerateNewName
from canfestival import RootClass as CanOpenRootClass
from canfestival.canfestival import _SlaveCTN, _NodeListCTN, NodeManager
from canfestival.NetworkEditor import NetworkEditor
from canfestival.SlaveEditor import SlaveEditor
# havecanfestival = False
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',
'other': 'Comic Sans MS',
faces = {'times': 'Times',
'other': 'new century schoolbook',
from editors.ConfTreeNodeEditor import GenBitmapTextButton
from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
# ConfTreeNodeEditor.SHOW_BASE_PARAMS = False
# -------------------------------------------------------------------------------
# CANFESTIVAL CONFNODE HACK
# -------------------------------------------------------------------------------
# from canfestival import canfestival
# class LPC_canfestival_config:
# def getCFLAGS(self, *args):
# def getLDFLAGS(self, *args):
# canfestival.local_canfestival_config = LPC_canfestival_config()
import LPCBus as LPCBus_mod
LPCBus_mod.LPCarch = "MC9"
LPCBus_mod.LPCarch = arch
# -------------------------------------------------------------------------------
# LPC CanFestival ConfNode Class
# -------------------------------------------------------------------------------
class LPCSlaveEditor(SlaveEditor):
# SHOW_BASE_PARAMS = False
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: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:restriction base="xsd:integer">
<xsd:minInclusive value="1"/>
<xsd:maxInclusive value="99"/>
EditorType = LPCSlaveEditor
# TODO change netname when name change
NodeManager.__init__(self)
odfilepath = self.GetSlaveODPath()
if (os.path.isfile(odfilepath)):
self.OpenFileInCurrent(odfilepath)
self.CreateNewNode("SlaveNode", # Name - will be changed at build time
0x00, # NodeID - will be changed at build time
return str(self.BaseParams.getIEC_Channel())
{"bitmap": "NetworkEdit",
"tooltip": _("Edit CanOpen slave with ObjdictEdit"),
] + _SlaveCTN.ConfNodeMethods
class LPCNetworkEditor(NetworkEditor):
# SHOW_BASE_PARAMS = False
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: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"/>
EditorType = LPCNetworkEditor
return str(self.BaseParams.getIEC_Channel())
{"bitmap": "NetworkEdit",
"name": _("Edit network"),
"tooltip": _("Edit CanOpen Network with NetworkEdit"),
] + _NodeListCTN.ConfNodeMethods
class LPCCanOpen(CanOpenRootClass):
CTNChildrenTypes = [("CanOpenNode", LPCCanOpenMaster, "CanOpen Master"),
("CanOpenSlave", LPCCanOpenSlave, "CanOpen Slave")]
ConfigTreeNode.LoadChildren(self)
if self.GetChildByName("Master") is None:
master = self.CTNAddChild("Master", "CanOpenNode", 0)
# master.BaseParams.setEnabled(False)
if self.GetChildByName("Slave") is None:
slave = self.CTNAddChild("Slave", "CanOpenSlave", 1)
# slave.BaseParams.setEnabled(False)
# -------------------------------------------------------------------------------
# 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):
mycopytree(srcpath, dstpath)
elif os.path.isfile(srcpath):
shutil.copy2(srcpath, dstpath)
[SIMULATION_MODE, TRANSFER_MODE] = range(2)
class LPCProjectNodeEditor(ProjectNodeEditor):
class LPCProjectNodeEditor(ProjectNodeEditor):
"tooltip": _("Simulate PLC"),
"tooltip": _("Start PLC"),
"tooltip": _("Stop Running PLC"),
"tooltip": _("Build project into build folder"),
"tooltip": _("Clean project build folder"),
"tooltip": _("Transfer PLC"),
"Started": [("_Simulate", False),
"Stopped": [("_Simulate", False),
"Connected": [("_Simulate", "not simulating"),
("_Stop", True if arch in PLC_module else "simulating"),
"Disconnected": [("_Simulate", "not simulating"),
("_Stop", True if arch in PLC_module else "simulating"),
"tooltip": _("Connect to the target PLC"),
"tooltip": _("Disconnect from PLC"),
"method": "_Disconnect"},
"tooltip": _("Update the PLC firmware"),
_MethodFromPLCState["Disconnected"] += [("_Connect", True),
_MethodFromPLCState["Connected"] += [("_Connect", False),
class LPCProjectController(ProjectController):
StatusMethods = _StatusMethods
EditorType = LPCProjectNodeEditor
def __init__(self, frame, logger, buildpath):
self.OrigBuildPath = buildpath
ProjectController.__init__(self, frame, logger)
self.CTNChildrenTypes += [("LPCBus", LPCBus, "LPC bus"), ("CanOpen", LPCCanOpen, "CanOpen bus")]
self.CTNChildrenTypes += [("LPCBus", LPCBus, "LPC bus")]
self.OnlineMode = "NORMAL" if arch in PLC_module else "OFF"
self.ConnectorPath = None
self.previous_mode = None
self.SimulationBuildPath = None
self.AbortTransferTimer = None
# Firmware update running status
self.firmawreUpadateIsRunning = False
# Bind mouse double click event on URI_location in Beremiz
def GetProjectName(self):
return self.Project.getname()
def GetDefaultTargetName(self):
if self.CurrentMode == SIMULATION_MODE:
return ProjectController.GetDefaultTargetName(self)
target = ProjectController.GetTarget(self)
if self.CurrentMode != SIMULATION_MODE and arch not in PLC_module:
target.getcontent().setBuildPath(self.BuildPath)
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
return ProjectController._getBuildPath(self)
# MD5 = self.GetLastBuildMD5()
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')
self.logger.write(_("Export file is successfully created on location: %s\n") % path_export_file)
self.logger.write(_("Export file is not created because eror: %s\n") % e)
save = self.ProjectTestModified()
self.AppFrame._Refresh(TITLE, FILEMENU)
if self.BuildPath is not None:
mycopytree(self.OrigBuildPath, self.BuildPath)
if ProjectController._Build(self):
wx.CallAfter(self.AppFrame.RefreshAll)
def SetProjectName(self, name):
return self.Project.setname(name)
def _SetOnlineMode(self, mode, path=None):
if self.OnlineMode != mode:
if mode not in ["OFF", ""]:
self.ConnectorPath = path
uri = "LPC://%s/%s" % (self.OnlineMode, path)
self.LPCConnector = connectors.ConnectorFactory(uri, self)
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:
self.logger.write_error(_("Connection failed to %s!\n") % uri)
self.ConnectorPath = None
SetOnlineMode = lambda *x: None if arch in PLC_module else _SetOnlineMode
def ApplyOnlineMode(self):
if self.CurrentMode != SIMULATION_MODE:
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)
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.logger.write_warning(_("Debug do not match PLC - stop/transfert/start to re-enable\n"))
elif self.StatusTimer and self.StatusTimer.IsRunning():
if self.CurrentMode == TRANSFER_MODE:
if self.OnlineMode == "BOOTLOADER":
elif self.OnlineMode == "APPLICATION":
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)
# Update a PLCOpenEditor Pou variable name
def UpdateProjectVariableName(self, old_name, new_name):
self.Project.updateElementName(old_name, new_name)
def RemoveProjectVariableByAddress(self, address):
self.Project.removeVariableByAddress(address)
def RemoveProjectVariableByFilter(self, leading):
self.Project.removeVariableByFilter(leading)
def AddProjectDefaultConfiguration(self, config_name="config", res_name="resource1"):
ProjectController.AddProjectDefaultConfiguration(self, config_name, res_name)
self.SetEditedResourceInfos(
self.ComputeConfigurationResourceName(config_name, res_name),
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):
result = self.OpenXMLFile(plc_file)
self.CreateNewProject({"companyName": "",
if len(self.GetProjectConfigNames()) == 0:
self.AddProjectDefaultConfiguration()
# Change XSD into class members
# 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()
# Load and init all the children
canopen_child = self.GetChildByName("CanOpen")
if havecanfestival and canopen_child is None:
canopen = self.CTNAddChild("CanOpen", "CanOpen", 0)
elif not havecanfestival and canopen_child is not None:
canopen_child.CTNRemove()
if self.CTNTestModified():
self.RefreshConfNodesBlockLists()
wx.CallAfter(self.RefreshConfNodesBlockLists)
self.SetParamsAttribute('BeremizRoot.TargetType', 'MC9')
return self.previous_plcstate == "Started" or self.previous_mode == SIMULATION_MODE
def ShowMethod(self, name, val):
simulating = self.CurrentMode == SIMULATION_MODE
if val.endswith("simulating"):
if val.startswith("not"):
ProjectController.ShowMethod(self, name, val)
def UpdateMethodsFromPLCStatus(self):
simulating = self.CurrentMode == SIMULATION_MODE
if self.OnlineMode == "OFF":
status, log_count = self._connector.GetPLCstatus()
self.UpdatePLCLog(log_count)
elif self.OnlineMode == "BOOTLOADER":
if self._connector is not None:
status, log_count = self._connector.GetPLCstatus()
if status == "Disconnected":
self._SetConnector(None, False)
self.UpdatePLCLog(log_count)
if self.previous_plcstate != status or self.previous_mode != self.CurrentMode:
for args in _MethodFromPLCState.get(status, []):
self.previous_plcstate = status
self.previous_mode = self.CurrentMode
if self.AppFrame is not None:
self.AppFrame.RefreshStatusToolBar()
connection_text = _("Connected to: ")
connection_text += _("Simulation")
if status == "Disconnected":
self.AppFrame.ConnectionStatusBar.SetStatusText(_(status), 1)
self.AppFrame.ConnectionStatusBar.SetStatusText('', 2)
self.AppFrame.ConnectionStatusBar.SetStatusText(connection_text, 1)
self.AppFrame.ConnectionStatusBar.SetStatusText(status_text, 2)
connection_text += " (%s)"
self.AppFrame.ConnectionStatusBar.SetStatusText(connection_text % self.ConnectorPath, 1)
self.AppFrame.ConnectionStatusBar.SetStatusText(status_text % _(status), 2)
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 *name = &beremiz_##name;
#include "LOCATED_VARIABLES.h"
def Generate_lpc_retain_array_sim(self):
Support for retain array in Simulation
return """/* Support for retain array */
#define USER_RETAIN_ARRAY_SIZE 2000
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((0<=INDEX) && (INDEX<USER_RETAIN_ARRAY_SIZE) && (0<=COLUMN) && (COLUMN<NUM_OF_COLS))
return retainArray[INDEX][COLUMN];
unsigned char __SetRetainData(unsigned char WRITE, unsigned int INDEX, unsigned int WORD1, unsigned int WORD2, unsigned int WORD3)
if((0<=INDEX) && (INDEX<USER_RETAIN_ARRAY_SIZE))
retainArray[INDEX][0] = WORD1;
retainArray[INDEX][1] = WORD2;
retainArray[INDEX][2] = WORD3;
unsigned char __FindRetainData(unsigned char SEARCH, unsigned int START_IDX, unsigned int END_IDX, unsigned int WORD1, unsigned int WORD2, unsigned int WORD3)
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 = USER_RETAIN_ARRAY_SIZE; /* Data not found => return index that is out of array bounds */
/* Since Beremiz debugger doesn't like pointer-by-reference stuff or global varibles, separate function is a must */
unsigned char __GetReadStatus(unsigned char dummy)
unsigned int __GetFoundIndex(unsigned char dummy)
Method called by user to Simulate PLC
self._SetConnector(connectors.ConnectorFactory(uri, self))
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:
self.logger.write_error(_("Connection failed to %s!\n") % uri)
self.CurrentMode = SIMULATION_MODE
buildpath = self._getBuildPath()
# Eventually create build dir
if not os.path.exists(buildpath):
# Generate SoftPLC IEC code
IECGenRes = self._Generate_SoftPLC()
# If IEC code gen fail, bail out.
self.logger.write_error(_("IEC-61131-3 code generation failed !\n"))
# 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
self.LocationCFilesAndCFLAGS = [(self.GetCurrentLocation(), CTNCFilesAndCFLAGS, DoCalls)]
self.LocationCFilesAndCFLAGS = []
# confnode asks for some LDFLAGS
# LDFLAGS can be either string
if type(CTNLDFLAGS) == type(str()):
self.LDFLAGS = [CTNLDFLAGS]
elif type(CTNLDFLAGS) == type(list()):
self.LDFLAGS = CTNLDFLAGS[:]
# 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 [
(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")]:
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))
self.logger.write_error(name + _(" generation failed !\n"))
self.logger.write_error(traceback.format_exc())
builder = self.GetBuilder()
self.logger.write_error(_("Fatal : cannot get builder.\n"))
self.logger.write_error(_("C Build failed.\n"))
self.logger.write_error(_("C Build crashed !\n"))
self.logger.write_error(traceback.format_exc())
data = builder.GetBinaryCode()
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"))
self.logger.write_error(_("Transfer failed\n"))
if not self.StatusTimer.IsRunning():
self.StatusTimer.Start(milliseconds=500, oneShot=False)
def StopSimulation(self):
self._SetConnector(None, False)
ProjectController._Stop(self)
if self.CurrentMode == SIMULATION_MODE:
def CompareLocalAndRemotePLC(self):
# if self.LPCConnector is None:
if self._connector is None:
# 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()
builder = self.GetBuilder()
builder.ResetBinaryCodeMD5(*([] if arch in PLC_module else [self.OnlineMode]))
def GetLastBuildMD5(self):
builder = self.GetBuilder()
return builder.GetBinaryCodeMD5(*([] if arch in PLC_module else [self.OnlineMode]))
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"))
self.logger.write_error(_("Build directory already clean\n"))
self.ShowMethod("_showIECcode", False)
self.EnableMethod("_Clean", False)
self.CompareLocalAndRemotePLC()
if self.OnlineMode == "NORMAL":
ProjectController._Transfer(self)
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.logger.write(_("Resetting PLC\n"))
# self.StatusTimer.Stop()
self.LPCConnector.ResetPLC()
self.AbortTransferTimer.Start(milliseconds=5000, oneShot=True)
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.AbortTransferTimer.Stop()
self.AbortTransferTimer = None
def _connect_debug(self):
self.previous_plcstate = None
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.setDaemon(True)
if self.GetIECProgramsAndVariables():
self._connector.StartPLC()
self.logger.write(_("Starting PLC\n"))
self.logger.write_error(_("Couldn't start PLC !\n"))
self.UpdateMethodsFromPLCStatus()
Method called by user to flash the firmware of the PLC
from dialogs import FirmwareUpdateDialog
from dialogs import DiscoveryDialog
from HostFirmwareUpdater import HostFirmwareUpdater
if self.firmawreUpadateIsRunning == True:
self.logger.write_error(_("Firmware update is already running!\n"))
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()
if answer == wx.ID_CANCEL:
self.logger.write_error(_("Firmware update canceled!\n"))
self.firmawreUpadateIsRunning = False
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
self.logger.write(_("Firmware image file: %s\n") % imageFilePath)
self.logger.write(_("Firmware update type: Linux kernel image\n"))
self.logger.write(_("Firmware update type: Linux DTB image\n"))
self.logger.write(_("Firmware update type: Root file system image\n"))
self.logger.write_error(_("Unknown firmware update type!\n"))
self.logger.write_error(_("Firmware update canceled!\n"))
self.firmawreUpadateIsRunning = False
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
self.logger.write(_("Chunks size : %d KiB\n") % chunksSize)
self.logger.write(_("Reboot after update : %s\n") % ("Yes" if reboot else "No"))
# 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)
# 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()
# 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
self._SetConnector(connectors.ConnectorFactory(uri, self))
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
# Did connection success ?
if self._connector is None:
self.logger.write_error(_("Connection failed to %s!\n") % uri)
self.logger.write_error(_("Firmware update canceled!\n"))
self.firmawreUpadateIsRunning = False
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.logger.write_error(_("Firmware update canceled!\n"))
self.firmawreUpadateIsRunning = False
# Lauch the firmaware update on the remote PLC
updater = HostFirmwareUpdater(self._connector, imageFilePath, updateType, chunksSize, reboot, self.logger)
self.logger.write_error(str(e) + "\n")
self.logger.write_error(_("Firmware update canceled!\n"))
self.firmawreUpadateIsRunning = False
self.firmawreUpadateIsRunning = False
# -------------------------------------------------------------------------------
# -------------------------------------------------------------------------------
class LPCBeremiz(Beremiz):
def _init_coll_FileMenu_Items(self, parent):
AppendMenu(parent, help='', id=wx.ID_SAVE,
kind=wx.ITEM_NORMAL, text=_(u'Save\tCTRL+S'))
AppendMenu(parent, help='', id=wx.ID_CLOSE,
kind=wx.ITEM_NORMAL, text=_(u'Close Tab\tCTRL+W'))
AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP,
kind=wx.ITEM_NORMAL, text=_(u'Page Setup'))
AppendMenu(parent, help='', id=wx.ID_PREVIEW,
kind=wx.ITEM_NORMAL, text=_(u'Preview'))
AppendMenu(parent, help='', id=wx.ID_PRINT,
kind=wx.ITEM_NORMAL, text=_(u'Print'))
AppendMenu(parent, help='', id=wx.ID_EXIT,
kind=wx.ITEM_NORMAL, text=_(u'Quit\tCTRL+Q'))
self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE)
self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE)
self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP)
self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW)
self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT)
self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT)
self.AddToMenuToolBar([(wx.ID_SAVE, "save", _(u'Save'), None),
(wx.ID_PRINT, "print", _(u'Print'), None)])
def _init_ctrls(self, prnt):
Beremiz._init_ctrls(self, prnt)
def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True):
Beremiz.__init__(self, parent, projectOpen, buildpath, ctr, debug)
def OnCloseFrame(self, event):
if self.CheckSaveBeforeClosing(_("Close Application")):
self.CTR.ResetAppFrame(lpcberemiz_cmd.Log)
if self.CTR.OnlineMode == 0:
self.CTR._SetConnector(None, False)
self.CTR.KillDebugThread()
lpcberemiz_cmd.Log.write("Closed\n")
projectname = self.CTR.GetProjectName()
if self.CTR.ProjectTestModified():
projectname = "~%s~" % projectname
self.SetTitle("%s - %s" % (name, projectname))
def RefreshFileMenu(self):
MenuToolBar = self.Panes["MenuToolBar"]
selected = self.TabsOpened.GetSelection()
graphic_viewer = isinstance(self.TabsOpened.GetPage(selected), Viewer)
if self.TabsOpened.GetPageCount() > 0:
self.FileMenu.Enable(wx.ID_CLOSE, True)
self.FileMenu.Enable(wx.ID_PREVIEW, True)
self.FileMenu.Enable(wx.ID_PRINT, True)
MenuToolBar.EnableTool(wx.ID_PRINT, True)
self.FileMenu.Enable(wx.ID_PREVIEW, False)
self.FileMenu.Enable(wx.ID_PRINT, False)
MenuToolBar.EnableTool(wx.ID_PRINT, False)
self.FileMenu.Enable(wx.ID_CLOSE, False)
self.FileMenu.Enable(wx.ID_PREVIEW, False)
self.FileMenu.Enable(wx.ID_PRINT, False)
MenuToolBar.EnableTool(wx.ID_PRINT, False)
self.FileMenu.Enable(wx.ID_PAGE_SETUP, True)
project_modified = self.CTR.ProjectTestModified()
self.FileMenu.Enable(wx.ID_SAVE, project_modified)
MenuToolBar.EnableTool(wx.ID_SAVE, project_modified)
self.FileMenu.Enable(wx.ID_CLOSE, False)
self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
self.FileMenu.Enable(wx.ID_PREVIEW, False)
self.FileMenu.Enable(wx.ID_PRINT, False)
MenuToolBar.EnableTool(wx.ID_PRINT, False)
self.FileMenu.Enable(wx.ID_SAVE, False)
MenuToolBar.EnableTool(wx.ID_SAVE, False)
def RefreshScrollBars(self):
xstart, ystart = self.PLCConfig.GetViewStart()
window_size = self.PLCConfig.GetClientSize()
sizer = self.PLCConfig.GetSizer()
maxx, maxy = sizer.GetMinSize()
posx = max(0, min(xstart, (maxx - window_size[0]) / SCROLLBAR_UNIT))
posy = max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT))
self.PLCConfig.Scroll(posx, posy)
self.PLCConfig.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT,
maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, posx, posy)
# Remove taskbar icon when simulating
def StartLocalRuntime(self, taskbaricon=True):
return Beremiz.StartLocalRuntime(self, taskbaricon=False)
def __init__(self, port):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect(('localhost', port))
idx = self.Buffer.find("\n")
text = self.socket.recv(2048)
idx = self.Buffer.find("\n")
line = self.Buffer[:idx + 1]
self.Buffer = self.Buffer[idx + 1:]
""" Base class for file like objects to facilitate StdOut for the Shell."""
def write(self, s, style=None):
self.socket.send(s.encode('utf8'))
def write_warning(self, s):
def write_error(self, s):
if __name__ == '__main__':
from threading import Thread, Timer, Semaphore
wx_eval_lock = Semaphore(0)
def wx_evaluator(callable, *args, **kwargs):
eval_res = callable(*args, **kwargs)
def evaluator(callable, *args, **kwargs):
wx.CallAfter(wx_evaluator, callable, *args, **kwargs)
# Command log for debug, for viewing from wxInspector
class LPCBeremiz_Cmd(cmd.Cmd):
def __init__(self, CTR, Log):
cmd.Cmd.__init__(self, stdin=Log, stdout=Log)
self.use_rawinput = False
self.restore_last_state = False
if self.RefreshTimer is not None:
self.RefreshTimer.cancel()
self.RefreshTimer = Timer(0.1, wx.CallAfter, args=[self.Refresh])
self.RefreshTimer.start()
self.CTR.SetAppFrame(frame, frame.Log)
self.CTR.UpdateMethodsFromPLCStatus()
self.restore_last_state = True
if self.restore_last_state:
self.restore_last_state = False
frame._Refresh(TITLE, PROJECTTREE, POUINSTANCEVARIABLESPANEL, FILEMENU, EDITMENU)
self.CTR.ResetAppFrame(self.Log)
def SetProjectProperties(self, projectname, productname, productversion, companyname):
"projectName": projectname,
"productName": productname,
"productVersion": productversion,
"companyName": companyname}
self.CTR.SetProjectProperties(properties=new_properties, buffer=False)
def SetOnlineMode(self, mode, path=None):
self.CTR.SetOnlineMode(mode, path)
def AddBus(self, iec_channel, name, icon=None):
for child in self.CTR.IterChildren():
if child.BaseParams.getName() == name:
return "Error: A bus named %s already exists\n" % name
elif child.BaseParams.getIEC_Channel() == iec_channel:
return "Error: A bus with IEC_channel %d already exists\n" % iec_channel
bus = self.CTR.CTNAddChild(name, "LPCBus", iec_channel)
return "Error: Unable to create bus\n"
def RenameBus(self, iec_channel, name):
bus = self.CTR.GetChildByIECLocation((iec_channel,))
return "Error: No bus found\n"
for child in self.CTR.IterChildren():
if child != bus and child.BaseParams.getName() == name:
return "Error: A bus named %s already exists\n" % name
bus.BaseParams.setName(name)
def ChangeBusIECChannel(self, old_iec_channel, new_iec_channel):
bus = self.CTR.GetChildByIECLocation((old_iec_channel,))
return "Error: No bus found\n"
for child in self.CTR.IterChildren():
if child != bus and child.BaseParams.getIEC_Channel() == new_iec_channel:
return "Error: A bus with IEC_channel %d already exists\n" % new_iec_channel
self.CTR.UpdateProjectVariableLocation(str(old_iec_channel),
self.CTR.UpdateProjectVariableLocation(
bus.BaseParams.setIEC_Channel(new_iec_channel)
def RemoveBus(self, iec_channel):
bus = self.CTR.GetChildByIECLocation((iec_channel,))
return "Error: No bus found\n"
self.CTR.RemoveProjectVariableByFilter(str(iec_channel))
self.CTR.Children["LPCBus"].remove(bus)
def AddModule(self, parent, iec_channel, name, icode, icon=None):
module = self.CTR.GetChildByIECLocation(parent)
return "Error: No parent found\n"
for child in GetModuleChildren(module):
if child["name"] == name:
return "Error: A module named %s already exists\n" % name
elif child["IEC_Channel"] == iec_channel:
return "Error: A module with IEC_channel %d already exists\n" % iec_channel
GetLastModuleGroup(module).append({"name": name,
"IEC_Channel": iec_channel,
def RenameModule(self, iec_location, name):
module = self.CTR.GetChildByIECLocation(iec_location)
return "Error: No module found\n"
parent = self.CTR.GetChildByIECLocation(iec_location[:-1])
return "Error: No module found\n"
if module["name"] != name:
for child in GetModuleChildren(parent):
if child["name"] == name:
return "Error: A module named %s already exists\n" % name
def ChangeModuleIECChannel(self, old_iec_location, new_iec_channel):
module = self.CTR.GetChildByIECLocation(old_iec_location)
return "Error: No module found\n"
parent = self.CTR.GetChildByIECLocation(old_iec_location[:-1])
return "Error: No module found\n"
if module["IEC_Channel"] != new_iec_channel:
for child in GetModuleChildren(parent):
if child["IEC_Channel"] == new_iec_channel:
return "Error: A module with IEC_channel %d already exists\n" % new_iec_channel
self.CTR.UpdateProjectVariableLocation(".".join(map(str, old_iec_location)),
".".join(map(str, old_iec_location[:1] + (new_iec_channel,))))
module["IEC_Channel"] = new_iec_channel
def ChangeModuleInitCode(self, iec_location, icode):
module = self.CTR.GetChildByIECLocation(iec_location)
return "Error: No module found\n"
def RemoveModule(self, parent, iec_channel):
module = self.CTR.GetChildByIECLocation(parent)
return "Error: No parent found\n"
child = GetModuleBySomething(module, "IEC_Channel", (iec_channel,))
return "Error: No module found\n"
self.CTR.RemoveProjectVariableByFilter(".".join(map(str, parent + (iec_channel,))))
RemoveModuleChild(module, child)
def StartGroup(self, parent, name, icon=None):
module = self.CTR.GetChildByIECLocation(parent)
return "Error: No parent found\n"
for child in module["children"]:
if child["type"] == LOCATION_GROUP and child["name"] == name:
return "Error: A group named %s already exists\n" % name
module["children"].append({"name": name,
def AddVariable(self, parent, location, name, direction, type, rcode, pcode, description=""):
module = self.CTR.GetChildByIECLocation(parent)
return "Error: No parent found\n"
for child in GetModuleChildren(module):
if child["name"] == name:
return "Error: A variable named %s already exists\n" % name
if child["location"] == location and child["type"] == LOCATION_TYPES[direction]:
return "Error: A variable with location %s already exists\n" % ".".join(map(str, location))
GetLastModuleGroup(module).append({"name": name,
"type": LOCATION_TYPES[direction],
"description": description,
def ChangeVariableParams(self, parent, location, new_name, new_direction, new_type, new_rcode, new_pcode,
module = self.CTR.GetChildByIECLocation(parent)
return "Error: No parent found\n"
for child in GetModuleChildren(module):
if child["location"] == location and child["type"] == LOCATION_TYPES[new_direction]:
elif child["name"] == new_name:
return "Error: A variable named %s already exists\n" % new_name
return "Error: No variable found\n"
if variable["name"] != new_name:
self.CTR.UpdateProjectVariableName(variable["name"], new_name)
variable["name"] = new_name
variable["type"] = LOCATION_TYPES[new_direction]
variable["IEC_type"] = new_type
variable["retrieve"] = new_rcode
variable["publish"] = new_pcode
if new_description is not None:
variable["description"] = new_description
def RemoveVariable(self, parent, location, direction):
module = self.CTR.GetChildByIECLocation(parent)
return "Error: No parent found\n"
child = GetModuleVariable(module, location, direction)
return "Error: No variable found\n"
size = LOCATION_SIZES[self.CTR.GetBaseType(child["IEC_type"])]
address = "%" + LOCATION_DIRS[child["type"]] + size + ".".join(map(str, parent + location))
self.CTR.RemoveProjectVariableByAddress(address)
RemoveModuleChild(module, child)
return tuple(map(int, loc.split(".")))
def GetCmdFunction(function, arg_types, opt=0):
arg_number = len(arg_types)
def CmdFunction(self, line):
args_toks = line.split('"')
if len(args_toks) % 2 == 0:
self.Log.write("Error: Invalid command\n")
for num, arg in enumerate(args_toks):
args.extend(stripped.split(" "))
if opt == 0 and len(args) != arg_number:
elif len(args) > arg_number:
elif len(args) < arg_number - opt:
number = arg_number - opt
self.Log.write("Error: No argument%s expected\n" % extra)
self.Log.write("Error: 1 argument%s expected\n" % extra)
self.Log.write("Error: %d arguments%s expected\n" % (number, extra))
for num, arg in enumerate(args):
args[num] = arg_types[num](arg)
self.Log.write("Error: Invalid value for argument %d\n" % (num + 1))
func = getattr(self, function)
res = evaluator(func, *args)
cmdlog.append((function, line, res))
if len(cmdlog) > 100: # prevent debug log to grow too much
if isinstance(res, (StringType, UnicodeType)):
def CmdThreadProc(CTR, Log):
for function, (arg_types, opt) in {"Exit": ([], 0),
"SetProjectProperties": ([str, str, str, str], 0),
"SetOnlineMode": ([str, str], 1),
"AddBus": ([int, str, str], 1),
"RenameBus": ([int, str], 0),
"ChangeBusIECChannel": ([int, int], 0),
"AddModule": ([location, int, str, str, str], 1),
"RenameModule": ([location, str], 0),
"ChangeModuleIECChannel": ([location, int], 0),
"ChangeModuleInitCode": ([location, str], 0),
"RemoveModule": ([location, int], 0),
"StartGroup": ([location, str, str], 1),
"AddVariable": ([location, location, str, str, str, str, str, str], 1),
"ChangeVariableParams": (
[location, location, str, str, str, str, str, str], 1),
"RemoveVariable": ([location, location], 0)}.iteritems():
setattr(LPCBeremiz_Cmd, "do_%s" % function, GetCmdFunction(function, arg_types, opt))
lpcberemiz_cmd = LPCBeremiz_Cmd(CTR, Log)
Log = StdoutPseudoFile(port)
if projectOpen is not None:
projectOpen = DecodeFileSystemPath(projectOpen, False)
CTR = LPCProjectController(None, Log, buildpath)
if projectOpen is not None and os.path.isdir(projectOpen):
result = CTR.LoadProject(projectOpen)
Log.write("Error: Invalid project directory", result)
Log.write("Error: No such file or directory")
cmd_thread = Thread(target=CmdThreadProc, args=[CTR, Log])
# Install a exception handle for bug reports
AddExceptHook(os.getcwd(), __version__)
frame = LPCBeremiz(None, ctr=CTR, debug=True)