Split up code, added test
authorBenjamin Braatz <bb@bbraatz.eu>
Tue, 12 Jan 2021 15:05:03 +0000 (16:05 +0100)
committerBenjamin Braatz <bb@bbraatz.eu>
Tue, 12 Jan 2021 15:05:03 +0000 (16:05 +0100)
.gitignore
graphit_controlpi/config.py [new file with mode: 0644]
graphit_controlpi/main.py
graphit_controlpi/websocket.py [new file with mode: 0644]
test.py [new file with mode: 0644]

index c18dd8d83ceed1806b50b0aaa46beb7e335fff13..92afa22fd84964797c24ac50c6a69227c2534b99 100644 (file)
@@ -1 +1,2 @@
 __pycache__/
+venv/
diff --git a/graphit_controlpi/config.py b/graphit_controlpi/config.py
new file mode 100644 (file)
index 0000000..5037d58
--- /dev/null
@@ -0,0 +1,31 @@
+from graphit_pin import PCF8574Output, PCF8574Input, GPIOInputPin
+
+
+async def process_configuration(conf, out_queue):
+
+    def callback_factory(pin_name):
+        settable = pins[pin_name].settable
+
+        def callback(value):
+            out_queue.put_nowait({'event': 'pinstate', 'pin': pin_name,
+                                  'settable': settable, 'value': value,
+                                  'changed': True})
+        return callback
+    pins = {}
+    if 'i/o cards' in conf:
+        for card_conf in conf['i/o cards']:
+            card = None
+            if card_conf['type'] == 'output':
+                card = PCF8574Output(card_conf['address'])
+            elif card_conf['type'] == 'input':
+                card = PCF8574Input(card_conf['address'],
+                                    GPIOInputPin(card_conf['interrupt pin'],
+                                                 up=True))
+            if card is not None:
+                for i in range(8):
+                    pin = card.getPin(i)
+                    pin_names = card_conf['pins'][i]
+                    for pin_name in pin_names:
+                        pins[pin_name] = pin
+                        pin.on('change', callback_factory(pin_name))
+    return pins
index 923ea755084c9381ea1fe4d5142ffd57502e9d45..e0862c696f3b1d6ec562b6baca596bd8e400b6e1 100644 (file)
@@ -1,86 +1,20 @@
 import sys
 import json
-import websockets
-from graphit_pin import PCF8574Output, PCF8574Input, GPIOInputPin
+import asyncio
 
-pins = {}
-out_queue = asyncio.Queue()
+from .config import process_configuration
+from .websocket import setup_websocket
 
 
-def process_configuration(conf):
-
-    def callback_factory(pin_name):
-        settable = pins[pin_name].settable
-
-        def callback(value):
-            out_queue.put({'event': 'pinstate', 'pin': pin_name,
-                           'settable': settable, 'value': value,
-                           'changed': True})
-        return callback
-
-    if 'i/o cards' in conf:
-        for card_conf in conf['i/o cards']:
-            card = None
-            if card_conf['type'] == 'output':
-                card = PCF8574Output(card_conf['address'])
-            elif card_conf['type'] == 'input':
-                card = PCF8574Input(card_conf['address'],
-                                    GPIOInputPin(card_conf['interrupt pin'],
-                                                 up=True))
-            if card is not None:
-                for i in range(8):
-                    pin = card.getPin(i)
-                    pin_names = card_conf['pins'][i]
-                    for pin_name in pin_names:
-                        pins[pin_name] = pin
-                        pin.on('change', callback_factory(pin_name))
-
-
-async def process_command(command):
-    if command['command'] == 'setpin':
-        if command['pin'] in pins:
-            pins[command['pin']].value = command['value']
-    elif command['command'] == 'getpin':
-        if command['pin'] in pins:
-            pin = pins[command['pin']]
-            out_queue.put({'event': 'pinstate', 'pin': command['pin'],
-                           'settable': pin.settable, 'value': pin.value,
-                           'changed': False})
-    elif command['command'] == 'getallpins':
-        for pin_name in pins:
-            pin = pins[pin_name]
-            out_queue.put({'event': 'pinstate', 'pin': pin_name,
-                           'settable': pin.settable, 'value': pin.value,
-                           'changed': False})
-
-
-async def command_handler(websocket, path):
-    async for message in websocket:
-        command = json.loads(message)
-        await process_command(command)
-
-
-async def event_handler(websocket, path):
-    while True:
-        event = await out_queue.get()
-        message = json.dumps(event)
-        await websocket.send(message)
-
+async def setup():
+    pins = {}
+    out_queue = asyncio.Queue()
+    with open(sys.argv[1]) as json_data:
+        conf = json.load(json_data)
+        pins = await process_configuration(conf, out_queue)
+    await setup_websocket(pins, out_queue)
 
