From 95810921aed04f994b2c0f2bfc899be1238cd3d7 Mon Sep 17 00:00:00 2001 From: Davide De Tommaso Date: Wed, 27 Oct 2021 11:13:10 +0200 Subject: [PATCH] Fixed waitEvent with timeout --- apps/.env | 2 +- apps/client_server_timeout.yml | 29 +++++++++++++++ apps/python-examples/client_timeout.py | 7 ++++ hidman/__init__.py | 2 +- hidman/core.py | 51 +++++++++++++++++--------- 5 files changed, 72 insertions(+), 19 deletions(-) create mode 100644 apps/client_server_timeout.yml create mode 100644 apps/python-examples/client_timeout.py diff --git a/apps/.env b/apps/.env index e7e75bf..e4dc0bc 100644 --- a/apps/.env +++ b/apps/.env @@ -1 +1 @@ -HIDMAN_DEVICE=/dev/input/event7 +HIDMAN_DEVICE=/dev/input/event6 diff --git a/apps/client_server_timeout.yml b/apps/client_server_timeout.yml new file mode 100644 index 0000000..bce82dd --- /dev/null +++ b/apps/client_server_timeout.yml @@ -0,0 +1,29 @@ +version: '3.7' + +x-hidmanbase: &hidman-base + + volumes: + - type: bind + source: ../ + target: /usr/local/src/hidman + + image: iitschri/hidman:latest + + privileged: true + stdin_open: true + tty: true + network_mode: host + +services: + + hidman-server: + <<: *hidman-base + command: python3 /usr/local/src/hidman/apps/python-examples/server.py + devices: + - ${HIDMAN_DEVICE}:/dev/input/hidman0 + + hidman-client: + <<: *hidman-base + command: python3 /usr/local/src/hidman/apps/python-examples/client_timeout.py + depends_on: + - hidman-server diff --git a/apps/python-examples/client_timeout.py b/apps/python-examples/client_timeout.py new file mode 100644 index 0000000..be1cae5 --- /dev/null +++ b/apps/python-examples/client_timeout.py @@ -0,0 +1,7 @@ +from hidman.core import HIDClient, HIDKeyboard + + +dev = HIDClient(address="tcp://localhost:6666") + +while True: + print(dev.waitEvent(timeout_ms=3000)) diff --git a/hidman/__init__.py b/hidman/__init__.py index 9379502..d9b0fc2 100644 --- a/hidman/__init__.py +++ b/hidman/__init__.py @@ -1,6 +1,6 @@ __authors__ = 'Davide De Tommaso' __emails__ = 'davide.detommaso@iit.it' __license__ = 'MIT' -__version__ = '0.2' +__version__ = '0.3' __description__ = 'A Python based HID (Human Interface Device) events manager' __requirements__ = ['pytest>=6.2.4', 'evdev>=1.4.0', 'pyzmq>=22.1.0'] diff --git a/hidman/core.py b/hidman/core.py index 43069bc..7b60da3 100644 --- a/hidman/core.py +++ b/hidman/core.py @@ -4,6 +4,8 @@ import zmq import statistics import logging +import threading +from select import select class HIDDevice: @@ -20,25 +22,34 @@ def clear(self): return def waitEvent(self, event_type=None, event_code=None, event_value=None, timeout_ms=None, clear_events=True): + if clear_events: self.clear() if self._device: - for event in self._device.read_loop(): - if not event_type is None: - if event.type == event_type: - data = categorize(event) - if not event_value is None: - if data.keystate == event_value: - return (data.keystate, data.keycode, ecodes.EV_KEY) - if not event_code is None: - if event_code == data.keycode: - return (data.keystate, data.keycode, ecodes.EV_KEY) - else: + # In order to respect REQ and REP archetypes in ZMQ the client should receive a reply right before the timeout + if timeout_ms is None: + r = True + else: + r, w, x = select([self._device], [], [], timeout_ms/1000.0 - 0.01) + if r: + for event in self._device.read_loop(): + if not event_type is None: + if event.type == event_type: + data = categorize(event) + if not event_value is None: + if data.keystate == event_value: return (data.keystate, data.keycode, ecodes.EV_KEY) - else: - return (data.keystate, data.keycode, ecodes.EV_KEY) - else: - return (event.code, event.value) + if not event_code is None: + if event_code == data.keycode: + return (data.keystate, data.keycode, ecodes.EV_KEY) + else: + return (data.keystate, data.keycode, ecodes.EV_KEY) + else: + return (data.keystate, data.keycode, ecodes.EV_KEY) + else: + return (event.code, event.value) + else: #Timeout reached + return None else: return None @@ -53,6 +64,12 @@ def __init__(self, device=None, address="ipc://pyboard"): self._context = zmq.Context() self._socket = self._context.socket(zmq.REP) self._socket.bind(address) + self._socket_lock = threading.Lock() + + def reply(self, req): + res = self._device.waitEvent(event_type=req[0], event_code=req[1], event_value=req[2], timeout_ms=req[3]) + with self._socket_lock: + self._socket.send_pyobj(res) def run(self): while True: @@ -60,8 +77,7 @@ def run(self): if req is None: self._device.close() return - res = self._device.waitEvent(event_type=req[0], event_code=req[1], event_value=req[2], timeout_ms=req[3]) - self._socket.send_pyobj(res) + self.reply(req) #TODO: consider the reply call for multi-threading in the future class HIDKeyboard: KEY_UP = 0 @@ -69,6 +85,7 @@ class HIDKeyboard: KEY_HOLD = 2 class HIDClient: + def __init__(self, address="ipc://pyboard"): self._address = address self._context = zmq.Context()