Skip to content
This repository was archived by the owner on Sep 5, 2026. It is now read-only.

Commit 33afb74

Browse files
author
David Cedar
committed
Improved USB hid adapter.
1 parent de8b2cf commit 33afb74

11 files changed

Lines changed: 157 additions & 26 deletions

File tree

programmor_adapters/shared/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def run(self) -> None:
128128
self._process_scheduled_messages()
129129

130130
# Sleep the thread
131-
sleep(0.0001) # 0.1ms
131+
sleep(0.001) # 0.1ms
132132

133133
def _process_scheduled_messages(self) -> None:
134134
"""Process Scheduled Messages

programmor_adapters/shared/comm.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,14 @@ def run(self) -> None:
6363
# Process incoming messages
6464
self.process_incoming_frames()
6565

66+
# Sleep the thread
67+
sleep(0.005) # 5ms
68+
6669
# Process outgoing messages
6770
self.process_outgoing_frames()
6871

6972
# Sleep the thread
70-
sleep(0.0001) # 0.1ms
73+
sleep(0.005) # 5ms
7174

7275
def process_incoming_frames(self) -> ProcessState:
7376
"""Processes the incoming frames.
@@ -185,6 +188,7 @@ def process_outgoing_frames(self) -> ProcessState:
185188
# Write data
186189
frame_data = frame.to_bytes()
187190
self.write(frame_data)
191+
sleep(0.001) # 1ms
188192

189193
return ProcessState.OK
190194

programmor_adapters/shared/socket_endpoint.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class SocketEndpoint(Endpoint):
3535
class ApiNamespace(WebSocketEndpoint):
3636
api: API
3737
ws_connections: List[WebSocket]
38-
emit: Callable
38+
emit: Callable # type: ignore
3939

4040
async def get_devices(self, _):
4141
"""Get Devices
@@ -219,7 +219,7 @@ def __init__(self, api: API, port: int) -> None:
219219
self.ApiNamespace.emit = self.emit
220220
api.register_callback(lambda data: self.emit_data(data))
221221
self.app = Starlette(routes=routes, middleware=middleware, on_startup=[self.start_thread])
222-
self.message_queue = queue.Queue()
222+
self.message_queue: queue.Queue[ResponseType] = queue.Queue()
223223
self.stop_event = threading.Event()
224224
logger.debug('Websocket Endpoint Initialised')
225225

@@ -268,7 +268,7 @@ def patch_log_started_message(listeners):
268268
orig_log_started_message(listeners)
269269
server_started(server)
270270

271-
server._log_started_message = patch_log_started_message
271+
server._log_started_message = patch_log_started_message # type: ignore
272272
logger.debug('Starting socket endpoint')
273273
server.run()
274274

@@ -277,4 +277,4 @@ def stop(self) -> None:
277277
"""
278278
self.stop_event.set()
279279
for ws in self.ws_connections:
280-
ws.close()
280+
ws.close() # type: ignore

programmor_adapters/test_adapter/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33

44
# Run Application
5-
main()
5+
main()

programmor_adapters/test_adapter/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def main():
9191
time.sleep(0.1) # Sleep to keep the main thread alive
9292
except KeyboardInterrupt:
9393
logger.info("User stopping Application")
94-
94+
9595
# Stopping Adapter
9696
logger.info("Stopping Application")
9797
api.stop()

programmor_adapters/test_adapter/test_device.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def process_data(self, data: bytes) -> None:
146146
elif inMessage.action == transaction_pb2.TransactionMessage.SHARE_PUBLISH: # type: ignore
147147
if inMessage.shareId == 1:
148148
# Share1 publish
149-
inData: bytes = inMessage.data[0:inMessage.dataLength]
149+
inData: bytes = inMessage.data[0:inMessage.dataLength] # type: ignore
150150
testMessage: test_pb2.TestMessage = test_pb2.Share1() # type: ignore
151151
try:
152152
testMessage.ParseFromString(bytes(inData[0:DATA_MAX_SIZE]))
@@ -159,7 +159,7 @@ def process_data(self, data: bytes) -> None:
159159

