__version__ = "$Revision$"
import os, 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)
if __name__ == '__main__':
#wxversion.select(['2.8', '3.0'])
wxversion.select(['3.0'])
return os.path.join(CWD, *args)
if __name__ == '__main__':
PLC_GOT_module = ['GOT', 'GOT_111', 'GOT_131']
PLC_module.extend(PLC_MC9_module)
PLC_module.extend(PLC_GOT_module)
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:
__builtin__.__dict__["BMZ_DBG"] = os.path.exists("LPC_DEBUG")
if wx.VERSION >= (3, 0, 0):
app = wx.App(redirect=BMZ_DBG)
app = wx.PySimpleApp(redirect=BMZ_DBG)
app.SetAppName('beremiz')
if wx.VERSION < (3, 0, 0):
wx.InitAllImageHandlers()
from util.misc import InstallLocalRessources
InstallLocalRessources(_beremiz_folder)
from util.BitmapLibrary import AddBitmapFolder
AddBitmapFolder(os.path.join(_lpcmanager_path, "images"))
config = wx.ConfigBase.Get()
report = str(config.Read("Report"))
config.Write("Report", '0')
client = Client('https://2d582ee8f9234cc08283f8a686c92dae:631a5b935bf64b36b5a2d1781af437b2@sentry.io/174818')
_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')]
if arch in PLC_MC9_module:
features.libraries += [('Python', 'py_ext.PythonLibrary'), ('RTC', lambda: PLCLibraryRTC)]
elif arch in PLC_GOT_module:
features.libraries += [('Python', 'py_ext.PythonLibrary'), ('RTC', lambda: PLCLibraryRTC), ('GOT', lambda: PLCLibraryGOT)]
features.libraries += [('LPC', lambda: PLCLibraryLPC)]
features.catalog.pop([i for i, x in enumerate(features.catalog) if x[0].startswith('svg')][0])
mwmodbus = ('modbus', _('Modbus support'), _('Map located variables over Modbus'), 'modbus.modbus.RootClass')
features.catalog.append(mwmodbus)
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.ConfTreeNodeEditor import ConfTreeNodeEditor
from editors.CodeFileEditor import VariablesTable
from editors.CodeFileEditor import VariablesEditor
from editors.CodeFileEditor import CodeEditor
from editors.TextViewer import TextViewer, EditorPanel
from controls.CustomStyledTextCtrl import GetCursorPos
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 UriLocationEditor import UriLocationEditor
from UriLocationEditor import UriTextControl
from controls import TextCtrlAutoComplete
from py_ext import PythonFileCTNMixin
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()),
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((
for varinfo in varinfos])
# Runtime calls (start, stop, init, and cleanup)
for section in self.SECTIONS_NAMES:
rtcalls += "def _runtime_%s_%s():\n" % (location_str, section)
sectiontext = self.GetSection(section).strip()
sectiontext.replace('\n', '\n ') + "\n\n"
globalsection = self.GetSection("globals")
## Code generated by Beremiz python mixin confnode
## Code for PLC global variable access
from targets.typemapping import TypeTranslator
_%(pyextname)sGlobalsDesc = []
__ext_name__ = "%(pyextname)s"
PLCGlobalsDesc.append(( "%(pyextname)s" , _%(pyextname)sGlobalsDesc ))
## User code in "global" scope
## Beremiz python runtime calls
# 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'))
# C code for safe global variables access
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);
PYTHON_POLL* __%(name)s_notifier;
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);
if(!AtomicCompareExchange(&__%(name)s_rlock, 0, 1)){
__%(name)s_rbuffer = __GET_VAR(%(configname)s__%(uppername)s);
AtomicCompareExchange((long*)&__%(name)s_rlock, 1, 0);
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
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.
* Code generated by Beremiz py_ext confnode
* for safe global variables access
#include "iec_types_all.h"
/* User variables reference */
/* Beremiz confnode functions */
int __init_%(location_str)s(int argc,char **argv){
void __cleanup_%(location_str)s(void){
void __retrieve_%(location_str)s(void){
void __publish_%(location_str)s(void){
Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str)
pycfile = open(Gen_PyCfile_path, 'w')
pycfile.write(PyCFileContent)
matiec_CFLAGS = '"-I%s"' % os.path.abspath(
self.GetCTRoot().GetIECLibPath())
return ([(Gen_PyCfile_path, matiec_CFLAGS)],
("runtime_%s.py" % location_str, file(runtimefile_path, "rb")))
PythonFileCTNMixin.CTNGenerate_C = CTNGenerate_C
def GenerateSizerElements(self, sizer, elements, path, clean=True):
CTR = self.ParentWindow.CTR
CTR_BeremizRoot = CTR.BeremizRoot
CTR_AppFrame = CTR.AppFrame
uri = CTR_BeremizRoot.getURI_location().strip()
dialog = UriLocationEditor(CTR_AppFrame, uri)
answer = dialog.ShowModal()
CTR_BeremizRoot.setURI_location(uri)
if CTR._View is not None:
if CTR_AppFrame is not None:
CTR_AppFrame.RefreshTitle()
CTR_AppFrame.RefreshFileMenu()
CTR_AppFrame.RefreshEditMenu()
CTR_AppFrame.RefreshPageTitles()
from editors.ConfTreeNodeEditor import GenStaticBitmap
for element_infos in elements:
element_path = "%s.%s" % (path, element_infos["name"])
element_path = element_infos["name"]
if element_infos["type"] == "element":
name = element_infos["name"]
value = element_infos["value"]
label += " - %s" % _(value)
staticbox = wx.StaticBox(self.ParamsEditor,
label=_(label), size=wx.Size(10, 0))
staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL)
flags = (wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT)
sizer.AddSizer(staticboxsizer, border=5, flag=flags)
self.GenerateSizerElements(staticboxsizer,
element_infos["children"],
boxsizer = wx.FlexGridSizer(cols=3, rows=1)
boxsizer.AddGrowableCol(1)
flags = (wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT)
sizer.AddSizer(boxsizer, border=5, flag=flags)
staticbitmap = GenStaticBitmap(ID=-1, bitmapname=element_infos["name"],
name="%s_bitmap" % element_infos["name"], parent=self.ParamsEditor,
pos=wx.Point(0, 0), size=wx.Size(24, 24), style=0)
boxsizer.AddWindow(staticbitmap, border=5, flag=wx.RIGHT)
statictext = wx.StaticText(self.ParamsEditor,
label="%s:" % _(element_infos["name"]))
boxsizer.AddWindow(statictext, border=5,
flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT)
if isinstance(element_infos["type"], types.ListType):
if isinstance(element_infos["value"], types.TupleType):
browse_boxsizer = wx.BoxSizer(wx.HORIZONTAL)
boxsizer.AddSizer(browse_boxsizer)
textctrl = wx.TextCtrl(self.ParamsEditor,
size=wx.Size(275, -1), style=wx.TE_READONLY)
if element_infos["value"] is not None:
textctrl.SetValue(element_infos["value"][0])
value_infos = element_infos["value"][1]
browse_boxsizer.AddWindow(textctrl)
button = wx.Button(self.ParamsEditor,
label="...", size=wx.Size(25, 25))
browse_boxsizer.AddWindow(button)
button.Bind(wx.EVT_BUTTON,
self.GetBrowseCallBackFunction(element_infos["name"], textctrl, element_infos["type"],
value_infos, element_path),
combobox = wx.ComboBox(self.ParamsEditor,
size=wx.Size(300, -1), style=wx.CB_READONLY)
boxsizer.AddWindow(combobox)
if element_infos["use"] == "optional":
if len(element_infos["type"]) > 0 and isinstance(element_infos["type"][0], types.TupleType):
for choice, xsdclass in element_infos["type"]:
name = element_infos["name"]
value = element_infos["value"]
staticbox = wx.StaticBox(self.ParamsEditor,
label="%s - %s" % (_(name), _(value)), size=wx.Size(10, 0))
staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL)
sizer.AddSizer(staticboxsizer, border=5, flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT)
self.GenerateSizerElements(staticboxsizer, element_infos["children"], element_path)
callback = self.GetChoiceContentCallBackFunction(combobox, staticboxsizer, element_path)
for choice in element_infos["type"]:
callback = self.GetChoiceCallBackFunction(combobox, element_path)
if element_infos["value"] is None:
combobox.SetStringSelection("")
combobox.SetStringSelection(element_infos["value"])
combobox.Bind(wx.EVT_COMBOBOX, callback, combobox)
elif isinstance(element_infos["type"], types.DictType):
if "min" in element_infos["type"]:
scmin = element_infos["type"]["min"]
if "max" in element_infos["type"]:
scmax = element_infos["type"]["max"]
spinctrl = wx.SpinCtrl(self.ParamsEditor,
size=wx.Size(300, -1), style=wx.SP_ARROW_KEYS | wx.ALIGN_RIGHT)
spinctrl.SetRange(scmin, scmax)
boxsizer.AddWindow(spinctrl)
if element_infos["value"] is not None:
spinctrl.SetValue(element_infos["value"])
spinctrl.Bind(wx.EVT_SPINCTRL,
self.GetTextCtrlCallBackFunction(spinctrl, element_path),
if element_infos["type"] == "boolean":
checkbox = wx.CheckBox(self.ParamsEditor, size=wx.Size(17, 25))
boxsizer.AddWindow(checkbox)
if element_infos["value"] is not None:
checkbox.SetValue(element_infos["value"])
checkbox.Bind(wx.EVT_CHECKBOX,
self.GetCheckBoxCallBackFunction(checkbox, element_path),
elif element_infos["type"] in ["unsignedLong", "long", "integer"]:
if element_infos["type"].startswith("unsigned"):
spinctrl = wx.SpinCtrl(self.ParamsEditor,
size=wx.Size(300, -1), style=wx.SP_ARROW_KEYS | wx.ALIGN_RIGHT)
spinctrl.SetRange(scmin, scmax)
boxsizer.AddWindow(spinctrl)
if element_infos["value"] is not None:
spinctrl.SetValue(element_infos["value"])
spinctrl.Bind(wx.EVT_SPINCTRL,
self.GetTextCtrlCallBackFunction(spinctrl, element_path),
choices = self.ParentWindow.GetConfigEntry(element_path, [""])
CTR = self.ParentWindow.CTR
if element_infos["name"] == "URI_location":
textctrl = UriTextControl(name=element_infos["name"],
parent=self.ParamsEditor,
element_path=element_path,
textctrl = TextCtrlAutoComplete(name=element_infos["name"],
parent=self.ParamsEditor,
element_path=element_path,
boxsizer.AddWindow(textctrl)
if element_infos["value"] is not None:
textctrl.ChangeValue(str(element_infos["value"]))
callback = self.GetTextCtrlCallBackFunction(textctrl, element_path)
textctrl.Bind(wx.EVT_TEXT_ENTER, callback)
textctrl.Bind(wx.EVT_TEXT, callback)
textctrl.Bind(wx.EVT_KILL_FOCUS, callback)
ConfTreeNodeEditor.GenerateSizerElements = GenerateSizerElements
def ResetSearchResults(self):
self.SearchResults = None
self.VariableSearchResults = []
self.CurrentFindHighlight = None
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)
ERROR_HIGHLIGHT: STC_CODE_ERROR,
SEARCH_RESULT_HIGHLIGHT: STC_CODE_SEARCH_RESULT,
def FindVariable(self, direction, search_params, variables):
if self.SearchParams != search_params:
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 = []
value = variables.VariablesGrid.GetCellValue(row, col)
search_value = search_params['find_pattern']
if search_params['case_sensitive'] == False:
search_value = search_value.lower()
if search_value in value:
self.VariableSearchResults.append((row, col))
if len(self.VariableSearchResults) > 0:
(r, c) = self.VariableSearchResults[self.index]
variables.VariablesGrid.SetCellBackgroundColour(r, c, SEARCH_RESULT_HIGHLIGHT[1])
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):
from xml.dom import minidom
dir_list = next(os.walk(self.ProjectPath))[1]
if dir not in ["build", "CanOpen@CanOpen", "project_files"]:
path = os.path.join(self.ProjectPath, dir, 'pyfile.xml')
pyfile = minidom.parse(path)
variablelist = pyfile.getElementsByTagName('variable')
if criteria["find_pattern"] in s.attributes['name'].value:
PLCControler.SearchInPyfile = SearchInPyfile
def OnSearchInProjectMenu(self, event):
dialog = SearchInProjectDialog(self)
if dialog.ShowModal() == wx.ID_OK:
criteria = dialog.GetCriteria()
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):
item_name = infos["name"]
if infos["type"] in ITEMS_UNEDITABLE:
if len(infos["values"]) == 1:
return self.GenerateProjectTreeBranch(root, infos["values"][0], True)
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"])])
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():
item, root_cookie = self.ProjectTree.GetNextChild(root, root_cookie)
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.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))
SearchResultPanel.SetSearchResults = SetSearchResults
def AddPyFileResults(self, search_results):
distinct_list = list(set(search_results))
for result in distinct_list:
matches = search_results.count(result)
root = self.SearchResultsTree.GetRootItem()
self.SearchResultsTree.AppendItem(root, name[0], image=self.TreeImageDict["py_file"])
root = self.SearchResultsTree.GetRootItem()
self.SearchResultsTree.AppendItem(root, name[0], image=self.TreeImageDict["wx_glade"])
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.PySearchResults = []
SearchResultPanel.ResetSearchResults = ResetSearchResults
def GetPythonTextCtrlDClickFunction(self, item):
for type in self.ParentWindow.CTR.Children.values():
if name.BaseParams.attrib['Name'] == item:
selected = self.ParentWindow.TabsOpened.GetSelection()
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)
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
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)
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)
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)
for idx, (name, label) in enumerate([('XScale', _('Horizontal:')),
('YScale', _('Vertical:'))]):
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,
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.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
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 GetFunctionBlockTypes(self, tagname="", debug=False):
project = self.GetProject(debug)
words = tagname.split("::")
if project is not None and words[0] in ["P", "T", "A"]:
for blocks in self.TotalTypesDict.itervalues():
for sectioname, block in blocks:
if block["type"] == "functionBlock":
blocktypes.append(block["name"])
blocktypes.extend([pou.getname() for pou in project.getpous(name, ["functionBlock"]) if (name is None or len(self.GetInstanceList(pou, name, debug)) == 0)])
blocktypes.extend([pou.getname() for pou in project.getpous(name, ["functionBlock"])])
PLCControler.GetFunctionBlockTypes = GetFunctionBlockTypes
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)
self.dialog = WampOptionsEditor(self.Parent.Parent, options, desc)
answer = self.dialog.ShowModal()
opt,OnChange,value,description = self.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 RefreshView(self, variablepanel=True):
EditorPanel.RefreshView(self, variablepanel)
if self.Controler is not None:
self.DisableEvents = True
old_cursor_pos = self.GetCurrentPos()
line = self.Editor.GetFirstVisibleLine()
column = self.Editor.GetXOffset()
old_text = self.GetText()
new_text = self.Controler.GetEditedElementText(self.TagName, self.Debug)
new_cursor_pos = GetCursorPos(old_text, new_text)
self.Editor.LineScroll(column, line)
if new_cursor_pos != None:
self.Editor.GotoPos(new_cursor_pos)
self.Editor.GotoPos(old_cursor_pos)
self.Editor.EmptyUndoBuffer()
self.DisableEvents = False
self.RefreshVariableTree()
self.TypeNames = [typename.upper() for typename in self.Controler.GetDataTypes(self.TagName, True, self.Debug)]
self.EnumeratedValues = [value.upper() for value in self.Controler.GetEnumeratedDataValues()]
for category in self.Controler.GetBlockTypes(self.TagName, self.Debug):
for blocktype in category["list"]:
blockname = blocktype["name"].upper()
if blocktype["type"] == "function" and blockname not in self.Keywords and blockname not in self.Variables.keys():
interface = dict([(name, {}) for name, type, modifier in blocktype["inputs"] + blocktype["outputs"] if name != ''])
for param in ["EN", "ENO"]:
if not interface.has_key(param):
if self.Functions.has_key(blockname):
self.Functions[blockname]["interface"].update(interface)
self.Functions[blockname]["extensible"] |= blocktype["extensible"]
self.Functions[blockname] = {"interface": interface,
"extensible": blocktype["extensible"]}
TextViewer.RefreshView = RefreshView
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.
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.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()
# self.GrandParent.CTR.logger.write_warning("Can not reset log messages!\n")
# self.ResetLogMessages()
# LogViewer.OnCleanButton = OnCleanButton
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
defaultGenerateNewName = PLCControler.GenerateNewName
def newGenerateNewName(self, tagname, name, format, start_idx=0, exclude={}, debug=False):
return 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
defaultGetCFLAGS = canfestival.local_canfestival_config.getCFLAGS
defaultGetLDFLAGS = canfestival.local_canfestival_config.getLDFLAGS
return str(defaultGetCFLAGS(*args))
return str(defaultGetLDFLAGS(*args))
canfestival.local_canfestival_config.getCFLAGS = getCFLAGS
canfestival.local_canfestival_config.getLDFLAGS = getLDFLAGS
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)
threads = threading.enumerate()
build_succeeded = ProjectController._Build(args)
wx.CallAfter(args.AppFrame.RefreshAll)
# pub.sendMessage("build", msg="True")
# self._Clean(building = True)
save = self.ProjectTestModified()
self.AppFrame._Refresh(TITLE, FILEMENU)
if self.BuildPath is not None:
mycopytree(self.OrigBuildPath, self.BuildPath)
if len(filter(lambda x: x.getName() == "OnBuildThread", threads)) == 0:
self._Clean(building=True)
build_thread = Thread(name="OnBuildThread", target=__Build, args=(self,save))
build_thread.daemon = True
# frame = splash.SmartehScreenSplash(self.AppFrame, bitmap=GetPath(splash.SPLASH_FN), pub=pub)
self.logger.write(_("Already building project.\n"))
# build_succeeded = 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)
stop_thread = Thread(target=ProjectController._Stop, args=(self,))
stop_thread.daemon = True
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]))
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"))
self.logger.write_error(_("Build directory already clean\n"))
self.ShowMethod("_showIECcode", False)
self.EnableMethod("_Clean", False)
self.CompareLocalAndRemotePLC()
if self.OnlineMode == "NORMAL":
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:
transfer_thread = Thread(target=ProjectController._Transfer, args=(self,))
transfer_thread.daemon = True
#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():
run_thread = Thread(target=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 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"))
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):
config = wx.ConfigBase.Get()
export = str(config.Read("Exporter"))
config.Write("Exporter", '0')
checker = str(config.Read("Checker"))
config.Write("Checker", '0')
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=ID_EXPORT,
kind=wx.ITEM_NORMAL, text=_(u'Export'))
parent.AppendCheckItem(ID_CHECK, u'Disable checking')
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, lambda event: VariableWriter(self, event, self.CTR.ProjectPath), id=ID_EXPORT)
self.Bind(wx.EVT_MENU, lambda event: self.CheckEnabler(event), id=ID_CHECK)
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 CheckEnabler(self, event):
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()
# close wxGlade if running
config = wx.ConfigBase.Get()
wxGladePid = str(config.Read("BeremizRoot.wxGlade"))
os.kill(int(wxGladePid), signal.SIGTERM)
config.Write("BeremizRoot.wxGlade", str(wxGladePid))
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, Lock
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)
# reset pid of running wxGlade
config = wx.ConfigBase.Get()
config.Write("BeremizRoot.wxGlade", str(wxGladePid))
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)