# On Windows install schema, we compute path to beremiz
# relative to path to python folder (sys.path[0] in that case)
# note: beware that wx import messes up sys.path[0]
_dist_folder = os.path.split(sys.path[0])[0]
_beremiz_folder = os.path.join(_dist_folder, "beremiz")
# Then we add it to sys.path, to make "import Beremiz" possible
sys.path.append(_beremiz_folder)
from util.BitmapLibrary import AddBitmapFolder
# Path of directory containing current python file
_lpcmanager_path = os.path.split(__file__)[0]
from LPCArch import PLC_module, SetLPCArch
class LPCManagerLauncher(BeremizIDELauncher):
BeremizIDELauncher.__init__(self)
self.extensions = [os.path.join(_lpcmanager_path, "extention.py")]
print("\nUsage of LPCManager.py :")
print("\n %s Projectpath Buildpath port arch\n" % sys.argv[0])
def ProcessCommandLineArgs(self):
# Command line arguments parsing
opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
except getopt.GetoptError:
# print help information and exit:
# asking for help causes exit
if o in ("-h", "--help"):
self.projectOpen = args[0]
# overload with exacltly same code, but this is intended.
# we want extensions to use globals of this module, not Beremiz.py
CMDpipe = self.StdoutPseudoFile.StdoutPseudoFile(self.port, self.debug)
if self.projectOpen is not None:
self.projectOpen = self.BeremizIDE.DecodeFileSystemPath(self.projectOpen, False)
CTR = self.LPCProjectController.LPCProjectController(
None, CMDpipe, self.buildpath, self.arch)
if self.projectOpen is not None and os.path.isdir(self.projectOpen):
result = CTR.LoadProject(self.projectOpen)
CMDpipe.write("Error: Invalid project directory", result)
CMDpipe.write("Error: No such file or directory")
lpcberemiz_cmd = LPCCommand(CTR, CMDpipe)
cmd_thread = Thread(target=lpcberemiz_cmd.cmdloop)
# TODO: join() when exiting
self.frame = self.LPCBeremiz.LPCBeremiz(None, ctr=CTR)
# the "Show" command from composer does it instead
def CreateApplication(self):
BeremizIDELauncher.CreateApplication(self)
# Add LPCmanager's image folder to searched ones.
AddBitmapFolder(os.path.join(_lpcmanager_path, "images"))
from editors.CodeFileEditor import CodeEditor
from controls import ProjectPropertiesPanel
from controls.SearchResultPanel import SearchResultPanel
# from controls.LogViewer import LogViewer
from VariableExporter import VariableWriter
from PLCControler import PLCControler, ITEMS_UNEDITABLE, ITEM_POU
from wx.lib.scrolledpanel import ScrolledPanel
from types import MethodType
from IDEFrame import IDEFrame
from dialogs import SearchInProjectDialog
from controls import TextCtrlAutoComplete
from py_ext import PythonFileCTNMixin
from plcopen.structures import TestIdentifier, IDENTIFIER_MODEL
def CTNGenerate_C(self, buildpath, locations):
# location string for that CTN
location_str = "_".join(map(lambda x: str(x),
self.GetCurrentLocation()))
configname = self.GetCTRoot().GetProjectConfigNames()[0]
pyextname = self.CTNName()
varinfos = map(lambda variable: {
"name": variable.getname(),
"desc": repr(variable.getdesc()),
"onchangecode": '"' + variable.getonchange() + \
"('" + variable.getname() + "')\"" \
if variable.getonchange() else '""',
"onchange": repr(variable.getonchange()) \
if variable.getonchange() else None,
"opts": repr(variable.getopts()),
"configname": configname.upper(),
"uppername": variable.getname().upper(),
"IECtype": variable.gettype(),
"initial": repr(variable.getinitial()),
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
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
if __name__ == '__main__':
lpcmanager = LPCManagerLauncher()