160160
elif inMessage.shareId == 2:
161161
# Share2 publish
162-
inData: bytes = inMessage.data[0:inMessage.dataLength]
162+
inData: bytes = inMessage.data[0:inMessage.dataLength] # type: ignore
163163
testMessage: test_pb2.TestMessage = test_pb2.Share2() # type: ignore
164164
try:
165165
testMessage.ParseFromString(bytes(inData[0:DATA_MAX_SIZE]))
@@ -175,7 +175,7 @@ def process_data(self, data: bytes) -> None:
175175

176176
elif inMessage.shareId == 4:
177177
# Share4 publish
178-
inData: bytes = inMessage.data[0:inMessage.dataLength]
178+
inData: bytes = inMessage.data[0:inMessage.dataLength] # type: ignore
179179
testMessage: test_pb2.TestMessage = test_pb2.Share4() # type: ignore
180180
try:
181181
testMessage.ParseFromString(bytes(inData[0:DATA_MAX_SIZE]))
@@ -187,7 +187,7 @@ def process_data(self, data: bytes) -> None:
187187

188188
elif inMessage.shareId == 5:
189189
# Share5 publish
190-
inData: bytes = inMessage.data[0:inMessage.dataLength]
190+
inData: bytes = inMessage.data[0:inMessage.dataLength] # type: ignore
191191
testMessage: test_pb2.TestMessage = test_pb2.Share5() # type: ignore
192192
try:
193193
testMessage.ParseFromString(bytes(inData[0:DATA_MAX_SIZE]))
@@ -204,7 +204,7 @@ def process_data(self, data: bytes) -> None:
204204

205205
elif inMessage.shareId == 6:
206206
# Share6 publish
207-
inData: bytes = inMessage.data[0:inMessage.dataLength]
207+
inData: bytes = inMessage.data[0:inMessage.dataLength] # type: ignore
208208
testMessage: test_pb2.TestMessage = test_pb2.Share6() # type: ignore
209209
try:
210210
testMessage.ParseFromString(bytes(inData[0:DATA_MAX_SIZE]))

programmor_adapters/usb_adapter/usb.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
from typing import Optional
21
import usb.core
32
import usb.util
43
import libusb_package
4+
from typing import Optional
55

66
from shared.comm import Comm
77
from usb_adapter.helper import get_device_endpoints
8+
from usb_adapter.usb_read import USBRead
9+
from usb_adapter.usb_write import USBWrite
810

911
import logging
1012
logger = logging.getLogger(__name__)
@@ -24,27 +26,24 @@ def __init__(self, device_id_vender: int, device_id_product: int) -> None:
2426
# meta data
2527
# self.device_manufacturer: str = None
2628
# self.device_product: str = None
29+
# Endpoints.
30+
self.read_endpoint: Optional[USBRead] = None
31+
self.write_endpoint: Optional[USBWrite] = None
2732

2833
def __str__(self) -> str:
2934
return f"USB(id: vender_id: {self.device_id_vender} product_id: {self.device_id_product})"
3035

3136
def read(self) -> bytes:
3237
# Return no bytes if device is not ready
33-
if self.device is None:
34-
return bytes(0)
35-
try:
36-
if self.device_endpoint_in is not None:
37-
return bytes(self.device_endpoint_in.read(64, 100))
38-
else:
39-
return bytes(0)
40-
except Exception:
38+
if self.device is None or self.read_endpoint is None:
4139
return bytes(0)
40+
return self.read_endpoint.read()
4241

4342
def write(self, buffer: bytes) -> None:
4443
if len(buffer) > 0 and self.device is None:
4544
return # Not connected to device
46-
if self.device_endpoint_out is not None:
47-
self.device_endpoint_out.write(bytes(buffer), 100)
45+
if self.write_endpoint:
46+
self.write_endpoint.write(buffer)
4847

4948
def connect(self) -> bool:
5049
self.device: Optional[usb.core.Device] = libusb_package.find(idVendor=self.device_id_vender, idProduct=self.device_id_product) # type: ignore
@@ -53,6 +52,10 @@ def connect(self) -> bool:
5352
self.device_endpoint_in, self.device_endpoint_out = get_device_endpoints(self.device)
5453
if self.device_endpoint_in is None or self.device_endpoint_out is None:
5554
return False
55+
self.read_endpoint = USBRead(self.device_endpoint_in, 1)
56+
self.write_endpoint = USBWrite(self.device_endpoint_out, 1)
57+
self.read_endpoint.start()
58+
self.write_endpoint.start()
5659
logger.debug(f"Connected {self.__str__()}")
5760
return True
5861

