Client name optional and MAC address during connection.
authorBenjamin Braatz <bb@bbraatz.eu>
Tue, 27 Jul 2021 01:45:00 +0000 (03:45 +0200)
committerBenjamin Braatz <bb@bbraatz.eu>
Tue, 27 Jul 2021 01:45:00 +0000 (03:45 +0200)
conf-machine.json
controlpi_plugins/wsclient.py

index 26245eded0ac2701cc08262dce4c1085e755bf4b..cba238dd4e12083d4bdcc7df42569eacb3f44fa1 100644 (file)
     "Controller": {
         "plugin": "WSClient",
         "url": "ws://localhost:8080",
+        "interface": "enp0s25",
         "client": "Machine",
         "up filter": [ { "sender": { "const": "Engine" } } ],
         "down filter": [ { "sender": { "const": "Engine Clearance" } } ]
     },
     "Engine Clearance": {
         "plugin": "Alias",
-        "from": { "sender": { "const": "Controller" },
-                  "original sender": { "const": "Controller/Engine Clearance" } },
+        "from": { "original sender": { "const": "Controller/Engine Clearance" } },
         "to": { }
     },
     "Engine Switch": {
index e14be7b5aa6adfbdbc920628b78426b3cf3005cd..5155e021a55f28080d32f941bb1b034c9fa349dd 100644 (file)
@@ -5,7 +5,10 @@
 TODO: documentation, doctests
 """
 import asyncio
+import fcntl
 import json
+import socket
+import struct
 from websockets import ConnectionClosed, connect
 from controlpi import BasePlugin, Message, MessageTemplate
 
@@ -18,53 +21,58 @@ class WSClient(BasePlugin):
 
     CONF_SCHEMA = {'properties':
                    {'url': {'type': 'string'},
+                    'interface': {'type': 'string'},
                     'client': {'type': 'string'},
                     'up filter': {'type': 'array',
                                   'items': {'type': 'object'}},
                     'down filter': {'type': 'array',
                                     'items': {'type': 'object'}}},
-                   'required': ['url', 'client', 'up filter', 'down filter']}
+                   'required': ['url', 'up filter', 'down filter']}
 
     async def _receive(self, message: Message) -> None:
         if not self._websocket:
             return
         assert isinstance(message['sender'], str)
         prefix = f"{self.name}/"
-        original_sender = self.conf['client']
+        original_sender = self._client
         if 'original sender' in message:
             if message['original sender'].startswith(prefix):
                 return
             original_sender += f"/{message['original sender']}"
-        else:
+        elif message['sender'] != '':
             original_sender += f"/{message['sender']}"
         message['original sender'] = original_sender
         del message['sender']
         if 'target' in message:
             assert isinstance(message['target'], str)
             target = message['target']
-            if target.startswith(prefix):
+            if target == '':
+                target = self._client
+            elif target.startswith(prefix):
                 target = target[len(prefix):]
             else:
-                target = f"{self.conf['client']}/{target}"
+                target = f"{self._client}/{target}"
             message['target'] = target
         json_message = json.dumps(message)
         await self._websocket.send(json_message)
 
     async def _send(self, json_message: str) -> None:
         message = json.loads(json_message)
-        prefix = f"{self.conf['client']}/"
+        prefix = f"{self._client}/"
         original_sender = self.name
         if 'original sender' in message:
             if message['original sender'].startswith(prefix):
                 return
             original_sender += f"/{message['original sender']}"
-        else:
+        elif message['sender'] != '':
             original_sender += f"/{message['sender']}"
         message['original sender'] = original_sender
         message['sender'] = self.name
         if 'target' in message:
             target = message['target']
-            if target.startswith(prefix):
+            if target == '':
+                target = self.name
+            elif target.startswith(prefix):
                 target = target[len(prefix):]
             else:
                 target = f"{self.name}/{target}"
@@ -74,6 +82,16 @@ class WSClient(BasePlugin):
     def process_conf(self) -> None:
         """Register plugin as bus client."""
         self._websocket = None
+        if 'client' in self.conf:
+            self._client = self.conf['client']
+        if 'interface' in self.conf:
+            # Get own MAC address:
+            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+            info = fcntl.ioctl(sock.fileno(), 0x8927,
+                               struct.pack('256s',
+                                           bytes(self.conf['interface'],
+                                                 'utf-8')[:15]))
+            self._mac = ':'.join('%02x' % b for b in info[18:24])
         sends = []
         sends.append(MessageTemplate({'event':
                                       {'const': 'registered'}}))
@@ -90,7 +108,7 @@ class WSClient(BasePlugin):
                         'const' in send_template['original sender']):
                     const = send_template['original sender']['const']
                     original_sender += f"/{const}"
-                else:
+                elif send_template['sender']['const'] != '':
                     const = send_template['sender']['const']
                     original_sender += f"/{const}"
                 send_template['original sender'] = {'const': original_sender}
@@ -98,9 +116,14 @@ class WSClient(BasePlugin):
             if ('target' in send_template and
                     'const' in send_template['target']):
                 target = send_template['target']['const']
-                prefix = f"{self.conf['client']}/"
-                if target.startswith(prefix):
-                    target = target[len(prefix):]
+                if target == '':
+                    target = self.name
+                elif 'client' in self.conf:
+                    prefix = f"{self.conf['client']}/"
+                    if target.startswith(prefix):
+                        target = target[len(prefix):]
+                    else:
+                        target = f"{self.name}/{target}"
                 else:
                     target = f"{self.name}/{target}"
                 send_template['target'] = {'const': target}
@@ -110,40 +133,49 @@ class WSClient(BasePlugin):
 
     async def run(self) -> None:
         """Connect to wsserver and process messages from it."""
-        up_filter = []
-        for template in self.conf['up filter']:
-            up_template = MessageTemplate(template)
-            if ('sender' in up_template and
-                    'const' in up_template['sender']):
-                original_sender = self.conf['client']
-                if ('original sender' in up_template and
-                        'const' in up_template['original sender']):
-                    const = up_template['original sender']['const']
-                    original_sender += f"/{const}"
-                else:
-                    const = up_template['sender']['const']
-                    original_sender += f"/{const}"
-                up_template['original sender'] = {'const': original_sender}
-                del up_template['sender']
-            if ('target' in up_template and
-                    'const' in up_template['target']):
-                target = up_template['target']['const']
-                prefix = f"{self.name}/"
-                if target.startswith(prefix):
-                    target = target[len(prefix):]
-                else:
-                    target = f"{self.conf['client']}/{target}"
-                up_template['target'] = {'const': target}
-            up_filter.append(up_template)
-        conf_command = {'command': 'configure websocket',
-                        'target': '',
-                        'name': self.conf['client'],
-                        'up filter': up_filter,
-                        'down filter': self.conf['down filter']}
-        json_command = json.dumps(conf_command)
         while True:
             try:
                 async with connect(self.conf['url']) as websocket:
+                    conf_command = {'command': 'configure websocket',
+                                    'target': ''}
+                    if 'client' in self.conf:
+                        conf_command['name'] = self._client
+                    else:
+                        address = websocket.local_address[0]
+                        port = websocket.local_address[1]
+                        self._client = f"{address}:{port}"
+                    if 'interface' in self.conf:
+                        conf_command['mac'] = self._mac
+                    up_filter = []
+                    for template in self.conf['up filter']:
+                        up_template = MessageTemplate(template)
+                        if ('sender' in up_template and
+                                'const' in up_template['sender']):
+                            original_sender = self._client
+                            if ('original sender' in up_template and
+                                    'const' in up_template['original sender']):
+                                const = up_template['original sender']['const']
+                                original_sender += f"/{const}"
+                            elif up_template['sender']['const'] != '':
+                                const = up_template['sender']['const']
+                                original_sender += f"/{const}"
+                            up_template['original sender'] = {'const': original_sender}
+                            del up_template['sender']
+                        if ('target' in up_template and
+                                'const' in up_template['target']):
+                            target = up_template['target']['const']
+                            prefix = f"{self.name}/"
+                            if target == '':
+                                target = self._client
+                            elif target.startswith(prefix):
+                                target = target[len(prefix):]
+                            else:
+                                target = f"{self._client}/{target}"
+                            up_template['target'] = {'const': target}
+                        up_filter.append(up_template)
+                    conf_command['up filter'] = up_filter
+                    conf_command['down filter'] = self.conf['down filter']
+                    json_command = json.dumps(conf_command)
                     await websocket.send(json_command)
                     await self.bus.send(Message(self.name,
                                                 {'event':