194 lines
7.0 KiB
Python
194 lines
7.0 KiB
Python
import json
|
|
import threading
|
|
import queue
|
|
import time
|
|
import traceback
|
|
from orchestrateur.sensors import gps
|
|
from shared import get_lora, get_mqtt_client, deviceTypes, config, payloads
|
|
from shared.logging import log
|
|
from sensors import ultrasonicRanger, temp_hum, button, camera
|
|
|
|
# --- Read Unique Device ID ---
|
|
try:
|
|
with open("device_id.txt", "r") as f:
|
|
DEVICE_ID = f.read().strip()
|
|
except Exception:
|
|
try:
|
|
with open("/home/pi/SmartWave/orchestrateur/device_id.txt", "r") as f:
|
|
DEVICE_ID = f.read().strip()
|
|
except Exception:
|
|
DEVICE_ID = "RPI_Orchestrateur_Default"
|
|
|
|
# Thread-safe queue for application messages
|
|
data_queue = queue.Queue()
|
|
|
|
lora = get_lora()
|
|
lora.configure()
|
|
|
|
def lora_listener():
|
|
"""Background Thread: Listens to LoRa traffic and responds to Heartbeats."""
|
|
print("Thread Écouteur LoRa démarré.")
|
|
while True:
|
|
paquet = lora.receive_packet(timeout_ms=1000)
|
|
if paquet:
|
|
donnees = paquet["data"]
|
|
expediteur_type = donnees.get("type")
|
|
|
|
if expediteur_type == deviceTypes.DEVICE_TYPES["MICROWAVE"]:
|
|
print(f"\n[Thread LoRa] Heartbeat reçu de {donnees.get('id')}")
|
|
reponse = {
|
|
"id": DEVICE_ID,
|
|
"type": deviceTypes.DEVICE_TYPES["ORCHESTRATOR"]
|
|
}
|
|
lora.send(reponse)
|
|
else:
|
|
data_queue.put({"source": "LoRa", "data": paquet})
|
|
|
|
# --- Setup & Connect MQTT ---
|
|
mqtt_client = get_mqtt_client(
|
|
host="192.168.50.1", # Using explicit gateway IP to dodge Docker loopback blocks
|
|
client_id="smartwave-orchestrateur-"+DEVICE_ID,
|
|
use_tls=config.USE_TLS,
|
|
cafile="/home/pi/SmartWave/orchestrateur/mqtt/certs/ca.crt",
|
|
keepalive=config.MQTT_KEEPALIVE,
|
|
)
|
|
mqtt_client.connect()
|
|
mqtt_client.subscribe(config.MQTT_TOPIC_SENSOR, qos=config.MQTT_QOS)
|
|
mqtt_client.subscribe(config.MQTT_TOPIC_HELLO, qos=config.MQTT_QOS)
|
|
print(f"Subscribed to topic: {config.MQTT_TOPIC_SENSOR}")
|
|
|
|
# --- THE CRUCIAL PAHO FIX ---
|
|
# Start Paho's internal background thread. This handles all network packets,
|
|
# automatic keepalive pings, and delivery receipts cleanly.
|
|
if hasattr(mqtt_client._client, "loop_start"):
|
|
mqtt_client._client.loop_start()
|
|
print("Paho MQTT asynchronous network loop started.")
|
|
|
|
|
|
def mqtt_listener():
|
|
"""Background Thread: Constantly inspects incoming MQTT message cache."""
|
|
print("Thread MQTT démarré.")
|
|
while True:
|
|
message = mqtt_client.get_message()
|
|
|
|
if message:
|
|
# Try to parse the payload as a python dictionary, but if it fails, just print the raw payload
|
|
try:
|
|
payload = json.loads(message['payload'])
|
|
except Exception as e:
|
|
print(f"Error parsing MQTT payload: {e}")
|
|
payload = message['payload'] # Fallback to raw payload if parsing fails
|
|
|
|
print(f"\n[Thread MQTT] Message reçu : {message}")
|
|
data_queue.put({"source": "MQTT", "topic": message['topic'] ,"data": payload})
|
|
|
|
# Sleep for 100ms. Prevents the thread from turning into an infinite 100% CPU hog.
|
|
time.sleep(0.2)
|
|
|
|
# Button
|
|
button_state = False
|
|
def button_callback():
|
|
global button_state
|
|
button_state = not button_state
|
|
print(f"\n[Thread Button] Button state changed to: {button_state}")
|
|
|
|
button.set_callback(button_callback)
|
|
|
|
# Launch background monitoring workers
|
|
# threading.Thread(target=lora_listener, daemon=True).start()
|
|
# threading.Thread(target=mqtt_listener, daemon=True).start()
|
|
# Launch button monitoring thread
|
|
button.start_button_monitoring_thread()
|
|
|
|
print("Orchestrateur prêt. Le main loop est libre.")
|
|
|
|
# Sensor reading
|
|
def read_sensors():
|
|
"""Read all sensors and return a dictionary of their values."""
|
|
log("\nLecture des capteurs...")
|
|
sensor_data = {}
|
|
|
|
# Read Ultrasonic Ranger
|
|
distance = ultrasonicRanger.get_dish_height()
|
|
if distance is not None:
|
|
log(f"\nLecture du capteur Ultrason : {distance}")
|
|
sensor_data["ultrasonic_distance"] = distance
|
|
|
|
# Read Temperature and Humidity
|
|
temperature, humidity = temp_hum.get_temperature_and_humidity()
|
|
if temperature is not None and humidity is not None:
|
|
log(f"\nLecture du capteur Temp/Hum : {temperature}, {humidity}")
|
|
sensor_data["temperature"] = temperature
|
|
sensor_data["humidity"] = humidity
|
|
|
|
|
|
# Read GPS Data
|
|
gps_data = gps.get_gps_data()
|
|
if gps_data:
|
|
log(f"\nLecture du capteur GPS : {gps_data}")
|
|
sensor_data["gps"] = gps_data
|
|
|
|
# Camera
|
|
picture_bytes = None
|
|
try:
|
|
picture_bytes = camera.get_picture()
|
|
log(f"\nLecture du capteur Caméra : {len(picture_bytes)} bytes")
|
|
sensor_data["camera_image"] = picture_bytes
|
|
except Exception as e:
|
|
log(f"Error reading camera data: {e}")
|
|
|
|
# Read Button State (last because he can still change state while reading other sensors)
|
|
sensor_data["button_state"] = button_state
|
|
|
|
return sensor_data
|
|
|
|
# --- MAIN EXECUTION LOOP ---
|
|
while True:
|
|
try:
|
|
# Check for non-heartbeat data
|
|
try:
|
|
msg = data_queue.get(block=False)
|
|
|
|
# print(msg)
|
|
|
|
if msg["source"] == "LoRa":
|
|
print(f"\n[Main Loop] LoRa : Données traitées : {msg['data']}")
|
|
elif msg["source"] == "MQTT":
|
|
if (msg["topic"] == config.MQTT_TOPIC_HELLO.decode('utf-8')):
|
|
if ("id_orchestrator" in msg["data"] and msg["data"]["id_orchestrator"] == DEVICE_ID):
|
|
# Do not answer to messages coming from me
|
|
continue
|
|
microwave_id = msg["data"]["id_microwave"]
|
|
print(f"\n[Main Loop] MQTT : Hello reçu de {microwave_id}.")
|
|
# Responds
|
|
mqtt_client.publish(config.MQTT_TOPIC_HELLO, payloads.mqtt_hello_ack(DEVICE_ID, microwave_id), qos=config.MQTT_QOS)
|
|
print(f"[Main Loop] MQTT : Réponse Hello envoyée à {microwave_id}.")
|
|
# TODO : Save in database
|
|
|
|
|
|
print(f"\n[Main Loop] MQTT : Données traitées : {msg['data']}")
|
|
except queue.Empty:
|
|
pass
|
|
|
|
# DEBUG : Read sensors
|
|
sensor_values = read_sensors()
|
|
if sensor_values:
|
|
sensor_values_print = sensor_values.copy()
|
|
if "camera_image" in sensor_values_print:
|
|
sensor_values_print["camera_image"] = f"<{len(sensor_values_print['camera_image'])} bytes>"
|
|
print(f"\nCapteurs Données lues : {sensor_values_print}")
|
|
|
|
time.sleep(3)
|
|
|
|
|
|
except KeyboardInterrupt:
|
|
break
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
time.sleep(1) # Prevents rapid error logging in case of persistent issues
|
|
|
|
# Clean termination
|
|
if hasattr(mqtt_client._client, "loop_stop"):
|
|
mqtt_client._client.loop_stop()
|
|
mqtt_client.close()
|