raise NoReceiver if the receiver is removed during read
This commit is contained in:
parent
0dc02af78d
commit
692ea58937
|
@ -187,7 +187,7 @@ def list_devices(handle):
|
|||
|
||||
|
||||
def get_device_info(handle, device, device_name=None, features_array=None):
|
||||
"""Gets the complete info for a device.
|
||||
"""Gets the complete info for a device (type, name, firmwares, and features_array).
|
||||
|
||||
:returns: an AttachedDeviceInfo tuple, or ``None``.
|
||||
"""
|
||||
|
|
|
@ -141,7 +141,7 @@ def write(handle, device, data):
|
|||
|
||||
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
||||
been physically removed from the machine, or the kernel driver has been
|
||||
unloaded.
|
||||
unloaded. The handle will be closed automatically.
|
||||
"""
|
||||
wdata = b'\x10' + chr(device) + data + b'\x00' * (5 - len(data))
|
||||
_l.log(_LOG_LEVEL, "(%d,%d) <= w[%s]", handle, device, wdata.encode('hex'))
|
||||
|
@ -166,8 +166,17 @@ def read(handle, timeout=DEFAULT_TIMEOUT):
|
|||
(reply_code, device, message data). The reply code should be ``0x11`` for a
|
||||
successful feature call, or ``0x10`` to indicate some error, e.g. the device
|
||||
is no longer available.
|
||||
|
||||
:raises NoReceiver: if the receiver is no longer available, i.e. has
|
||||
been physically removed from the machine, or the kernel driver has been
|
||||
unloaded. The handle will be closed automatically.
|
||||
"""
|
||||
data = _hid.read(handle, _MAX_REPLY_SIZE * 2, timeout)
|
||||
if data is None:
|
||||
_l.warn("(%d,*) read failed, assuming receiver no longer available", handle)
|
||||
close(handle)
|
||||
raise NoReceiver
|
||||
|
||||
if data:
|
||||
_l.log(_LOG_LEVEL, "(%d,*) => r[%s]", handle, data.encode('hex'))
|
||||
if len(data) < _MIN_REPLY_SIZE:
|
||||
|
@ -175,8 +184,7 @@ def read(handle, timeout=DEFAULT_TIMEOUT):
|
|||
if len(data) > _MAX_REPLY_SIZE:
|
||||
_l.warn("(%d,*) => r[%s] read packet too long: %d bytes", handle, data.encode('hex'), len(data))
|
||||
return ord(data[0]), ord(data[1]), data[2:]
|
||||
else:
|
||||
_l.log(_LOG_LEVEL, "(%d,*) => r[]", handle)
|
||||
_l.log(_LOG_LEVEL, "(%d,*) => r[]", handle)
|
||||
|
||||
|
||||
def request(handle, device, feature_index_function, params=b'', features_array=None):
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
from time import sleep
|
||||
|
||||
from . import base
|
||||
from .exceptions import *
|
||||
|
@ -13,16 +13,29 @@ from .exceptions import *
|
|||
_LOG_LEVEL = 6
|
||||
_l = logging.getLogger('logitech.unifying_receiver.listener')
|
||||
|
||||
_EVENT_TIMEOUT = 100
|
||||
_IDLE_SLEEP = 1000.0 / 1000.0
|
||||
|
||||
_READ_EVENT_TIMEOUT = 90 # ms
|
||||
_IDLE_SLEEP = 900 # ms
|
||||
|
||||
|
||||
class EventsListener(threading.Thread):
|
||||
def __init__(self, receiver, callback):
|
||||
"""Listener thread for events from the Unifying Receiver.
|
||||
|
||||
Incoming events (code, device, data) will be delivered to the callback
|
||||
function. The callback is called in the listener thread, so it should return
|
||||
as fast as possible.
|
||||
|
||||
While this listener is running, you should use the request() method to make
|
||||
regular UR API calls, otherwise the replies will be captured by the listener
|
||||
and delivered as events to the callback. As an exception, you can make UR
|
||||
API calls in the events callback.
|
||||
"""
|
||||
def __init__(self, receiver, events_callback):
|
||||
super(EventsListener, self).__init__(name='Unifying_Receiver_Listener_' + str(receiver))
|
||||
self.daemon = True
|
||||
|
||||
self.receiver = receiver
|
||||
self.callback = callback
|
||||
self.callback = events_callback
|
||||
|
||||
self.task = None
|
||||
self.task_processing = threading.Lock()
|
||||
|
@ -30,38 +43,50 @@ class EventsListener(threading.Thread):
|
|||
self.task_reply = None
|
||||
self.task_done = threading.Event()
|
||||
|
||||
self.active = False
|
||||
|
||||
def run(self):
|
||||
_l.log(_LOG_LEVEL, "(%d) starting", self.receiver)
|
||||
self.active = True
|
||||
while self.active:
|
||||
# _l.log(_LOG_LEVEL, "(%d) reading next event", self.receiver)
|
||||
event = base.read(self.receiver, _EVENT_TIMEOUT)
|
||||
if event:
|
||||
_l.log(_LOG_LEVEL, "(%d) got event %s", self.receiver, event)
|
||||
self.callback.__call__(*event)
|
||||
elif self.task is None:
|
||||
# _l.log(_LOG_LEVEL, "(%d) idle sleep", self.receiver)
|
||||
time.sleep(_IDLE_SLEEP)
|
||||
else:
|
||||
self.task_reply = self._make_request(*self.task)
|
||||
self.task_done.set()
|
||||
try:
|
||||
# _l.log(_LOG_LEVEL, "(%d) reading next event", self.receiver)
|
||||
event = base.read(self.receiver, _READ_EVENT_TIMEOUT)
|
||||
except NoReceiver:
|
||||
_l.warn("(%d) receiver disconnected", self.receiver)
|
||||
self.active = False
|
||||
break
|
||||
|
||||
if self.active:
|
||||
if event:
|
||||
_l.log(_LOG_LEVEL, "(%d) got event %s", self.receiver, event)
|
||||
self.callback.__call__(*event)
|
||||
elif self.task is None:
|
||||
# _l.log(_LOG_LEVEL, "(%d) idle sleep", self.receiver)
|
||||
sleep(_IDLE_SLEEP / 1000.0)
|
||||
else:
|
||||
self.task_reply = self._make_request(*self.task)
|
||||
self.task_done.set()
|
||||
|
||||
def stop(self):
|
||||
"""Tells the listener to stop as soon as possible."""
|
||||
_l.log(_LOG_LEVEL, "(%d) stopping", self.receiver)
|
||||
self.active = False
|
||||
self.join()
|
||||
|
||||
def request(self, api_function, *args, **kwargs):
|
||||
"""Make an UR API request.
|
||||
|
||||
The api_function will get the receiver handle as a first agument, all
|
||||
other args and kwargs will follow.
|
||||
"""
|
||||
# _l.log(_LOG_LEVEL, "(%d) request '%s' with %s, %s", self.receiver, api_function.__name__, args, kwargs)
|
||||
self.task_processing.acquire()
|
||||
self.task_done.clear()
|
||||
self.task = (api_function, args, kwargs)
|
||||
|
||||
self.task_done.wait()
|
||||
reply = self.task_reply
|
||||
self.task = self.task_reply = None
|
||||
self.task_processing.release()
|
||||
|
||||
# _l.log(_LOG_LEVEL, "(%d) request '%s' => [%s]", self.receiver, api_function.__name__, reply.encode('hex'))
|
||||
if isinstance(reply, Exception):
|
||||
raise reply
|
||||
|
|
Loading…
Reference in New Issue