Skip to content

Commit f0b85ec

Browse files
committed
feat: python RPC server w example
1 parent ff262b7 commit f0b85ec

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
def add(a, b):
2+
return a + b
3+
4+
def greet(name):
5+
return f"Hello, {name}!"
6+
7+
server = SerialServer(port='/dev/ttyUSB0', baudrate=115200)
8+
server.register_callback('add', add)
9+
server.register_callback('greet', greet)
10+
server.start()
11+
12+
print("Serial server running... Press Ctrl+C to exit.")
13+
try:
14+
while True:
15+
pass # Keep main thread alive
16+
except KeyboardInterrupt:
17+
server.stop()
18+
print("\nSerial server stopped.")

extras/serial_server.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import serial
2+
import msgpack
3+
import threading
4+
5+
6+
REQUEST = 0
7+
RESPONSE = 1
8+
NOTIFY = 2
9+
10+
11+
class SerialServer:
12+
def __init__(self, port, baudrate=115200):
13+
self.ser = serial.Serial(port, baudrate, timeout=0.1)
14+
self.callbacks = {}
15+
self.running = False
16+
17+
def register_callback(self, command, func):
18+
"""Register a callback for a specific command key"""
19+
self.callbacks[command] = func
20+
21+
def on_request(self, msg_id, command, args):
22+
"""Execute the callback and respond"""
23+
try:
24+
result = self.callbacks[command](*args)
25+
return [RESPONSE, msg_id, None, result]
26+
except Exception as e:
27+
print("Not handling exceptions yet")
28+
29+
def handle_message(self, message) -> bytes:
30+
"""Process incoming messages"""
31+
msgsize = len(message)
32+
if msgsize != 4 and msgsize != 3:
33+
raise Exception("Invalid MessagePack-RPC protocol: message = {0}".format(message))
34+
35+
msgtype = message[0]
36+
if msgtype == REQUEST:
37+
response = self.on_request(message[1], message[2], message[3])
38+
elif msgtype == RESPONSE:
39+
raise Exception("Server receiving RESPONSE not implemented")
40+
# response = self.on_response(message[1], message[2], message[3])
41+
elif msgtype == NOTIFY:
42+
print("Server does nothing on notification")
43+
# self.on_notify(message[1], message[2])
44+
response = None
45+
else:
46+
raise Exception("Unknown message type: type = {0}".format(msgtype))
47+
48+
return msgpack.packb(response)
49+
50+
def start(self):
51+
"""Start the serial server loop"""
52+
self.running = True
53+
threading.Thread(target=self._run, daemon=True).start()
54+
55+
def _run(self):
56+
unpacker = msgpack.Unpacker(raw=False)
57+
while self.running:
58+
try:
59+
data = self.ser.read(1024)
60+
if data:
61+
unpacker.feed(data)
62+
for message in unpacker:
63+
response = self.handle_message(message)
64+
if response is not None:
65+
self.ser.write(response)
66+
except Exception as e:
67+
print(f"Error: {e}")
68+
69+
def stop(self):
70+
self.running = False
71+
self.ser.close()

0 commit comments

Comments
 (0)