--- a/ProjectController.py Mon Nov 11 15:22:44 2024 +0100
+++ b/ProjectController.py Fri Nov 22 16:31:51 2024 +0100
@@ -46,7 +46,7 @@
import features
import connectors
-import util.paths as paths
+import util.paths as pathutils
from util.misc import CheckPathPerm, GetClassImporter
from util.MiniTextControler import MiniTextControler
from util.ProcessLogger import ProcessLogger
@@ -65,7 +65,6 @@
from ConfigTreeNode import ConfigTreeNode, XSDSchemaErrorMessage
from POULibrary import UserAddressedException
-base_folder = paths.AbsParentDir(__file__)
MATIEC_ERROR_MODEL = re.compile(
r".*\.st:(\d+)-(\d+)\.\.(\d+)-(\d+): (?:error)|(?:warning) : (.*)$")
@@ -116,7 +115,7 @@
def findCmd(self):
cmd = "iec2c" + (".exe" if os.name == 'nt' else "")
paths = [
- os.path.join(base_folder, "matiec")
+ pathutils.ThirdPartyPath("matiec")
]
path = self.findObject(
paths, lambda p: os.path.isfile(os.path.join(p, cmd)))
@@ -129,7 +128,7 @@
def findLibPath(self):
paths = [
- os.path.join(base_folder, "matiec", "lib"),
+ pathutils.ThirdPartyPath("matiec", "lib"),
"/usr/lib/matiec"
]
path = self.findObject(
--- a/connectors/ERPC/__init__.py Mon Nov 11 15:22:44 2024 +0100
+++ b/connectors/ERPC/__init__.py Fri Nov 22 16:31:51 2024 +0100
@@ -22,6 +22,7 @@
import PSKManagement as PSK
from connectors.ERPC.PSK_Adapter import SSLPSKClientTransport
from connectors.ConnectorBase import ConnectorBase
+from connectors.ERPC_URI import per_scheme_model
enum_to_PLCstatus = dict(map(lambda t:(t[1],t[0]),getmembers(PLCstatus_enum, lambda x:type(x)==int)))
@@ -74,6 +75,36 @@
for idx, force in orders],)
}
+def rpc_wrapper(method_name, confnodesroot):
+ client_method = getattr(BeremizPLCObjectServiceClient, method_name)
+ return_wrapper = ReturnWrappers.get(
+ method_name,
+ lambda client_method, obj, args_wrapper, *args: client_method(obj, *args_wrapper(*args)))
+ args_wrapper = ArgsWrappers.get(method_name, lambda *x:x)
+
+ def exception_wrapper(self, *args):
+ try:
+ return return_wrapper(client_method, self, args_wrapper, *args)
+ except erpc.transport.ConnectionClosed as e:
+ confnodesroot._SetConnector(None)
+ confnodesroot.logger.write_error(_("Connection lost!\n"))
+ except erpc.codec.CodecError as e:
+ confnodesroot.logger.write_warning(_("ERPC codec error: %s\n") % e)
+ except erpc.client.RequestError as e:
+ confnodesroot.logger.write_error(_("ERPC request error: %s\n") % e)
+ except MissingCallException as e:
+ confnodesroot.logger.write_warning(_("Remote call not supported: %s\n") % e)
+ except Exception as e:
+ errmess = _("Exception calling remote PLC object fucntion %s:\n") % method_name \
+ + traceback.format_exc()
+ confnodesroot.logger.write_error(errmess + "\n")
+ confnodesroot._SetConnector(None)
+
+ return self.PLCObjDefaults.get(method_name)
+ return exception_wrapper
+
+
+
def ERPC_connector_factory(uri, confnodesroot):
"""
returns the ERPC connector
@@ -84,92 +115,71 @@
# ERPC:///dev/ttyXX:baudrate or ERPC://:COM4:baudrate
try:
- _scheme, location = uri.split("://",1)
- locator, *IDhash = location.split('#',1)
- x = re.match(r'(?P<host>[^\s:]+):?(?P<port>\d+)?', locator)
- host = x.group('host')
- port = x.group('port')
- if port:
- port = int(port)
- else:
- # default port depends on security
- port = 4000 if IDhash else 3000
-
- if not IDhash and _scheme=="ERPCS":
- confnodesroot.logger.write_error(
- f'Invalid URI "{uri}": ERPCS requires PLC ID after "#"\n')
- return None
- elif IDhash and _scheme!="ERPCS":
- confnodesroot.logger.write_error(
- f'URI "{uri}": Non-encrypted ERPC does not take a PLC ID after "#"\n')
- return None
-
+ scheme, location = uri.split("://",1)
+ _model, _useID, parser, _builder = per_scheme_model[scheme]
+ location_data = parser(location)
except Exception as e:
confnodesroot.logger.write_error(
'Malformed URI "%s": %s\n' % (uri, str(e)))
return None
- def rpc_wrapper(method_name):
- client_method = getattr(BeremizPLCObjectServiceClient, method_name)
- return_wrapper = ReturnWrappers.get(
- method_name,
- lambda client_method, obj, args_wrapper, *args: client_method(obj, *args_wrapper(*args)))
- args_wrapper = ArgsWrappers.get(method_name, lambda *x:x)
-
- def exception_wrapper(self, *args):
- try:
- return return_wrapper(client_method, self, args_wrapper, *args)
- except erpc.transport.ConnectionClosed as e:
- confnodesroot._SetConnector(None)
- confnodesroot.logger.write_error(_("Connection lost!\n"))
- except erpc.codec.CodecError as e:
- confnodesroot.logger.write_warning(_("ERPC codec error: %s\n") % e)
- except erpc.client.RequestError as e:
- confnodesroot.logger.write_error(_("ERPC request error: %s\n") % e)
- except MissingCallException as e:
- confnodesroot.logger.write_warning(_("Remote call not supported: %s\n") % e)
- except Exception as e:
- errmess = _("Exception calling remote PLC object fucntion %s:\n") % method_name \
- + traceback.format_exc()
- confnodesroot.logger.write_error(errmess + "\n")
- confnodesroot._SetConnector(None)
-
- return self.PLCObjDefaults.get(method_name)
- return exception_wrapper
-
-
PLCObjectERPCProxy = type(
"PLCObjectERPCProxy",
(ConnectorBase, BeremizPLCObjectServiceClient),
- {name: rpc_wrapper(name)
+ {name: rpc_wrapper(name, confnodesroot)
for name,_func in getmembers(IBeremizPLCObjectService, isfunction)})
- try:
- if IDhash:
- ID = IDhash[0]
- # load PSK from project
- secpath = os.path.join(confnodesroot.ProjectPath, 'psk', ID + '.secret')
- if not os.path.exists(secpath):
+ if scheme in ["ERPCS", "ERPC"]:
+ if scheme=="ERPCS":
+ ID = location_data["ID"]
+ if not ID:
confnodesroot.logger.write_error(
- 'Error: Pre-Shared-Key Secret in %s is missing!\n' % secpath)
+ f'Invalid URI "{uri}": ERPCS requires PLC ID after "#"\n')
return None
- secret = open(secpath).read().partition(':')[2].rstrip('\n\r').encode()
- transport = SSLPSKClientTransport(host, port, (secret, ID.encode()))
+ default_port = 4000
else:
- # TODO if serial URI then
- # transport = erpc.transport.SerialTransport(device, baudrate)
+ ID = None
+ if "#" in location:
+ confnodesroot.logger.write_error(
+ f'URI "{uri}": Non-encrypted ERPC does not take a PLC ID after "#"\n')
+ return None
+ default_port = 3000
- transport = erpc.transport.TCPTransport(host, port, False)
+ host = location_data["host"]
+ port = location_data["port"]
+ port = int(port) if port else default_port
- clientManager = erpc.client.ClientManager(transport, erpc.basic_codec.BasicCodec)
- client = PLCObjectERPCProxy(clientManager)
+ try:
+ if ID:
+ # load PSK from project
+ secpath = os.path.join(confnodesroot.ProjectPath, 'psk', ID + '.secret')
+ if not os.path.exists(secpath):
+ confnodesroot.logger.write_error(
+ 'Error: Pre-Shared-Key Secret in %s is missing!\n' % secpath)
+ return None
+ secret = open(secpath).read().partition(':')[2].rstrip('\n\r').encode()
+ transport = SSLPSKClientTransport(host, port, (secret, ID.encode())) # type: ignore
+ else:
- except Exception as e:
+ transport = erpc.transport.TCPTransport(host, port, False)
+
+ clientManager = erpc.client.ClientManager(transport, erpc.basic_codec.BasicCodec)
+ client = PLCObjectERPCProxy(clientManager)
+
+ except Exception as e:
+ confnodesroot.logger.write_error(
+ _("Connection to {loc} failed with exception {ex}\n").format(
+ loc=uri, ex=str(e)))
+ return None
+
+ else:
+ # TODO if serial URI then
+ # transport = erpc.transport.SerialTransport(device, baudrate)
+
confnodesroot.logger.write_error(
- _("Connection to {loc} failed with exception {ex}\n").format(
- loc=locator, ex=str(e)))
+ _("Unknown scheme {scheme} in URI {uri}\n").format(
+ scheme=scheme, uri=uri))
return None
-
# Check connection is effective.
IDPSK = client.GetPLCID()
if IDPSK:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/connectors/ERPC_URI.py Fri Nov 22 16:31:51 2024 +0100
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# See COPYING file for copyrights details.
+
+from itertools import repeat, islice, chain
+
+## URI parsing functions
+
+def split_as_dict(s, sep, labels):
+ return dict(zip(labels, islice(chain(s.split(sep), repeat("")), len(labels))))
+
+def parse_tcp(loc):
+ return split_as_dict(loc, ":", ["host", "port"])
+
+def parse_sslpsk(loc):
+ locals().update(**split_as_dict(loc, "#", ["hostport", "ID"]))
+ return dict(**parse_tcp(hostport), ID=ID) # type: ignore
+
+def parse_serial(loc):
+ return split_as_dict(loc, "@", ["device", "baudrate"])
+
+def parse_usb(loc):
+ return split_as_dict(loc, ":", ["VID", "PID", "serialnumber"])
+
+## URI building functions
+
+def build_tcp(fields):
+ if fields['port']:
+ return "{host}:{port}".format(**fields)
+ return fields['host']
+
+def build_sslpsk(fields):
+ return "{hostport}#{ID}".format(hostport=build_tcp(fields), **fields)
+
+def build_serial(fields):
+ if fields['baudrate']:
+ return "{device}@{baudrate}".format(**fields)
+ return fields['device']
+
+def build_usb(fields):
+ if fields['serialnumber']:
+ return "{VID}:{PID}:{serialnumber}".format(**fields)
+ if fields['PID']:
+ return "{VID}:{PID}".format(**fields)
+ return fields['VID']
+
+## Dialog fields definition
+
+model_tcp = [('host', _("Host:")),
+ ('port', _("Port:"))]
+
+model_serial = [('device', _("Device:")),
+ ('baudrate', _("Baud rate:"))]
+
+model_usb = [('VID', _("Vendor ID:")),
+ ('PID', _("Product ID:")),
+ ('serialnumber', _("Serial number:"))]
+
+
+## Schemes description
+
+schemes_desc = [
+# ( scheme name , data model , use ID, parser , builder )
+ ("LOCAL", [], False, lambda x: {}, lambda x: ""),
+ ("ERPC", model_tcp, False, parse_tcp, build_tcp ),
+ ("ERPCS", model_tcp, True, parse_sslpsk, build_sslpsk),
+ ("ERPC-SERIAL", model_serial, False, parse_serial, build_serial),
+ ("ERPC-USB", model_usb, False, parse_usb, build_usb )]
+
+per_scheme_model = {sch: desc for sch, *desc in schemes_desc}
+
--- a/connectors/ERPC_dialog.py Mon Nov 11 15:22:44 2024 +0100
+++ b/connectors/ERPC_dialog.py Fri Nov 22 16:31:51 2024 +0100
@@ -3,46 +3,24 @@
# See COPYING file for copyrights details.
+from connectors.ERPC_URI import schemes_desc, per_scheme_model
+from connectors.SchemeEditor import SchemeEditor
+
+## Scheme list for the dialog's combobox
+
+Schemes = list(zip(*schemes_desc))[0]
-from itertools import repeat, islice, chain
-
-from connectors.SchemeEditor import SchemeEditor
-
-
-model = [('host', _("Host:")),
- ('port', _("Port:"))]
-
-# (scheme, model, secure)
-models = [("LOCAL", [], False), ("ERPC", model, False), ("ERPCS", model, True)]
-
-Schemes = list(zip(*models))[0]
-
-_PerSchemeConf = {sch: (mod, sec) for sch, mod, sec in models}
-
+## Specialized SchemeEditor panel for ERPC
class ERPC_dialog(SchemeEditor):
def __init__(self, scheme, *args, **kwargs):
- # ID selector is enabled only on ERPC (secure)
- self.model, self.EnableIDSelector = _PerSchemeConf[scheme]
+ self.model, self.EnableIDSelector, self.parser, self.builder = per_scheme_model[scheme]
SchemeEditor.__init__(self, scheme, *args, **kwargs)
- # pylint: disable=unused-variable
def SetLoc(self, loc):
- hostport, ID = list(islice(chain(loc.split("#"), repeat("")), 2))
- host, port = list(islice(chain(hostport.split(":"), repeat("")), 2))
- self.SetFields(locals())
+ self.SetFields(self.parser(loc))
def GetLoc(self):
- if self.model:
- fields = self.GetFields()
- template = "{host}"
- if fields['port']:
- template += ":{port}"
- if self.EnableIDSelector:
- if fields['ID']:
- template += "#{ID}"
-
- return template.format(**fields)
- return ''
+ return self.builder(self.GetFields())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/connectors/ZeroConfListener.py Fri Nov 22 16:31:51 2024 +0100
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# See COPYING file for copyrights details.
+
+import weakref
+from zeroconf import ServiceBrowser, Zeroconf, get_all_addresses
+import threading
+
+service_type = '_Beremiz._tcp.local.'
+
+class ZeroConfListenerClass:
+ def __init__(self, dialog):
+ self.dialog = weakref.ref(dialog)
+
+ self.IfacesMonitorState = None
+ self.IfacesMonitorTimer = None
+ self.Browser = None
+ self.ZeroConfInstance = None
+ self.PublishedServices = set()
+
+ self.start()
+
+ def __del__(self):
+ self.stop()
+
+ def start(self):
+ self.ZeroConfInstance = Zeroconf()
+ self.Browser = ServiceBrowser(self.ZeroConfInstance, service_type, self)
+ # Start the ifaces_monitor timer thread
+ self.IfacesMonitorTimer = threading.Timer(1.0, self.ifaces_monitor)
+ self.IfacesMonitorTimer.start()
+
+ def stop(self):
+ if self.IfacesMonitorTimer is not None:
+ self.IfacesMonitorTimer.cancel()
+ self.IfacesMonitorTimer = None
+
+ if self.Browser is not None:
+ self.Browser.cancel()
+ self.Browser = None
+
+ if self.ZeroConfInstance is not None:
+ self.ZeroConfInstance.close()
+ self.ZeroConfInstance = None
+
+ def update_service(self, zeroconf, _type, name):
+ self.remove_service(zeroconf, _type, name)
+ self.add_service(zeroconf, _type, name)
+
+ def add_service(self, zeroconf, _type, name):
+ dialog = self.dialog()
+ if not dialog:
+ return
+
+ info = self.ZeroConfInstance.get_service_info(_type, name)
+ if info is None:
+ return
+
+ typename = info.properties.get(b"protocol", None).decode()
+ ip = str(info.parsed_addresses()[0])
+ port = info.port
+ dialog.addService(typename, ip, port, name)
+ self.PublishedServices.add(name)
+
+ def remove_service(self, zeroconf, _type, name):
+ dialog = self.dialog()
+ if not dialog:
+ return
+
+ if name in self.PublishedServices:
+ dialog.removeService(name)
+ self.PublishedServices.discard(name)
+
+ def ifaces_monitor(self):
+ dialog = self.dialog()
+ if not dialog:
+ return
+
+ NewState = get_all_addresses()
+ OldState = self.IfacesMonitorState
+ self.IfacesMonitorState = NewState
+ do_restart = False
+
+ if OldState is not None:
+ # detect if a new address appeared
+ for addr in NewState:
+ if addr not in OldState:
+ do_restart = True
+ break
+ else:
+ OldState.remove(addr)
+ # detect if an address disappeared
+ if len(OldState) > 0:
+ do_restart = True
+
+
+ if do_restart:
+ self.stop()
+
+ while self.PublishedServices:
+ dialog.removeService(self.PublishedServices.pop())
+
+ self.start()
+ else:
+ # Restart the ifaces_monitor timer thread
+ self.IfacesMonitorTimer = threading.Timer(1.0, self.ifaces_monitor)
+ self.IfacesMonitorTimer.start()
--- a/connectors/__init__.py Mon Nov 11 15:22:44 2024 +0100
+++ b/connectors/__init__.py Fri Nov 22 16:31:51 2024 +0100
@@ -83,6 +83,8 @@
scheme = _scheme
elif _scheme[-1] == 'S' and _scheme[:-1] in connectors:
scheme = _scheme[:-1]
+ elif _scheme.split("-")[0] in connectors:
+ scheme = _scheme.split("-")[0]
else:
return None
--- a/controls/DiscoveryPanel.py Mon Nov 11 15:22:44 2024 +0100
+++ b/controls/DiscoveryPanel.py Fri Nov 22 16:31:51 2024 +0100
@@ -24,57 +24,9 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-import socket
-import weakref
import wx
import wx.lib.mixins.listctrl as listmix
-from zeroconf import ServiceBrowser, Zeroconf, get_all_addresses
-
-service_type = '_Beremiz._tcp.local.'
-
-class ZeroConfListenerClass:
- def __init__(self, dialog):
- self.dialog = weakref.ref(dialog)
- self.ZeroConfInstance = Zeroconf()
- self.Browser = ServiceBrowser(self.ZeroConfInstance, service_type, self)
-
- def clean(self):
- if self.Browser is not None:
- self.Browser.cancel()
- self.Browser = None
- if self.ZeroConfInstance is not None:
- self.ZeroConfInstance.close()
- self.ZeroConfInstance = None
-
- def __del__(self):
- self.clean()
-
- def update_service(self, zeroconf, _type, name):
- self.remove_service(zeroconf, _type, name)
- self.add_service(zeroconf, _type, name)
-
- def add_service(self, zeroconf, _type, name):
- info = self.ZeroConfInstance.get_service_info(_type, name)
- if info is None:
- return
- typename = info.properties.get(b"protocol", None).decode()
- ip = str(info.parsed_addresses()[0])
- port = info.port
-
- wx.CallAfter(self._add_service, typename, ip, port, name)
-
- def _add_service(self, typename, ip, port, name):
- dialog = self.dialog()
- if not dialog: return
- dialog._addService(typename, ip, port, name)
-
- def remove_service(self, zeroconf, _type, name):
- wx.CallAfter(self._remove_service, name)
-
- def _remove_service(self, name):
- dialog = self.dialog()
- if not dialog: return
- dialog._removeService(name)
+from connectors.ZeroConfListener import ZeroConfListenerClass
class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
def __init__(self, parent, name, pos=wx.DefaultPosition,
@@ -82,7 +34,6 @@
wx.ListCtrl.__init__(self, parent, wx.ID_ANY, pos, size, style, name=name)
listmix.ListCtrlAutoWidthMixin.__init__(self)
-
class DiscoveryPanel(wx.Panel, listmix.ColumnSorterMixin):
def _init_coll_MainSizer_Items(self, parent):
@@ -118,15 +69,11 @@
self.ServicesList = AutoWidthListCtrl(
name='ServicesList', parent=self, pos=wx.Point(0, 0), size=wx.Size(0, 0),
style=wx.LC_REPORT | wx.LC_EDIT_LABELS | wx.LC_SORT_ASCENDING | wx.LC_SINGLE_SEL)
- self.ServicesList.InsertColumn(0, _('NAME'))
- self.ServicesList.InsertColumn(1, _('TYPE'))
- self.ServicesList.InsertColumn(2, _('IP'))
- self.ServicesList.InsertColumn(3, _('PORT'))
- self.ServicesList.SetColumnWidth(0, 150)
- self.ServicesList.SetColumnWidth(1, 150)
- self.ServicesList.SetColumnWidth(2, 150)
- self.ServicesList.SetColumnWidth(3, 150)
+ for col, (label, width) in enumerate([
+ (_('NAME'), 150), (_('TYPE'), 150), (_('IP'), 150), (_('PORT'), 150)]):
+ self.ServicesList.InsertColumn(col, label)
self.ServicesList.SetInitialSize(wx.Size(-1, 300))
+
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.ServicesList)
self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, self.ServicesList)
@@ -156,46 +103,27 @@
self._init_ctrls(parent)
+ self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
+
self.itemDataMap = {}
self.nextItemId = 0
self.URI = None
+ self.LatestSelection = None
+
+ self.ZeroConfListener = None
+
self.RefreshList()
- self.LatestSelection = None
-
- self.IfacesMonitorState = None
- self.IfacesMonitorTimer = wx.Timer(self)
- self.IfacesMonitorTimer.Start(2000)
- self.ZeroConfListener = None
- self.Bind(wx.EVT_TIMER, self.IfacesMonitor, self.IfacesMonitorTimer)
+
def _cleanup(self):
- if self.IfacesMonitorTimer is not None:
- self.IfacesMonitorTimer.Stop()
- self.IfacesMonitorTimer = None
if self.ZeroConfListener is not None:
- self.ZeroConfListener.clean()
+ self.ZeroConfListener.stop()
self.ZeroConfListener = None
- def __del__(self):
+ def OnDestroy(self, event):
self._cleanup()
-
- def Destroy(self):
- self._cleanup()
- wx.Panel.Destroy(self)
-
- def IfacesMonitor(self, event):
- NewState = get_all_addresses()
-
- if self.IfacesMonitorState != NewState:
- if self.IfacesMonitorState is not None:
- # refresh only if a new address appeared
- for addr in NewState:
- if addr not in self.IfacesMonitorState:
- self.RefreshList()
- break
- self.IfacesMonitorState = NewState
event.Skip()
def RefreshList(self):
@@ -246,6 +174,9 @@
# return str("MDNS://%s" % svcname)
return None
+ def removeService(self, name):
+ wx.CallAfter(self._removeService, name)
+
def _removeService(self, name):
'''
called when a service with the desired type goes offline.
@@ -263,6 +194,8 @@
self.ServicesList.DeleteItem(idx)
break
+ def addService(self, typename, ip, port, name):
+ wx.CallAfter(self._addService, typename, ip, port, name)
def _addService(self, typename, ip, port, name):
'''
--- a/runtime/ServicePublisher.py Mon Nov 11 15:22:44 2024 +0100
+++ b/runtime/ServicePublisher.py Fri Nov 22 16:31:51 2024 +0100
@@ -34,7 +34,6 @@
class ServicePublisher(object):
def __init__(self, protocol):
- # type: fully qualified service type name
self.serviceproperties = {
'description': 'Beremiz remote PLC',
'protocol': protocol
@@ -50,12 +49,12 @@
def RegisterService(self, name, ip, port):
try:
self._RegisterService(name, ip, port)
- except Exception:
+ except Exception as e:
+ print(f"Failed to register service ({str(e)}), retrying in 2 seconds")
self.retrytimer = threading.Timer(2, self.RegisterService, [name, ip, port])
self.retrytimer.start()
def _RegisterService(self, name, ip, port):
- # name: fully qualified service name
self.service_name = '%s.%s' % (name, service_type)
self.name = name
self.port = port
@@ -72,12 +71,14 @@
print("MDNS brodcasted service address :" + ip)
self.ip_32b = socket.inet_aton(ip)
- self.server.register_service(
- zeroconf.ServiceInfo(service_type,
- self.service_name,
- self.ip_32b,
- self.port,
- properties=self.serviceproperties))
+ self.service_info = zeroconf.ServiceInfo(
+ service_type,
+ self.service_name,
+ self.port,
+ addresses=[self.ip_32b],
+ properties=self.serviceproperties)
+
+ self.server.register_service(self.service_info)
self.retrytimer = None
def UnRegisterService(self):
@@ -85,12 +86,7 @@
self.retrytimer.cancel()
if self.server is not None:
- self.server.unregister_service(
- zeroconf.ServiceInfo(service_type,
- self.service_name,
- self.ip_32b,
- self.port,
- properties=self.serviceproperties))
+ self.server.unregister_service(self.service_info)
self.server.close()
self.server = None
--- a/runtime/eRPCServer.py Mon Nov 11 15:22:44 2024 +0100
+++ b/runtime/eRPCServer.py Fri Nov 22 16:31:51 2024 +0100
@@ -87,7 +87,7 @@
self.transport = None
self.servicename = servicename
self.ip_addr = ip_addr
- self.port = port
+ self.port = int(port)
self.servicepublisher = None
def _to_be_published(self):
@@ -120,7 +120,7 @@
# transport = erpc.transport.SerialTransport(device, baudrate)
# initialize TCP transport layer
- self.transport = erpc.transport.TCPTransport(self.ip_addr, int(self.port), True)
+ self.transport = erpc.transport.TCPTransport(self.ip_addr, self.port, True)
self.server = erpc.simple_server.SimpleServer(self.transport, erpc.basic_codec.BasicCodec)
self.server.add_service(service)
--- a/util/paths.py Mon Nov 11 15:22:44 2024 +0100
+++ b/util/paths.py Fri Nov 22 16:31:51 2024 +0100
@@ -50,6 +50,10 @@
"""
Return folder where to find sibling projects like Modbus, CanFestival, BACnet
"""
+ env_name = name.upper() + "_PATH"
+ if env_name in os.environ:
+ return os.path.join(os.environ[env_name], *suffixes)
+
return os.path.join(AbsParentDir(__file__, 2), name, *suffixes)
def Bpath(*names):