-async def handler(websocket, path):
-    command_task = asyncio.ensure_future(command_handler(websocket, path))
-    event_task = asyncio.ensure_future(event_handler(websocket, path))
-    done, pending = await asyncio.wait(
-        [command_task, event_task],
-        return_when=asyncio.FIRST_COMPLETED,
-    )
-    for task in pending:
-        task.cancel()
 
 if __name__ == '__main__':
-    with open(sys.argv[1]) as json_data:
-        conf = json.load(json_data)
-        process_configuration(conf)
-    start_server = websockets.serve(handler, 'localhost', 5678)
-    asyncio.get_event_loop().run_until_complete(start_server)
+    asyncio.get_event_loop().run_until_complete(setup())
     asyncio.get_event_loop().run_forever()
diff --git a/graphit_controlpi/websocket.py b/graphit_controlpi/websocket.py
new file mode 100644 (file)
index 0000000..cad0636
--- /dev/null
@@ -0,0 +1,67 @@
+import functools
+import socket
+import json
+import asyncio
+import websockets
+
+
+async def process_command(command, pins, out_queue):
+    if command['command'] == 'setpin':
+        if command['pin'] in pins and pins[command['pin']].settable:
+            pins[command['pin']].value = command['value']
+    elif command['command'] == 'getpin':
+        if command['pin'] in pins:
+            pin = pins[command['pin']]
+            await out_queue.put({'event': 'pinstate', 'pin': command['pin'],
+                                 'settable': pin.settable, 'value': pin.value,
+                                 'changed': False})
+    elif command['command'] == 'getallpins':
+        for pin_name in pins:
+            pin = pins[pin_name]
+            await out_queue.put({'event': 'pinstate', 'pin': pin_name,
+                                 'settable': pin.settable, 'value': pin.value,
+                                 'changed': False})
+
+
+async def command_handler(websocket, path, pins, out_queue):
+    async for message in websocket:
+        command = json.loads(message)
+        await process_command(command, pins, out_queue)
+
+
+async def event_handler(websocket, path):
+    while True:
+        event = await out_queue.get()
+        message = json.dumps(event)
+        await websocket.send(message)
+        out_queue.task_done()
+
+
+async def handler(pins, out_queue, websocket, path):
+    command_task = asyncio.create_task(command_handler(websocket, path,
+                                                       pins, out_queue))
+    event_task = asyncio.create_task(event_handler(websocket, path))
+    done, pending = await asyncio.wait(
+        [command_task, event_task],
+        return_when=asyncio.FIRST_COMPLETED,
+    )
+    for task in pending:
+        task.cancel()
+
+
+def get_ip():
+    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    try:
+        # doesn't even have to be reachable
+        s.connect(('10.255.255.255', 1))
+        ip = s.getsockname()[0]
+    except Exception:
+        ip = '127.0.0.1'
+    finally:
+        s.close()
+    return ip
+
+
+async def setup_websocket(pins, out_queue):
+    parameterised_handler = functools.partial(handler, pins, out_queue)
+    await websockets.serve(parameterised_handler, get_ip(), 80)
diff --git a/test.py b/test.py
new file mode 100644 (file)
index 0000000..f68f638
--- /dev/null
+++ b/test.py
@@ -0,0 +1,40 @@
+import sys
+import json
+import asyncio
+import websockets
+
+
+async def test_commands(websocket):
+    commands = [{'command': 'getallpins'},
+                {'command': 'setpin', 'pin': 'A2-8', 'value': True},
+                {'command': 'setpin', 'pin': 'A2-8', 'value': True},
+                {'command': 'setpin', 'pin': 'A2-8', 'value': False},
+                {'command': 'getpin', 'pin': 'T2-16'}]
+    for command in commands:
+        message = json.dumps(command)
+        await websocket.send(message)
+        print(f"Sent Command: {message}")
+
+
+async def receive_events(websocket):
+    async for message in websocket:
+        print(f"Recvd. Event: {message}")
+
+
+async def main(hostname):
+    async with websockets.connect(f"ws://{hostname}:80") as websocket:
+        command_task = asyncio.create_task(test_commands(websocket))
+        event_task = asyncio.create_task(receive_events(websocket))
+        done, pending = await asyncio.wait(
+            [command_task, event_task],
+            return_when=asyncio.BOTH_COMPLETED,
+        )
+        for task in pending:
+            task.cancel()
+
+
+if __name__ == '__main__':
+    try:
+        asyncio.get_event_loop().run_until_complete(main(sys.argv[1]))
+    except KeyboardInterrupt:
+        pass