Save current changes before reorganizing drivers and cleaning workspace
This commit is contained in:
+61
-5
@@ -5,7 +5,7 @@ import time
|
||||
class MCPServer:
|
||||
"""A lightweight JSON-RPC HTTP server implementing Model Context Protocol (MCP) endpoints."""
|
||||
|
||||
def __init__(self, display, led, battery, sensor, rtc, ble, vstream=None):
|
||||
def __init__(self, display, led, battery, sensor, rtc, ble, vstream=None, touch=None):
|
||||
self.display = display
|
||||
self.led = led
|
||||
self.battery = battery
|
||||
@@ -13,6 +13,7 @@ class MCPServer:
|
||||
self.rtc = rtc
|
||||
self.ble = ble
|
||||
self.vstream = vstream
|
||||
self.touch = touch
|
||||
|
||||
self.sock = None
|
||||
self.active_led_mode = "off" # static, breath, rainbow, off
|
||||
@@ -20,6 +21,7 @@ class MCPServer:
|
||||
|
||||
def start(self, port=80):
|
||||
"""Starts the TCP server non-blockingly."""
|
||||
self.port = port
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.sock.bind(('', port))
|
||||
@@ -39,6 +41,27 @@ class MCPServer:
|
||||
print("Failed to bind UDP Discovery socket: {}".format(ue))
|
||||
self.udp_sock = None
|
||||
|
||||
def restart(self):
|
||||
"""Re-initializes the listening sockets after a fatal error."""
|
||||
print("Restarting MCP Server sockets...")
|
||||
try:
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
except:
|
||||
pass
|
||||
self.sock = None
|
||||
try:
|
||||
if hasattr(self, 'udp_sock') and self.udp_sock:
|
||||
self.udp_sock.close()
|
||||
except:
|
||||
pass
|
||||
self.udp_sock = None
|
||||
|
||||
# Add a short delay to allow socket reuse to settle
|
||||
time.sleep_ms(100)
|
||||
port = getattr(self, 'port', 80)
|
||||
self.start(port)
|
||||
|
||||
def update(self):
|
||||
"""Check for incoming HTTP requests and handle them non-blockingly."""
|
||||
# 1. Handle UDP Discovery queries
|
||||
@@ -56,8 +79,18 @@ class MCPServer:
|
||||
|
||||
try:
|
||||
client, addr = self.sock.accept()
|
||||
except OSError:
|
||||
# No incoming connection
|
||||
except OSError as e:
|
||||
import errno
|
||||
err = getattr(e, 'errno', None)
|
||||
if err is None and e.args:
|
||||
err = e.args[0]
|
||||
ewouldblock = getattr(errno, 'EWOULDBLOCK', errno.EAGAIN)
|
||||
if err in (errno.EAGAIN, ewouldblock) or err is None:
|
||||
# No incoming connection
|
||||
return
|
||||
# Fatal socket error
|
||||
print(f"Fatal socket error in MCP Server accept(): {e}. Re-initializing...")
|
||||
self.restart()
|
||||
return
|
||||
|
||||
try:
|
||||
@@ -100,14 +133,21 @@ class MCPServer:
|
||||
rpc_req = json.loads(body)
|
||||
rpc_resp = self._handle_rpc(rpc_req)
|
||||
|
||||
# Send HTTP Response
|
||||
resp_body = json.dumps(rpc_resp)
|
||||
resp = "HTTP/1.1 200 OK\r\n"
|
||||
resp += "Content-Type: application/json\r\n"
|
||||
resp += f"Content-Length: {len(resp_body)}\r\n"
|
||||
resp += "Connection: close\r\n\r\n"
|
||||
resp += resp_body
|
||||
client.send(resp.encode('utf-8'))
|
||||
|
||||
data_to_send = resp.encode('utf-8')
|
||||
total_sent = 0
|
||||
while total_sent < len(data_to_send):
|
||||
sent = client.write(data_to_send[total_sent:])
|
||||
if sent is None or sent == 0:
|
||||
time.sleep_ms(10)
|
||||
continue
|
||||
total_sent += sent
|
||||
else:
|
||||
# Return 404
|
||||
resp = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
|
||||
@@ -171,6 +211,11 @@ class MCPServer:
|
||||
"description": "Read the current battery voltage and estimated capacity percentage.",
|
||||
"inputSchema": {"type": "object", "properties": {}}
|
||||
},
|
||||
{
|
||||
"name": "get_touch",
|
||||
"description": "Read the current touch state and coordinates from the capacitive touchscreen.",
|
||||
"inputSchema": {"type": "object", "properties": {}}
|
||||
},
|
||||
{
|
||||
"name": "get_sensors",
|
||||
"description": "Read onboard SHTC3 temperature and relative humidity.",
|
||||
@@ -404,6 +449,17 @@ class MCPServer:
|
||||
t, h = self.sensor.read_sensor()
|
||||
return json.dumps({"temperature_c": t, "humidity_pct": h})
|
||||
|
||||
elif name == "get_touch":
|
||||
if self.touch is None:
|
||||
return json.dumps({"error": "Touchscreen not configured on this device."})
|
||||
is_t = self.touch.is_touched()
|
||||
x, y = None, None
|
||||
if is_t:
|
||||
pt = self.touch.read_touch()
|
||||
if pt:
|
||||
x, y = pt
|
||||
return json.dumps({"is_touched": is_t, "x": x, "y": y})
|
||||
|
||||
elif name == "scan_ble":
|
||||
dur = int(args.get("duration_ms", 3000))
|
||||
# Scan returns dictionary: {mac: {rssi: rssi, name: name}}
|
||||
|
||||
Reference in New Issue
Block a user