# import paho.mqtt.client as mqtt # import uuid # import time # import threading # import socket # class MqttClient: # """ # Handles the connection and message processing for a single MQTT station. # This is a standard Python class, with no GUI dependencies. # """ # def __init__(self, broker, port, user, password, station_id, on_message_callback): # self.broker = broker # self.port = port # self.user = user # self.password = password # self.station_id = station_id # self.on_message_callback = on_message_callback # unique_id = str(uuid.uuid4()) # self.client_id = f"WebApp-Backend-{self.station_id}-{unique_id}" # self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, self.client_id) # # Assign callback functions # self.client.on_connect = self.on_connect # self.client.on_message = self.on_message # self.client.on_disconnect = self.on_disconnect # if self.user and self.password: # self.client.username_pw_set(self.user, self.password) # self.is_connected = False # self.reconnect_delay = 1 # self.max_reconnect_delay = 60 # self.stop_thread = False # # --- CORRECTED CALLBACK SIGNATURES --- # def on_connect(self, client, userdata, flags, reason_code, properties): # """Callback for when the client connects to the broker.""" # if reason_code == 0: # self.is_connected = True # self.reconnect_delay = 1 # print(f"Successfully connected to MQTT broker for station: {self.station_id}") # topic_base = f"VEC/batterySmartStation/v100/{self.station_id}/#" # # topic_base = f"VEC/batterySmartStation/v100/+/+" # self.client.subscribe(topic_base) # print(f"Subscribed to: {topic_base}") # else: # print(f"Failed to connect to MQTT for station {self.station_id}, return code {reason_code}") # def on_disconnect(self, client, userdata, disconnect_flags, reason_code, properties): # """Callback for when the client disconnects.""" # print(f"Disconnected from MQTT for station {self.station_id}. Will attempt to reconnect...") # def on_message(self, client, userdata, msg): # """Callback for when a message is received from the broker.""" # try: # self.on_message_callback(self.station_id, msg.topic, msg.payload) # except Exception as e: # print(f"Error processing message in callback for topic {msg.topic}: {e}") # def connect(self): # """Connects the client to the MQTT broker.""" # print(f"Attempting to connect to {self.broker}:{self.port} with client ID: {self.client_id}") # try: # self.client.connect(self.broker, self.port, 60) # except Exception as e: # print(f"Error connecting to MQTT for station {self.station_id}: {e}") # def start(self): # """Starts the MQTT client's network loop in a separate thread.""" # self.connect() # self.client.loop_start() # def stop(self): # """Stops the MQTT client's network loop.""" # print(f"Stopping MQTT client for station: {self.station_id}") # self.client.loop_stop() import paho.mqtt.client as mqtt import uuid import time import threading import socket class MqttClient: """ Handles the connection and message processing for a single MQTT station. This is a standard Python class, with no GUI dependencies. """ def __init__(self, broker, port, user, password, station_id, on_message_callback): self.broker = broker self.port = port self.user = user self.password = password self.station_id = station_id self.on_message_callback = on_message_callback unique_id = str(uuid.uuid4()) self.client_id = f"WebApp-Backend-{self.station_id}-{unique_id}" self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, self.client_id) # Assign callback functions self.client.on_connect = self.on_connect self.client.on_message = self.on_message self.client.on_disconnect = self.on_disconnect if self.user and self.password: self.client.username_pw_set(self.user, self.password) self.is_connected = False self.stop_thread = False # <-- We will use this flag # --- (Your on_connect, on_disconnect, and on_message methods stay the same) --- def on_connect(self, client, userdata, flags, reason_code, properties): """Callback for when the client connects to the broker.""" if reason_code == 0: self.is_connected = True print(f"Successfully connected to MQTT broker for station: {self.station_id}") topic_base = f"VEC/batterySmartStation/v100/{self.station_id}/#" self.client.subscribe(topic_base) print(f"Subscribed to: {topic_base}") else: print(f"Failed to connect to MQTT for station {self.station_id}, return code {reason_code}") def on_disconnect(self, client, userdata, disconnect_flags, reason_code, properties): """Callback for when the client disconnects.""" self.is_connected = False # Only print reconnect message if it wasn't a deliberate stop if not self.stop_thread: print(f"Disconnected from MQTT for station {self.station_id}. Will attempt to reconnect...") else: print(f"Intentionally disconnected from MQTT for station {self.station_id}.") def on_message(self, client, userdata, msg): """Callback for when a message is received from the broker.""" try: self.on_message_callback(self.station_id, msg.topic, msg.payload) except Exception as e: print(f"Error processing message in callback for topic {msg.topic}: {e}") def run(self): """A blocking loop that handles connection and reconnection.""" while not self.stop_thread: try: print(f"Attempting to connect to {self.broker}:{self.port} for station {self.station_id}") self.client.connect(self.broker, self.port, 60) self.client.loop_forever() # This is a blocking call break # Exit loop if loop_forever finishes cleanly except socket.error as e: print(f"Connection error for {self.station_id}: {e}. Retrying in 5 seconds...") time.sleep(5) except Exception as e: print(f"An unexpected error occurred for {self.station_id}: {e}. Retrying in 5 seconds...") time.sleep(5) def start(self): """Starts the MQTT client's network loop in a separate thread.""" # --- CHANGED --- # We now run our custom `run` method in a thread main_thread = threading.Thread(target=self.run) main_thread.daemon = True main_thread.start() def stop(self): """Stops the MQTT client's network loop.""" # --- CHANGED --- # This is the complete, correct way to stop the client print(f"Stopping MQTT client for station: {self.station_id}") self.stop_thread = True self.client.disconnect() # This tells the client to disconnect gracefully