Compare commits
2 Commits
fb6ee0099a
...
25e635e207
| Author | SHA1 | Date | |
|---|---|---|---|
| 25e635e207 | |||
| fb41a2d07a |
+41
-4
@@ -6,12 +6,40 @@ import asyncio
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
from orchestrateur.sensors import gps
|
from orchestrateur.sensors import gps
|
||||||
from shared import get_lora, get_mqtt_client, deviceTypes, config, payloads
|
from shared import get_lora, get_mqtt_client, deviceTypes, config, payloads, db
|
||||||
from shared.logging import log
|
from shared.logging import log
|
||||||
from shared.cookingState import CookingStates
|
from shared.cookingState import CookingStates
|
||||||
from shared.lora_device import LoraCommands
|
from shared.lora_device import LoraCommands
|
||||||
from sensors import ultrasonicRanger, temp_hum, button, camera
|
from sensors import ultrasonicRanger, temp_hum, button, camera
|
||||||
|
|
||||||
|
# --- DB SETUP ---
|
||||||
|
DB_PATH = "orchestrateur/db.sqlite"
|
||||||
|
|
||||||
|
def init_db():
|
||||||
|
"""Ensures the connected_components table exists on startup."""
|
||||||
|
sql = """
|
||||||
|
CREATE TABLE IF NOT EXISTS connected_components (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
type TEXT,
|
||||||
|
timestamp INTEGER
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
db.execute(DB_PATH, sql)
|
||||||
|
print(f"[DB] Initialized database table at {DB_PATH}")
|
||||||
|
|
||||||
|
def save_connected_component(component_id: str, component_type: str):
|
||||||
|
"""Upserts component information into the database (blocking sync worker)."""
|
||||||
|
current_time = int(time.time())
|
||||||
|
sql = """
|
||||||
|
INSERT INTO connected_components (id, type, timestamp)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
ON CONFLICT(id) DO UPDATE SET
|
||||||
|
type = excluded.type,
|
||||||
|
timestamp = excluded.timestamp;
|
||||||
|
"""
|
||||||
|
db.execute(DB_PATH, sql, (str(component_id), str(component_type), current_time))
|
||||||
|
print(f"[DB] Component saved/updated -> ID: {component_id}, Type: {component_type}, Timestamp: {current_time}")
|
||||||
|
|
||||||
# --- Read Unique Device ID ---
|
# --- Read Unique Device ID ---
|
||||||
def get_device_id():
|
def get_device_id():
|
||||||
for path in ["device_id.txt", "/home/pi/SmartWave/orchestrateur/device_id.txt"]:
|
for path in ["device_id.txt", "/home/pi/SmartWave/orchestrateur/device_id.txt"]:
|
||||||
@@ -287,11 +315,17 @@ async def process_messages_task():
|
|||||||
|
|
||||||
if topic == hello_topic:
|
if topic == hello_topic:
|
||||||
if data.get("id_orchestrator") != DEVICE_ID:
|
if data.get("id_orchestrator") != DEVICE_ID:
|
||||||
mw_id = data.get("id_microwave")
|
component_id = data.get("id_microwave")
|
||||||
print(f"[MQTT] Hello from {mw_id}. Sending ACK.")
|
component_type = data.get("type", deviceTypes.DEVICE_TYPES["MICROWAVE"])
|
||||||
|
|
||||||
|
if component_id:
|
||||||
|
print(f"[MQTT] Hello received from '{component_id}' ({component_type}). Updating DB & sending ACK.")
|
||||||
|
# Offload DB insertion to async thread execution pool
|
||||||
|
await asyncio.to_thread(save_connected_component, component_id, component_type)
|
||||||
|
|
||||||
mqtt_client.publish(
|
mqtt_client.publish(
|
||||||
config.MQTT_TOPIC_HELLO,
|
config.MQTT_TOPIC_HELLO,
|
||||||
payloads.mqtt_hello_ack(DEVICE_ID, mw_id),
|
payloads.mqtt_hello_ack(DEVICE_ID, component_id),
|
||||||
qos=config.MQTT_QOS
|
qos=config.MQTT_QOS
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -372,6 +406,9 @@ async def main():
|
|||||||
global async_event_queue
|
global async_event_queue
|
||||||
print("🚀 Orchestrateur Asyncio prêt. Lancement des tâches...")
|
print("🚀 Orchestrateur Asyncio prêt. Lancement des tâches...")
|
||||||
|
|
||||||
|
# Initialize SQLite database table
|
||||||
|
init_db()
|
||||||
|
|
||||||
async_event_queue = asyncio.Queue()
|
async_event_queue = asyncio.Queue()
|
||||||
|
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
|
|||||||
+38
-5
@@ -5,6 +5,8 @@ same code can run on CPython (`sqlite3`) and MicroPython (`sqlite3` or
|
|||||||
`usqlite`, depending on the port).
|
`usqlite`, depending on the port).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from shared.logging import log
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import sqlite3 as _sqlite
|
import sqlite3 as _sqlite
|
||||||
DRIVER_NAME = "sqlite3"
|
DRIVER_NAME = "sqlite3"
|
||||||
@@ -13,20 +15,25 @@ except ImportError:
|
|||||||
import usqlite as _sqlite
|
import usqlite as _sqlite
|
||||||
DRIVER_NAME = "usqlite"
|
DRIVER_NAME = "usqlite"
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
||||||
|
log("[DB Error] No sqlite driver found. Expected sqlite3 or usqlite.")
|
||||||
raise ImportError("No sqlite driver found. Expected sqlite3 or usqlite.") from exc
|
raise ImportError("No sqlite driver found. Expected sqlite3 or usqlite.") from exc
|
||||||
|
|
||||||
|
|
||||||
def _connect(database_path, **connect_kwargs):
|
def _connect(database_path, **connect_kwargs):
|
||||||
|
try:
|
||||||
if connect_kwargs:
|
if connect_kwargs:
|
||||||
try:
|
try:
|
||||||
return _sqlite.connect(database_path, **connect_kwargs)
|
return _sqlite.connect(database_path, **connect_kwargs)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
return _sqlite.connect(database_path)
|
return _sqlite.connect(database_path)
|
||||||
|
except Exception as e:
|
||||||
|
log(f"[DB Error] Driver connect failed for '{database_path}': {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
"""Lightweight connection wrapper with a consistent API."""
|
"""Lightweight connection wrapper with logging and consistent API."""
|
||||||
|
|
||||||
def __init__(self, database_path, **connect_kwargs):
|
def __init__(self, database_path, **connect_kwargs):
|
||||||
self._database_path = database_path
|
self._database_path = database_path
|
||||||
@@ -40,29 +47,45 @@ class Database:
|
|||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
if self._connection is not None:
|
if self._connection is not None:
|
||||||
|
try:
|
||||||
self._connection.close()
|
self._connection.close()
|
||||||
|
except Exception as e:
|
||||||
|
log(f"[DB Error] Failed to close database '{self._database_path}': {e}")
|
||||||
|
finally:
|
||||||
self._connection = None
|
self._connection = None
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
connection = self.open()
|
connection = self.open()
|
||||||
if hasattr(connection, "commit"):
|
if hasattr(connection, "commit"):
|
||||||
|
try:
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
except Exception as e:
|
||||||
|
log(f"[DB Error] Commit failed on '{self._database_path}': {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
def cursor(self):
|
def cursor(self):
|
||||||
return self.open().cursor()
|
return self.open().cursor()
|
||||||
|
|
||||||
def execute(self, sql, params=None):
|
def execute(self, sql, params=None):
|
||||||
cursor = self.cursor()
|
cursor = self.cursor()
|
||||||
|
try:
|
||||||
if params is None:
|
if params is None:
|
||||||
cursor.execute(sql)
|
cursor.execute(sql)
|
||||||
else:
|
else:
|
||||||
cursor.execute(sql, params)
|
cursor.execute(sql, params)
|
||||||
return cursor
|
return cursor
|
||||||
|
except Exception as e:
|
||||||
|
log(f"[DB Error] Query failed on '{self._database_path}' | SQL: {sql} | Params: {params} | Error: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
def executemany(self, sql, params_list):
|
def executemany(self, sql, params_list):
|
||||||
cursor = self.cursor()
|
cursor = self.cursor()
|
||||||
|
try:
|
||||||
cursor.executemany(sql, params_list)
|
cursor.executemany(sql, params_list)
|
||||||
return cursor
|
return cursor
|
||||||
|
except Exception as e:
|
||||||
|
log(f"[DB Error] Executemany failed on '{self._database_path}' | SQL: {sql} | Error: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
def fetchone(self, sql, params=None):
|
def fetchone(self, sql, params=None):
|
||||||
return self.execute(sql, params).fetchone()
|
return self.execute(sql, params).fetchone()
|
||||||
@@ -73,16 +96,23 @@ class Database:
|
|||||||
def executescript(self, script):
|
def executescript(self, script):
|
||||||
connection = self.open()
|
connection = self.open()
|
||||||
if hasattr(connection, "executescript"):
|
if hasattr(connection, "executescript"):
|
||||||
|
try:
|
||||||
return connection.executescript(script)
|
return connection.executescript(script)
|
||||||
|
except Exception as e:
|
||||||
|
log(f"[DB Error] Executescript failed on '{self._database_path}': {e}")
|
||||||
|
raise
|
||||||
|
log("[DB Error] executescript is not available on this sqlite backend")
|
||||||
raise NotImplementedError("executescript is not available on this sqlite backend")
|
raise NotImplementedError("executescript is not available on this sqlite backend")
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.open()
|
self.open()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc, traceback):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
if exc_type is None:
|
if exc_type is None:
|
||||||
self.commit()
|
self.commit()
|
||||||
|
else:
|
||||||
|
log(f"[DB Error] Context exited with exception on '{self._database_path}': {exc_val}")
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -91,12 +121,15 @@ def connect(database_path, **connect_kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def execute(database_path, sql, params=None, **connect_kwargs):
|
def execute(database_path, sql, params=None, **connect_kwargs):
|
||||||
return connect(database_path, **connect_kwargs).execute(sql, params)
|
with connect(database_path, **connect_kwargs) as db_inst:
|
||||||
|
return db_inst.execute(sql, params)
|
||||||
|
|
||||||
|
|
||||||
def fetchone(database_path, sql, params=None, **connect_kwargs):
|
def fetchone(database_path, sql, params=None, **connect_kwargs):
|
||||||
return connect(database_path, **connect_kwargs).fetchone(sql, params)
|
with connect(database_path, **connect_kwargs) as db_inst:
|
||||||
|
return db_inst.fetchone(sql, params)
|
||||||
|
|
||||||
|
|
||||||
def fetchall(database_path, sql, params=None, **connect_kwargs):
|
def fetchall(database_path, sql, params=None, **connect_kwargs):
|
||||||
return connect(database_path, **connect_kwargs).fetchall(sql, params)
|
with connect(database_path, **connect_kwargs) as db_inst:
|
||||||
|
return db_inst.fetchall(sql, params)
|
||||||
+3
-1
@@ -1,4 +1,5 @@
|
|||||||
from time import time
|
from time import time
|
||||||
|
from shared.deviceTypes import DEVICE_TYPES
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -16,7 +17,8 @@ def as_json(data):
|
|||||||
|
|
||||||
def mqtt_hello(id_microwave):
|
def mqtt_hello(id_microwave):
|
||||||
return as_json({
|
return as_json({
|
||||||
"id_microwave": id_microwave
|
"id_microwave": id_microwave,
|
||||||
|
"type": DEVICE_TYPES["MICROWAVE"]
|
||||||
})
|
})
|
||||||
|
|
||||||
def mqtt_hello_ack(id_orchestrator, id_microwave):
|
def mqtt_hello_ack(id_orchestrator, id_microwave):
|
||||||
|
|||||||
Reference in New Issue
Block a user