@@ -62,6 +65,12 @@ def close(self) -> None:
6265
# self.device.reset()
6366
usb.util.dispose_resources(self.device)
6467
self.device = None
68+
if self.read_endpoint:
69+
self.read_endpoint.stop()
70+
if self.write_endpoint:
71+
self.write_endpoint.stop()
72+
self.read_endpoint = None
73+
self.write_endpoint = None
6574
self.device_endpoint_in = None
6675
self.device_endpoint_out = None
6776
logger.debug(f"Closed {self.__str__()}")
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from time import sleep
2+
from typing import Optional
3+
from collections import deque
4+
import threading
5+
import usb.core
6+
7+
import logging
8+
logger = logging.getLogger(__name__)
9+
10+
11+
class USBEndpoint(threading.Thread):
12+
"""
13+
USBEndpoint.
14+
A generic USB endpoint. Extend this class to implement the reader/writer.
15+
"""
16+
def __init__(self, endpoint: Optional[usb.core.Endpoint], interval: int) -> None:
17+
"""Constructor method
18+
"""
19+
threading.Thread.__init__(self)
20+
self.stop_flag: bool = False
21+
self.endpoint = endpoint
22+
self.interval = interval
23+
self.queue: deque[bytes] = deque()
24+
25+
def start(self) -> None:
26+
"""Starts the Endpoint
27+
"""
28+
threading.Thread.start(self)
29+
self.stop_flag = False
30+
logger.debug("Starting USBEndpoint Thread")
31+
32+
def stop(self) -> None:
33+
"""Stops the thread
34+
"""
35+
logger.debug("Stopping USBEndpoint Thread")
36+
self.stop_flag = True
37+
38+
def run(self) -> None:
39+
"""Run method used by python threading
40+
"""
41+
while True:
42+
# Stop thread
43+
if self.stop_flag:
44+
logger.debug("Stopped USBEndpoint Thread")
45+
break
46+
47+
# Process incoming messages
48+
self.action()
49+
50+
# Sleep the thread as per the interval.
51+
sleep(self.interval/1000)
52+
53+
def action(self):
54+
raise NotImplementedError("Action method not implemented")

programmor_adapters/usb_adapter/usb_manager.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ def get_devices(self) -> List[str]:
4141
# Check if device is available
4242
if dev is None:
4343
continue
44-
# Check if meta data is available
44+
# Skip keyboards and mice
45+
if dev.bDeviceClass == 0x03:
46+
continue
4547
try:
4648
assert dev.manufacturer is not None
4749
assert dev.product is not None
@@ -50,7 +52,7 @@ def get_devices(self) -> List[str]:
5052
# Skip STM32 Link V3
5153
if dev.idVendor == 1155 and dev.idProduct == 14159:
5254
continue
53-
# print(f"{dev.manufacturer} {dev.product}")
55+
print(f"{dev.manufacturer} {dev.product}")
5456
# Check if the device is already in use
5557
device_id = self.get_device_id_from_vender_product(dev.idVendor, dev.idProduct)
5658
if device_id:
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from usb_adapter.usb_endpoint import USBEndpoint
2+
from typing import Optional
3+
import usb.core
4+
5+
import logging
6+
logger = logging.getLogger(__name__)
7+
8+
9+
class USBRead(USBEndpoint):
10+
def __init__(self, endpoint: Optional[usb.core.Endpoint], interval: int) -> None:
11+
super().__init__(endpoint, interval)
12+
13+
def action(self) -> None:
14+
try:
15+
if self.endpoint is not None:
16+
self.queue.append(bytes(self.endpoint.read(64, 1)))
17+
# print("Read")
18+
except Exception:
19+
# print("Read Error")
20+
# print(e)
21+
pass
22+
23+
def read(self) -> bytes:
24+
try:
25+
return self.queue.popleft()
26+
except Exception:
27+
return bytes(0)

0 commit comments

Comments
 (0)