From 4a1ec06871370d6a771246a4f819ae322b8609f6 Mon Sep 17 00:00:00 2001 From: Benjamin Braatz Date: Tue, 12 Jan 2021 16:05:03 +0100 Subject: [PATCH] Split up code, added test --- .gitignore | 1 + graphit_controlpi/config.py | 31 ++++++++++++ graphit_controlpi/main.py | 88 +++++----------------------------- graphit_controlpi/websocket.py | 67 ++++++++++++++++++++++++++ test.py | 40 ++++++++++++++++ 5 files changed, 150 insertions(+), 77 deletions(-) create mode 100644 graphit_controlpi/config.py create mode 100644 graphit_controlpi/websocket.py create mode 100644 test.py diff --git a/.gitignore b/.gitignore index c18dd8d..92afa22 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ __pycache__/ +venv/ diff --git a/graphit_controlpi/config.py b/graphit_controlpi/config.py new file mode 100644 index 0000000..5037d58 --- /dev/null +++ b/graphit_controlpi/config.py @@ -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 diff --git a/graphit_controlpi/main.py b/graphit_controlpi/main.py index 923ea75..e0862c6 100644 --- a/graphit_controlpi/main.py +++ b/graphit_controlpi/main.py @@ -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 index 0000000..cad0636 --- /dev/null +++ b/graphit_controlpi/websocket.py @@ -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 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 -- 2.34.1