--- a/Beremiz_service.py Fri Mar 22 11:10:37 2019 +0100
+++ b/Beremiz_service.py Fri Mar 22 13:26:31 2019 +0100
@@ -31,7 +31,6 @@
from threading import Thread, Semaphore, Lock, currentThread
from builtins import str as text
from past.builtins import execfile
from six.moves import builtins
--- a/PLCOpenEditor.py Fri Mar 22 11:10:37 2019 +0100
+++ b/PLCOpenEditor.py Fri Mar 22 13:26:31 2019 +0100
@@ -62,7 +62,8 @@
# Define PLCOpenEditor FileMenu extra items id
ID_PLCOPENEDITORFILEMENUGENERATE,
-] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)]
+ ID_PLCOPENEDITORFILEMENUGENERATEAS, +] = [wx.NewId() for _init_coll_FileMenu_Items in range(2)] beremiz_dir = paths.AbsDir(__file__)
@@ -86,6 +87,8 @@
kind=wx.ITEM_NORMAL, text=_(u'Save As...') + '\tCTRL+SHIFT+S')
AppendMenu(parent, help='', id=ID_PLCOPENEDITORFILEMENUGENERATE,
kind=wx.ITEM_NORMAL, text=_(u'Generate Program') + '\tCTRL+G')
+ AppendMenu(parent, help='', id=ID_PLCOPENEDITORFILEMENUGENERATEAS, + kind=wx.ITEM_NORMAL, text=_(u'Generate Program As...') + '\tCTRL+SHIFT+G') AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP,
kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P')
@@ -108,6 +111,8 @@
self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS)
self.Bind(wx.EVT_MENU, self.OnGenerateProgramMenu,
id=ID_PLCOPENEDITORFILEMENUGENERATE)
+ self.Bind(wx.EVT_MENU, self.OnGenerateProgramAsMenu, + id=ID_PLCOPENEDITORFILEMENUGENERATEAS) 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)
@@ -118,7 +123,8 @@
(wx.ID_OPEN, "open", _(u'Open'), None),
(wx.ID_SAVE, "save", _(u'Save'), None),
(wx.ID_SAVEAS, "saveas", _(u'Save As...'), None),
- (wx.ID_PRINT, "print", _(u'Print'), None)])
+ (wx.ID_PRINT, "print", _(u'Print'), None), + (ID_PLCOPENEDITORFILEMENUGENERATE, "Build", _(u'Generate Program'), None)]) def _init_coll_HelpMenu_Items(self, parent):
AppendMenu(parent, help='', id=wx.ID_HELP,
@@ -232,6 +238,8 @@
self.FileMenu.Enable(wx.ID_SAVEAS, True)
MenuToolBar.EnableTool(wx.ID_SAVEAS, True)
self.FileMenu.Enable(ID_PLCOPENEDITORFILEMENUGENERATE, True)
+ MenuToolBar.EnableTool(ID_PLCOPENEDITORFILEMENUGENERATE, True) + self.FileMenu.Enable(ID_PLCOPENEDITORFILEMENUGENERATEAS, True) self.FileMenu.Enable(wx.ID_CLOSE, False)
self.FileMenu.Enable(wx.ID_PAGE_SETUP, False)
@@ -245,6 +253,8 @@
self.FileMenu.Enable(wx.ID_SAVEAS, False)
MenuToolBar.EnableTool(wx.ID_SAVEAS, False)
self.FileMenu.Enable(ID_PLCOPENEDITORFILEMENUGENERATE, False)
+ MenuToolBar.EnableTool(ID_PLCOPENEDITORFILEMENUGENERATE, False) + self.FileMenu.Enable(ID_PLCOPENEDITORFILEMENUGENERATEAS, False) def OnNewProjectMenu(self, event):
if self.Controler is not None and not self.CheckSaveBeforeClosing():
@@ -305,27 +315,39 @@
def OnGenerateProgramMenu(self, event):
+ result = self.Controler.GetProgramFilePath() + self.GenerateProgramAs() + self.GenerateProgram(result) + def OnGenerateProgramAsMenu(self, event): + self.GenerateProgramAs() + def GenerateProgramAs(self): dialog = wx.FileDialog(self, _("Choose a file"), os.getcwd(), os.path.basename(self.Controler.GetProgramFilePath()), _("ST files (*.st)|*.st|All files|*.*"), wx.SAVE | wx.CHANGE_DIR)
if dialog.ShowModal() == wx.ID_OK:
- filepath = dialog.GetPath()
- header, icon = _("Done"), wx.ICON_INFORMATION
- if os.path.isdir(os.path.dirname(filepath)):
- _program, errors, warnings = self.Controler.GenerateProgram(filepath)
- message_text += "".join([_("warning: %s\n") % warning for warning in warnings])
- message_text += "".join([_("error: %s\n") % error for error in errors])
- message_text += _("Can't generate program to file %s!") % filepath
- header, icon = _("Error"), wx.ICON_ERROR
- message_text += _("Program was successfully generated!")
+ self.GenerateProgram(dialog.GetPath()) + def GenerateProgram(self, filepath=None): + header, icon = _("Done"), wx.ICON_INFORMATION + if os.path.isdir(os.path.dirname(filepath)): + _program, errors, warnings = self.Controler.GenerateProgram(filepath) + message_text += "".join([_("warning: %s\n") % warning for warning in warnings]) + message_text += "".join([_("error: %s\n") % error for error in errors]) + message_text += _("Can't generate program to file %s!") % filepath + header, icon = _("Error"), wx.ICON_ERROR - message_text += _("\"%s\" is not a valid folder!") % os.path.dirname(filepath)
- header, icon = _("Error"), wx.ICON_ERROR
- message = wx.MessageDialog(self, message_text, header, wx.OK | icon)
+ message_text += _("Program was successfully generated!") + message_text += _("\"%s\" is not a valid folder!") % os.path.dirname(filepath) + header, icon = _("Error"), wx.ICON_ERROR + message = wx.MessageDialog(self, message_text, header, wx.OK | icon) def OnPLCOpenEditorMenu(self, event):
wx.MessageBox(_("No documentation available.\nComing soon."))
--- a/connectors/ConnectorBase.py Fri Mar 22 11:10:37 2019 +0100
+++ b/connectors/ConnectorBase.py Fri Mar 22 13:26:31 2019 +0100
@@ -4,7 +4,7 @@
# See COPYING file for copyrights details.
from __future__ import absolute_import
class ConnectorBase(object):
@@ -12,7 +12,7 @@
def BlobFromFile(self, filepath, seed):
blobID = self.SeedBlob(seed)
with open(filepath, "rb") as f:
--- a/connectors/PYRO/PSK_Adapter.py Fri Mar 22 11:10:37 2019 +0100
+++ b/connectors/PYRO/PSK_Adapter.py Fri Mar 22 13:26:31 2019 +0100
@@ -1,10 +1,40 @@
+# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# Copyright (C) 2019: Edouard TISSERANT +# See COPYING file for copyrights details. +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +The TLS-PSK adapter that handles SSL connections instead of regular sockets, +but using Pre Shared Keys instead of Certificates from __future__ import absolute_import
from __future__ import print_function
from Pyro.core import PyroURI
from Pyro.protocol import _connect_socket, TCPConnection, PYROAdapter
@@ -12,13 +42,13 @@
from Pyro.util import Log
-# The TLS-PSK adapter that handles SSL connections instead of regular sockets,
-# but using Pre Shared Keys instead of Certificates
class PYROPSKAdapter(PYROAdapter):
- # This is essentialy the same as in Pyro/protocol.py
- # only raw_sock wrapping into sock through sslpsk.wrap_socket was added
- # Pyro unfortunately doesn't allow cleaner customization
+ This is essentialy the same as in Pyro/protocol.py + only raw_sock wrapping into sock through sslpsk.wrap_socket was added + Pyro unfortunately doesn't allow cleaner customization def bindToURI(self, URI):
with self.lock: # only 1 thread at a time can bind the URI
@@ -37,7 +67,7 @@
# receive the authentication challenge string, and use that to build the actual identification string.
authChallenge = self.recvAuthChallenge(conn)
- except ProtocolError, x:
+ except ProtocolError as x: # check if we were denied
if hasattr(x, "partialMsg") and x.partialMsg[:len(self.denyMSG)] == self.denyMSG:
raise ConnectionDeniedError(Pyro.constants.deniedReasons[int(x.partialMsg[-1])])
@@ -70,9 +100,6 @@
return _getProtocolAdapter(protocol)
-Pyro.protocol.getProtocolAdapter = getProtocolAdapter
_processStringURI = Pyro.core.processStringURI
@@ -91,4 +118,13 @@
return _processStringURI(URI)
-Pyro.core.processStringURI = processStringURI
+ Add PyroAdapter to the list of available in + Pyro adapters and handle new supported protocols + This function should be called after + reimport of Pyro module to enable PYROS:// again. + Pyro.protocol.getProtocolAdapter = getProtocolAdapter + Pyro.core.processStringURI = processStringURI --- a/connectors/PYRO/__init__.py Fri Mar 22 11:10:37 2019 +0100
+++ b/connectors/PYRO/__init__.py Fri Mar 22 13:26:31 2019 +0100
@@ -37,13 +37,19 @@
from Pyro.errors import PyroError
import PSKManagement as PSK
+import connectors.PYRO.PSK_Adapter from runtime import PlcStatus
-# this module attribute contains a list of DNS-SD (Zeroconf) service types
-# supported by this connector confnode.
-# for connectors that do not support DNS-SD, this attribute can be omitted
-# or set to an empty list.
+def switch_pyro_adapter(use_ssl): + Reloads Pyro module with new settings. + This is workaround for Pyro, because it doesn't work with SSL wrapper. + # Pyro.config.PYRO_BROKEN_MSGWAITALL = use_ssl + connectors.PYRO.PSK_Adapter.setupPSKAdapter() def PYRO_connector_factory(uri, confnodesroot):
@@ -53,8 +59,9 @@
confnodesroot.logger.write(_("PYRO connecting to URI : %s\n") % uri)
scheme, location = uri.split("://")
- import connectors.PYRO.PSK_Adapter # pylint: disable=wrong-import-order,unused-import,wrong-import-position
+ use_ssl = scheme == "PYROS" + switch_pyro_adapter(use_ssl) schemename = "PYROLOCPSK"
url, ID = location.split('#') # TODO fix exception when # not found
@@ -73,10 +80,10 @@
# Try to get the proxy object
RemotePLCObjectProxy = Pyro.core.getAttrProxyForURI(schemename + "://" + location + "/PLCObject")
confnodesroot.logger.write_error(
_("Connection to {loc} failed with exception {ex}\n").format(
- loc=location, exo=str(e)))
+ loc=location, ex=str(e))) RemotePLCObjectProxy.adapter.setTimeout(60)
--- a/connectors/SchemeEditor.py Fri Mar 22 11:10:37 2019 +0100
+++ b/connectors/SchemeEditor.py Fri Mar 22 13:26:31 2019 +0100
@@ -26,7 +26,7 @@
self.txtctrls[tag] = txtctrl
(wx.StaticText(self, label=label),
- wx.ALIGN_CENTER_VERTICAL),
+ wx.ALIGN_CENTER_VERTICAL), self.fieldsizer.AddWindow(win, flag=flag)
@@ -39,7 +39,7 @@
# use a callafter, as editor can be deleted by calling SetURI
partial(wx.CallAfter, parent.SetURI),
- self.txtctrls[tag].SetValue)
+ self.txtctrls["ID"].SetValue) self.mainsizer.AddWindow(self.idselector)
self.SetSizer(self.mainsizer)
--- a/connectors/__init__.py Fri Mar 22 11:10:37 2019 +0100
+++ b/connectors/__init__.py Fri Mar 22 13:26:31 2019 +0100
@@ -28,9 +28,7 @@
from __future__ import absolute_import
from os import listdir, path
-import util.paths as paths
from connectors.ConnectorBase import ConnectorBase
-from types import ClassType
connectors_packages = ["PYRO", "WAMP"]
@@ -119,8 +117,8 @@
# new class inheriting from generic and specific connector base classes
- return ClassType(_scheme + "_connector",
- (ConnectorBase, connector_specific_class), {})()
+ return type(_scheme + "_connector", + (ConnectorBase, connector_specific_class), {})() def EditorClassFromScheme(scheme):
--- a/controls/IDBrowser.py Fri Mar 22 11:10:37 2019 +0100
+++ b/controls/IDBrowser.py Fri Mar 22 13:26:31 2019 +0100
@@ -98,8 +98,8 @@
args(_("Last URI"), COL_URI, width=300 if big else 80),
args(_("Description"), COL_DESC, width=300 if big else 200,
mode=dv.DATAVIEW_CELL_EDITABLE
- else dv.DATAVIEW_CELL_INERT),
+ else dv.DATAVIEW_CELL_INERT), args(_("Last connection"), COL_LAST, width=120),
--- a/runtime/PLCObject.py Fri Mar 22 11:10:37 2019 +0100
+++ b/runtime/PLCObject.py Fri Mar 22 13:26:31 2019 +0100
@@ -28,14 +28,14 @@
-import _ctypes # pylint: disable=wrong-import-order
+from tempfile import mkstemp +from functools import wraps, partial from six.moves import xrange
from past.builtins import execfile
-from tempfile import mkstemp
-from functools import wraps, partial
from runtime.typemapping import TypeTranslator
from runtime.loglevels import LogLevelsDefault, LogLevelsCount
@@ -465,7 +465,7 @@
def SeedBlob(self, seed):
- blob = (mkstemp(dir=self.tmpdir) + (md5.new(),))
+ blob = (mkstemp(dir=self.tmpdir) + (hashlib.new('md5'),)) _fobj, _path, md5sum = blob
newBlobID = md5sum.digest()
@@ -606,7 +606,7 @@
def GetTraceVariables(self, DebugToken):
- if (DebugToken is not None and DebugToken == self.DebugToken):
+ if DebugToken is not None and DebugToken == self.DebugToken: return self.PLCStatus, self._TracesSwap()
return PlcStatus.Broken, []
--- a/runtime/Stunnel.py Fri Mar 22 11:10:37 2019 +0100
+++ b/runtime/Stunnel.py Fri Mar 22 13:26:31 2019 +0100
@@ -1,4 +1,5 @@
from __future__ import absolute_import
+from __future__ import print_function from binascii import b2a_hqx
@@ -11,6 +12,17 @@
+ Restart stunnel service using SysV init stript + to apply new generated credentials + call(restart_stunnel_cmdline) + print(_("Couldn't restart stunnel service")) # b2a_hqx output len is 4/3 input len
@@ -20,7 +32,7 @@
PSKstring = ID+":"+secretstring
with open(PSKpath, 'w') as f:
- call(restart_stunnel_cmdline)
def ensurePSK(ID, PSKpath):
--- a/runtime/WampClient.py Fri Mar 22 11:10:37 2019 +0100
+++ b/runtime/WampClient.py Fri Mar 22 13:26:31 2019 +0100
@@ -135,7 +135,7 @@
self.register(GetCallee(name), u'.'.join((ID, name)), registerOptions)
for name in SubscribedEvents:
- self.subscribe(GetCallee(name), unicode(name))
+ self.subscribe(GetCallee(name), text(name)) @@ -151,7 +151,7 @@
def publishWithOwnID(self, eventID, value):
ID = self.config.extra["ID"]
- self.publish(unicode(ID+'.'+eventID), value)
+ self.publish(text(ID+'.'+eventID), value) class ReconnectingWampWebSocketClientFactory(WampWebSocketClientFactory, ReconnectingClientFactory):
@@ -343,12 +343,12 @@
def PublishEvent(eventID, value):
if getWampStatus() == "Attached":
- _WampSession.publish(unicode(eventID), value)
+ _WampSession.publish(text(eventID), value) def PublishEventWithOwnID(eventID, value):
if getWampStatus() == "Attached":
- _WampSession.publishWithOwnID(unicode(eventID), value)
+ _WampSession.publishWithOwnID(text(eventID), value) # WEB CONFIGURATION INTERFACE
--- a/runtime/Worker.py Fri Mar 22 11:10:37 2019 +0100
+++ b/runtime/Worker.py Fri Mar 22 13:26:31 2019 +0100
@@ -9,9 +9,9 @@
from __future__ import absolute_import
from threading import Lock, Condition
+from six.moves import _thread @@ -51,20 +51,27 @@
self.free = Condition(self.mutex)
+ def reraise(self, job): + reraise exception happend in a job + @param job: job where original exception happend + exc_type = job.exc_info[0] + exc_value = job.exc_info[1] + exc_traceback = job.exc_info[2] + six.reraise(exc_type, exc_value, exc_traceback) def runloop(self, *args, **kwargs):
meant to be called by worker thread (blocking)
- self._threadID = thread.get_ident()
+ self._threadID = _thread.get_ident() _job = job(*args, **kwargs)
- raise _job.exc_info[0], _job.exc_info[1], _job.exc_info[2]
@@ -86,7 +93,7 @@
_job = job(*args, **kwargs)
- if self._threadID == thread.get_ident():
+ if self._threadID == _thread.get_ident(): # if caller is worker thread execute immediately
@@ -106,10 +113,7 @@
- exc_type = _job.exc_info[0]
- exc_value = _job.exc_info[1]
- exc_traceback = _job.exc_info[2]
- six.reraise(exc_type, exc_value, exc_traceback)
--- a/tests/first_steps/plc.xml Fri Mar 22 11:10:37 2019 +0100
+++ b/tests/first_steps/plc.xml Fri Mar 22 13:26:31 2019 +0100
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.plcopen.org/xml/tc6_0201">
<fileHeader companyName="Beremiz" productName="Beremiz" productVersion="1" creationDateTime="2016-10-24T18:09:22"/>
- <contentHeader name="First Steps" modificationDateTime="2019-02-13T10:30:06">
+ <contentHeader name="First Steps" modificationDateTime="2018-09-26T12:52:51"> @@ -676,11 +676,6 @@
<externalVars constant="true">
<variable name="ResetCounterValue">
@@ -813,7 +808,7 @@
<relPosition x="85" y="15"/>
</connectionPointOutAction>
- <actionBlock localId="8" height="63" width="148" executionOrderId="0">
+ <actionBlock localId="8" height="52" width="164" executionOrderId="0"> <position x="154" y="191"/>
<relPosition x="0" y="15"/>
@@ -830,14 +825,6 @@
- <action localId="0" qualifier="S">
- <relPosition x="0" y="0"/>
- <xhtml:p><![CDATA[ R2 := True;]]></xhtml:p>
<relPosition x="0" y="0"/>
@@ -848,18 +835,18 @@
<selectionConvergence localId="10" height="1" width="431">
- <position x="70" y="657"/>
+ <position x="70" y="273"/> <relPosition x="0" y="0"/>
<connection refLocalId="13">
- <position x="70" y="657"/>
+ <position x="70" y="273"/> <position x="70" y="244"/>
<relPosition x="431" y="0"/>
<connection refLocalId="14">
- <position x="501" y="657"/>
+ <position x="501" y="273"/> <position x="501" y="250"/>
@@ -868,12 +855,12 @@
<jumpStep localId="12" targetName="Start" height="13" width="12">
- <position x="280" y="701"/>
+ <position x="280" y="317"/> <relPosition x="6" y="0"/>
<connection refLocalId="10">
- <position x="286" y="701"/>
- <position x="286" y="658"/>
+ <position x="286" y="317"/> + <position x="286" y="274"/> @@ -883,7 +870,7 @@
<relPosition x="10" y="0"/>
<connection refLocalId="7">
<position x="70" y="242"/>
- <position x="70" y="221"/>
+ <position x="70" y="215"/> @@ -983,11 +970,6 @@
- <variable name="Reset0">