Fix: Improve MQTT connection and error handling
parent
77ff85c541
commit
7284626248
|
|
@ -1,17 +1,154 @@
|
||||||
|
# # In core/mqtt_client.py
|
||||||
|
# import socket
|
||||||
|
# from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot
|
||||||
|
# import paho.mqtt.client as mqtt
|
||||||
|
# import uuid
|
||||||
|
|
||||||
|
# class MqttClient(QObject):
|
||||||
|
# # --- MODIFIED SIGNAL: Now sends a bool and a string ---
|
||||||
|
# connection_status_changed = pyqtSignal(bool, str)
|
||||||
|
# message_received = pyqtSignal(str, bytes)
|
||||||
|
# connection_error = pyqtSignal(str)
|
||||||
|
# stop_logging_signal = pyqtSignal()
|
||||||
|
# connected = pyqtSignal()
|
||||||
|
# disconnected = pyqtSignal()
|
||||||
|
|
||||||
|
# def __init__(self, broker, port, user, password, client_id):
|
||||||
|
# super().__init__()
|
||||||
|
# self.broker = broker
|
||||||
|
# self.port = port
|
||||||
|
|
||||||
|
# self.client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id)
|
||||||
|
# if user and password:
|
||||||
|
# self.client.username_pw_set(user, password)
|
||||||
|
|
||||||
|
# self.client.on_connect = self.on_connect
|
||||||
|
# self.client.on_disconnect = self.on_disconnect
|
||||||
|
# self.client.on_message = self.on_message
|
||||||
|
# # self.client.on_subscribe = self.on_subscribe
|
||||||
|
|
||||||
|
# def on_connect(self, client, userdata, flags, rc, properties):
|
||||||
|
# if rc == 0:
|
||||||
|
# print("Connection to MQTT Broker successful!")
|
||||||
|
# self.connection_status_changed.emit(True, "✅ Connected")
|
||||||
|
# self.connected.emit()
|
||||||
|
# # The app is connected, so we should NOT emit disconnected signals here.
|
||||||
|
# else:
|
||||||
|
# print(f"Failed to connect, return code {rc}\n")
|
||||||
|
# self.connection_status_changed.emit(False, f"❌ Connection failed (Code: {rc})")
|
||||||
|
# self.disconnected.emit() # Connection failed, so we are disconnected
|
||||||
|
|
||||||
|
# def on_disconnect(self, client, userdata, flags, rc, properties):
|
||||||
|
# print("Disconnected from MQTT Broker.")
|
||||||
|
# # Correctly emit signals for a disconnection
|
||||||
|
# # Change the icon in the line below from 🔌 to 🔴 ❌ 🚫 💔
|
||||||
|
# self.connection_status_changed.emit(False, "💔 Disconnected")
|
||||||
|
# self.disconnected.emit()
|
||||||
|
# self.stop_logging_signal.emit() # It's appropriate to stop logging here
|
||||||
|
|
||||||
|
# def on_message(self, client, userdata, msg):
|
||||||
|
# # print(f"Received {len(msg.payload)} bytes of binary data from topic `{msg.topic}`")
|
||||||
|
# self.message_received.emit(msg.topic, msg.payload)
|
||||||
|
|
||||||
|
# # --- MODIFIED connect_to_broker METHOD ---
|
||||||
|
# def connect_to_broker(self):
|
||||||
|
# print(f"Attempting to connect to {self.broker}:{self.port}...")
|
||||||
|
# try:
|
||||||
|
# self.client.connect(self.broker, self.port, 120)
|
||||||
|
# print(f"Attempting to connect to {self.broker}:{self.port}...")
|
||||||
|
# self.client.loop_start()
|
||||||
|
# except socket.gaierror:
|
||||||
|
# msg = "Host not found. Check internet."
|
||||||
|
# print(f"❌ Connection Error: {msg}")
|
||||||
|
# self.connection_status_changed.emit(False, f"❌ {msg}")
|
||||||
|
# except (socket.error, TimeoutError):
|
||||||
|
# msg = "Connection failed. Server offline?"
|
||||||
|
# print(f"❌ Connection Error: {msg}")
|
||||||
|
# self.connection_status_changed.emit(False, f"❌ {msg}")
|
||||||
|
# except Exception as e:
|
||||||
|
# msg = f"An unexpected error occurred: {e}"
|
||||||
|
# print(f"❌ {msg}")
|
||||||
|
# self.connection_status_changed.emit(False, f"❌ Error")
|
||||||
|
|
||||||
|
|
||||||
|
# def run(self):
|
||||||
|
# """
|
||||||
|
# Connects to the broker and starts the network loop.
|
||||||
|
# Handles all common connection errors gracefully.
|
||||||
|
# """
|
||||||
|
# print(f"Attempting to connect to {self.broker}:{self.port}...")
|
||||||
|
# try:
|
||||||
|
# # 1. Attempt to connect
|
||||||
|
# # print(f"Attempting to connect to {self.broker}:{self.port}...")
|
||||||
|
# self.client.connect(self.broker, self.port, 120)
|
||||||
|
|
||||||
|
# # 2. Run the blocking network loop
|
||||||
|
# # This will run until self.client.disconnect() is called
|
||||||
|
# self.client.loop_forever()
|
||||||
|
|
||||||
|
# except socket.gaierror:
|
||||||
|
# msg = "Host not found. Check the broker address or your internet connection."
|
||||||
|
# print(f"❌ {msg}")
|
||||||
|
# self.connection_error.emit(msg) # Report error to the main window
|
||||||
|
|
||||||
|
# except (socket.error, ConnectionRefusedError):
|
||||||
|
# msg = "Connection refused. Is the server offline or the port incorrect?"
|
||||||
|
# print(f"❌ {msg}")
|
||||||
|
# self.connection_error.emit(msg)
|
||||||
|
|
||||||
|
# except TimeoutError:
|
||||||
|
# msg = "Connection timed out. The server is not responding."
|
||||||
|
# print(f"❌ {msg}")
|
||||||
|
# self.connection_error.emit(msg)
|
||||||
|
|
||||||
|
# except Exception as e:
|
||||||
|
# # Catch any other unexpected errors during connection or loop
|
||||||
|
# msg = f"An unexpected error occurred: {e}"
|
||||||
|
# print(f"❌ {msg}")
|
||||||
|
# self.connection_error.emit(msg)
|
||||||
|
|
||||||
|
# # def on_subscribe(self, client, userdata, mid, reason_code_list, properties):
|
||||||
|
# # """Callback function for when the broker responds to a subscription request."""
|
||||||
|
# # if reason_code_list[0].is_failure:
|
||||||
|
# # print(f"❌ Broker rejected subscription: {reason_code_list[0]}")
|
||||||
|
# # else:
|
||||||
|
# # print(f"✅ Broker accepted subscription with QoS: {reason_code_list[0].value}")
|
||||||
|
|
||||||
|
# # --- (The rest of the file remains the same) ---
|
||||||
|
# @pyqtSlot()
|
||||||
|
# def disconnect_from_broker(self):
|
||||||
|
# """Stops the MQTT client's network loop."""
|
||||||
|
# if self.client:
|
||||||
|
# self.client.loop_stop()
|
||||||
|
# self.client.disconnect()
|
||||||
|
# print("Stopping MQTT network loop.")
|
||||||
|
|
||||||
|
# def subscribe_to_topic(self, topic): # Add qos parameter
|
||||||
|
# print(f"Subscribing to topic: {topic}")
|
||||||
|
# self.client.subscribe(topic)
|
||||||
|
|
||||||
|
# def publish_message(self, topic, payload):
|
||||||
|
# self.client.publish(topic, payload)
|
||||||
|
|
||||||
|
# def cleanup(self):
|
||||||
|
# print("Stopping MQTT network loop.")
|
||||||
|
# self.client.loop_stop()
|
||||||
|
|
||||||
|
|
||||||
# In core/mqtt_client.py
|
# In core/mqtt_client.py
|
||||||
import socket
|
import socket
|
||||||
from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot
|
from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot
|
||||||
import paho.mqtt.client as mqtt
|
import paho.mqtt.client as mqtt
|
||||||
import uuid
|
|
||||||
|
|
||||||
class MqttClient(QObject):
|
class MqttClient(QObject):
|
||||||
# --- MODIFIED SIGNAL: Now sends a bool and a string ---
|
# Sends connection state (bool) and a message (str)
|
||||||
connection_status_changed = pyqtSignal(bool, str)
|
connection_status_changed = pyqtSignal(bool, str)
|
||||||
message_received = pyqtSignal(str, bytes)
|
# Generic signals for success or failure/disconnection
|
||||||
connection_error = pyqtSignal(str)
|
|
||||||
stop_logging_signal = pyqtSignal()
|
|
||||||
connected = pyqtSignal()
|
connected = pyqtSignal()
|
||||||
disconnected = pyqtSignal()
|
disconnected = pyqtSignal()
|
||||||
|
stop_logging_signal = pyqtSignal()
|
||||||
|
# Sends topic (str) and payload (bytes) when a message is received
|
||||||
|
message_received = pyqtSignal(str, bytes)
|
||||||
|
|
||||||
def __init__(self, broker, port, user, password, client_id):
|
def __init__(self, broker, port, user, password, client_id):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
@ -25,112 +162,55 @@ class MqttClient(QObject):
|
||||||
self.client.on_connect = self.on_connect
|
self.client.on_connect = self.on_connect
|
||||||
self.client.on_disconnect = self.on_disconnect
|
self.client.on_disconnect = self.on_disconnect
|
||||||
self.client.on_message = self.on_message
|
self.client.on_message = self.on_message
|
||||||
# self.client.on_subscribe = self.on_subscribe
|
|
||||||
|
|
||||||
def on_connect(self, client, userdata, flags, rc, properties):
|
def on_connect(self, client, userdata, flags, rc, properties):
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
print("Connection to MQTT Broker successful!")
|
print("Connection to MQTT Broker successful!")
|
||||||
self.connection_status_changed.emit(True, "✅ Connected")
|
self.connection_status_changed.emit(True, "✅ Connected")
|
||||||
self.connected.emit()
|
self.connected.emit()
|
||||||
# The app is connected, so we should NOT emit disconnected signals here.
|
|
||||||
else:
|
else:
|
||||||
print(f"Failed to connect, return code {rc}\n")
|
print(f"Failed to connect, return code {rc}\n")
|
||||||
self.connection_status_changed.emit(False, f"❌ Connection failed (Code: {rc})")
|
self.connection_status_changed.emit(False, f"❌ Connection failed (Code: {rc})")
|
||||||
self.disconnected.emit() # Connection failed, so we are disconnected
|
self.disconnected.emit()
|
||||||
|
|
||||||
def on_disconnect(self, client, userdata, flags, rc, properties):
|
def on_disconnect(self, client, userdata, flags, rc, properties):
|
||||||
print("Disconnected from MQTT Broker.")
|
print("Disconnected from MQTT Broker.")
|
||||||
# Correctly emit signals for a disconnection
|
|
||||||
# Change the icon in the line below from 🔌 to 🔴 ❌ 🚫 💔
|
|
||||||
self.connection_status_changed.emit(False, "💔 Disconnected")
|
self.connection_status_changed.emit(False, "💔 Disconnected")
|
||||||
self.disconnected.emit()
|
self.disconnected.emit()
|
||||||
self.stop_logging_signal.emit() # It's appropriate to stop logging here
|
self.stop_logging_signal.emit()
|
||||||
|
|
||||||
def on_message(self, client, userdata, msg):
|
def on_message(self, client, userdata, msg):
|
||||||
# print(f"Received {len(msg.payload)} bytes of binary data from topic `{msg.topic}`")
|
|
||||||
self.message_received.emit(msg.topic, msg.payload)
|
self.message_received.emit(msg.topic, msg.payload)
|
||||||
|
|
||||||
# --- MODIFIED connect_to_broker METHOD ---
|
@pyqtSlot()
|
||||||
def connect_to_broker(self):
|
def connect_to_broker(self):
|
||||||
print(f"Attempting to connect to {self.broker}:{self.port}...")
|
print(f"Attempting to connect to {self.broker}:{self.port}...")
|
||||||
try:
|
try:
|
||||||
self.client.connect(self.broker, self.port, 120)
|
self.client.connect(self.broker, self.port, 60)
|
||||||
print(f"Attempting to connect to {self.broker}:{self.port}...")
|
|
||||||
self.client.loop_start()
|
self.client.loop_start()
|
||||||
except socket.gaierror:
|
except socket.gaierror:
|
||||||
msg = "Host not found. Check internet."
|
msg = "Host not found. Check broker address or your internet connection."
|
||||||
print(f"❌ Connection Error: {msg}")
|
print(f"❌ Connection Error: {msg}")
|
||||||
self.connection_status_changed.emit(False, f"❌ {msg}")
|
self.connection_status_changed.emit(False, msg)
|
||||||
except (socket.error, TimeoutError):
|
except (socket.error, ConnectionRefusedError, TimeoutError):
|
||||||
msg = "Connection failed. Server offline?"
|
msg = "Connection failed. Is the server offline or the port incorrect?"
|
||||||
print(f"❌ Connection Error: {msg}")
|
print(f"❌ Connection Error: {msg}")
|
||||||
self.connection_status_changed.emit(False, f"❌ {msg}")
|
self.connection_status_changed.emit(False, msg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = f"An unexpected error occurred: {e}"
|
msg = f"An unexpected error occurred: {e}"
|
||||||
print(f"❌ {msg}")
|
print(f"❌ {msg}")
|
||||||
self.connection_status_changed.emit(False, f"❌ Error")
|
self.connection_status_changed.emit(False, f"Error: {e}")
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""
|
|
||||||
Connects to the broker and starts the network loop.
|
|
||||||
Handles all common connection errors gracefully.
|
|
||||||
"""
|
|
||||||
print(f"Attempting to connect to {self.broker}:{self.port}...")
|
|
||||||
try:
|
|
||||||
# 1. Attempt to connect
|
|
||||||
# print(f"Attempting to connect to {self.broker}:{self.port}...")
|
|
||||||
self.client.connect(self.broker, self.port, 120)
|
|
||||||
|
|
||||||
# 2. Run the blocking network loop
|
|
||||||
# This will run until self.client.disconnect() is called
|
|
||||||
self.client.loop_forever()
|
|
||||||
|
|
||||||
except socket.gaierror:
|
|
||||||
msg = "Host not found. Check the broker address or your internet connection."
|
|
||||||
print(f"❌ {msg}")
|
|
||||||
self.connection_error.emit(msg) # Report error to the main window
|
|
||||||
|
|
||||||
except (socket.error, ConnectionRefusedError):
|
|
||||||
msg = "Connection refused. Is the server offline or the port incorrect?"
|
|
||||||
print(f"❌ {msg}")
|
|
||||||
self.connection_error.emit(msg)
|
|
||||||
|
|
||||||
except TimeoutError:
|
|
||||||
msg = "Connection timed out. The server is not responding."
|
|
||||||
print(f"❌ {msg}")
|
|
||||||
self.connection_error.emit(msg)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
# Catch any other unexpected errors during connection or loop
|
|
||||||
msg = f"An unexpected error occurred: {e}"
|
|
||||||
print(f"❌ {msg}")
|
|
||||||
self.connection_error.emit(msg)
|
|
||||||
|
|
||||||
# def on_subscribe(self, client, userdata, mid, reason_code_list, properties):
|
|
||||||
# """Callback function for when the broker responds to a subscription request."""
|
|
||||||
# if reason_code_list[0].is_failure:
|
|
||||||
# print(f"❌ Broker rejected subscription: {reason_code_list[0]}")
|
|
||||||
# else:
|
|
||||||
# print(f"✅ Broker accepted subscription with QoS: {reason_code_list[0].value}")
|
|
||||||
|
|
||||||
# --- (The rest of the file remains the same) ---
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def disconnect_from_broker(self):
|
def disconnect_from_broker(self):
|
||||||
"""Stops the MQTT client's network loop."""
|
|
||||||
if self.client:
|
if self.client:
|
||||||
self.client.loop_stop()
|
self.client.loop_stop()
|
||||||
self.client.disconnect()
|
self.client.disconnect()
|
||||||
print("Stopping MQTT network loop.")
|
print("Stopping MQTT network loop.")
|
||||||
|
|
||||||
def subscribe_to_topic(self, topic): # Add qos parameter
|
def subscribe_to_topic(self, topic):
|
||||||
print(f"Subscribing to topic: {topic}")
|
print(f"Subscribing to topic: {topic}")
|
||||||
self.client.subscribe(topic)
|
self.client.subscribe(topic)
|
||||||
|
|
||||||
def publish_message(self, topic, payload):
|
def publish_message(self, topic, payload):
|
||||||
self.client.publish(topic, payload)
|
self.client.publish(topic, payload)
|
||||||
|
|
||||||
def cleanup(self):
|
|
||||||
print("Stopping MQTT network loop.")
|
|
||||||
self.client.loop_stop()
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -535,7 +535,7 @@ class MainWindow(QMainWindow):
|
||||||
self.theme_button.setText("🌙")
|
self.theme_button.setText("🌙")
|
||||||
self.theme_button.setToolTip("Switch to Dark Theme")
|
self.theme_button.setToolTip("Switch to Dark Theme")
|
||||||
self.setStyleSheet(get_light_theme_styles(self.scale_factor))
|
self.setStyleSheet(get_light_theme_styles(self.scale_factor))
|
||||||
self.timestamp_label.setStyleSheet("color: #ecf0f1; background-color: transparent;")
|
self.status_bar_device_id_label.setStyleSheet("color: #ecf0f1; background-color: transparent;")
|
||||||
self.apply_config_tab_styles()
|
self.apply_config_tab_styles()
|
||||||
|
|
||||||
def _on_fade_finished(self, overlay):
|
def _on_fade_finished(self, overlay):
|
||||||
|
|
@ -592,10 +592,18 @@ class MainWindow(QMainWindow):
|
||||||
logo_label = QLabel("BSS Dashboard")
|
logo_label = QLabel("BSS Dashboard")
|
||||||
logo_label.setFont(QFont("Arial", max(9, int(11 * self.scale_factor)), QFont.Weight.Bold))
|
logo_label.setFont(QFont("Arial", max(9, int(11 * self.scale_factor)), QFont.Weight.Bold))
|
||||||
logo_label.setStyleSheet("color: #ecf0f1; background-color: transparent;")
|
logo_label.setStyleSheet("color: #ecf0f1; background-color: transparent;")
|
||||||
self.timestamp_label = QLabel("Last Update: N/A")
|
|
||||||
self.timestamp_label.setFont(QFont("Arial", max(8, int(9 * self.scale_factor))))
|
self.status_bar_device_id_label = QLabel("Device ID: --- |") # Create the new label
|
||||||
|
self.status_bar_device_id_label.setFont(QFont("Arial", max(10, int(9 * self.scale_factor))))
|
||||||
|
self.status_bar_device_id_label.setStyleSheet("color: #000000; background-color: transparent; margin-left: 10px;")
|
||||||
|
|
||||||
|
self.status_bar_timestamp_label = QLabel("Last Update: --- ") # Create the new timestamp label
|
||||||
|
self.status_bar_timestamp_label.setFont(QFont("Arial", max(10, int(9 * self.scale_factor))))
|
||||||
|
self.status_bar_timestamp_label.setStyleSheet("color: #ffffffff; background-color: transparent; margin-left: 10px;")
|
||||||
|
|
||||||
left_layout.addWidget(logo_label)
|
left_layout.addWidget(logo_label)
|
||||||
left_layout.addWidget(self.timestamp_label)
|
left_layout.addWidget(self.status_bar_device_id_label)
|
||||||
|
left_layout.addWidget(self.status_bar_timestamp_label)
|
||||||
left_layout.addStretch()
|
left_layout.addStretch()
|
||||||
|
|
||||||
logo_path = resource_path("logo/vec_logo_svg.svg")
|
logo_path = resource_path("logo/vec_logo_svg.svg")
|
||||||
|
|
@ -778,6 +786,17 @@ class MainWindow(QMainWindow):
|
||||||
|
|
||||||
top_bar_layout = QHBoxLayout()
|
top_bar_layout = QHBoxLayout()
|
||||||
|
|
||||||
|
device_id_label = QLabel("DEVICE ID:")
|
||||||
|
device_id_label.setObjectName("TimestampTitleLabel")
|
||||||
|
top_bar_layout.addWidget(device_id_label)
|
||||||
|
|
||||||
|
self.device_id_display_field = QLineEdit("---- ---- ---- ---- ----")
|
||||||
|
self.device_id_display_field.setReadOnly(True)
|
||||||
|
self.device_id_display_field.setObjectName("TimestampDataField")
|
||||||
|
top_bar_layout.addWidget(self.device_id_display_field)
|
||||||
|
|
||||||
|
top_bar_layout.addSpacerItem(QSpacerItem(20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum))
|
||||||
|
|
||||||
ts_label = QLabel("LAST RECV TS:")
|
ts_label = QLabel("LAST RECV TS:")
|
||||||
ts_label.setObjectName("TimestampTitleLabel")
|
ts_label.setObjectName("TimestampTitleLabel")
|
||||||
top_bar_layout.addWidget(ts_label)
|
top_bar_layout.addWidget(ts_label)
|
||||||
|
|
@ -787,6 +806,7 @@ class MainWindow(QMainWindow):
|
||||||
|
|
||||||
self.last_recv_ts_field.setObjectName("TimestampDataField")
|
self.last_recv_ts_field.setObjectName("TimestampDataField")
|
||||||
top_bar_layout.addWidget(self.last_recv_ts_field)
|
top_bar_layout.addWidget(self.last_recv_ts_field)
|
||||||
|
|
||||||
top_bar_layout.addSpacerItem(QSpacerItem(20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum))
|
top_bar_layout.addSpacerItem(QSpacerItem(20, 20, QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Minimum))
|
||||||
|
|
||||||
# top_bar_layout.addWidget(QLabel("Backup Supply:"))
|
# top_bar_layout.addWidget(QLabel("Backup Supply:"))
|
||||||
|
|
@ -809,6 +829,7 @@ class MainWindow(QMainWindow):
|
||||||
refresh_btn = QPushButton("⟳ Refresh")
|
refresh_btn = QPushButton("⟳ Refresh")
|
||||||
refresh_btn.setObjectName("RefreshButton")
|
refresh_btn.setObjectName("RefreshButton")
|
||||||
refresh_btn.clicked.connect(self.reset_dashboard_ui)
|
refresh_btn.clicked.connect(self.reset_dashboard_ui)
|
||||||
|
refresh_btn.clicked.connect(self.clear_instance_log)
|
||||||
top_bar_layout.addWidget(refresh_btn)
|
top_bar_layout.addWidget(refresh_btn)
|
||||||
|
|
||||||
reset_btn = QPushButton("Station Reset")
|
reset_btn = QPushButton("Station Reset")
|
||||||
|
|
@ -976,14 +997,18 @@ class MainWindow(QMainWindow):
|
||||||
cursor.removeSelectedText()
|
cursor.removeSelectedText()
|
||||||
|
|
||||||
def setup_logs_ui(self):
|
def setup_logs_ui(self):
|
||||||
layout = QHBoxLayout(self.logs_tab)
|
# Use a main vertical layout to hold the logs and the new button
|
||||||
|
main_layout = QVBoxLayout(self.logs_tab)
|
||||||
|
|
||||||
|
# Create a horizontal layout for the two log panels
|
||||||
|
logs_layout = QHBoxLayout()
|
||||||
|
|
||||||
# --- Setup the Request Logs area ---
|
# --- Setup the Request Logs area ---
|
||||||
request_group = QGroupBox("Request Logs (Dashboard -> MQTT Server)")
|
request_group = QGroupBox("Request Logs (Dashboard -> MQTT Server)")
|
||||||
request_layout = QVBoxLayout(request_group)
|
request_layout = QVBoxLayout(request_group)
|
||||||
self.request_log_area = QPlainTextEdit()
|
self.request_log_area = QPlainTextEdit()
|
||||||
self.request_log_area.setReadOnly(True)
|
self.request_log_area.setReadOnly(True)
|
||||||
self.request_log_area.setObjectName("LogPanel")
|
self.request_log_area.setObjectName("LogPanel")
|
||||||
request_layout.addWidget(self.request_log_area)
|
request_layout.addWidget(self.request_log_area)
|
||||||
|
|
||||||
# --- Setup the Event Logs area ---
|
# --- Setup the Event Logs area ---
|
||||||
|
|
@ -994,8 +1019,21 @@ class MainWindow(QMainWindow):
|
||||||
self.event_log_area.setObjectName("LogPanel")
|
self.event_log_area.setObjectName("LogPanel")
|
||||||
event_layout.addWidget(self.event_log_area)
|
event_layout.addWidget(self.event_log_area)
|
||||||
|
|
||||||
layout.addWidget(request_group)
|
logs_layout.addWidget(request_group)
|
||||||
layout.addWidget(event_group)
|
logs_layout.addWidget(event_group)
|
||||||
|
|
||||||
|
# --- ADD THIS BUTTON ---
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
clear_logs_btn = QPushButton("Clear All Logs")
|
||||||
|
clear_logs_btn.setObjectName("ClearLogButton")
|
||||||
|
clear_logs_btn.clicked.connect(self.clear_all_logs)
|
||||||
|
button_layout.addStretch() # Pushes the button to the right
|
||||||
|
button_layout.addWidget(clear_logs_btn)
|
||||||
|
# --- END OF ADDED CODE ---
|
||||||
|
|
||||||
|
# Add both layouts to the main vertical layout
|
||||||
|
main_layout.addLayout(logs_layout)
|
||||||
|
main_layout.addLayout(button_layout)
|
||||||
|
|
||||||
def _create_main_status_field(self):
|
def _create_main_status_field(self):
|
||||||
field = QLineEdit()
|
field = QLineEdit()
|
||||||
|
|
@ -1057,8 +1095,6 @@ class MainWindow(QMainWindow):
|
||||||
|
|
||||||
# --- THIS IS THE CRITICAL METHOD TO UPDATE ---
|
# --- THIS IS THE CRITICAL METHOD TO UPDATE ---
|
||||||
def on_message_received(self, topic, payload):
|
def on_message_received(self, topic, payload):
|
||||||
now = datetime.datetime.now()
|
|
||||||
self.timestamp_label.setText(f"Last Update: {now.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
msg_type = topic.split('/')[-1]
|
msg_type = topic.split('/')[-1]
|
||||||
|
|
@ -1070,6 +1106,8 @@ class MainWindow(QMainWindow):
|
||||||
# print(data_dict)
|
# print(data_dict)
|
||||||
self._log_periodic_to_terminal(decoded_payload, data_dict)
|
self._log_periodic_to_terminal(decoded_payload, data_dict)
|
||||||
self.update_main_dashboard(data_dict)
|
self.update_main_dashboard(data_dict)
|
||||||
|
now = datetime.datetime.fromtimestamp(data_dict.get('ts', datetime.datetime.now().timestamp())).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
self.status_bar_timestamp_label.setText(f"| Last Update: {now}")
|
||||||
if self.save_logs_checkbox.isChecked():
|
if self.save_logs_checkbox.isChecked():
|
||||||
log_payload_str = json.dumps(data_dict)
|
log_payload_str = json.dumps(data_dict)
|
||||||
self.log_data_signal.emit([now, topic, data_dict, payload])
|
self.log_data_signal.emit([now, topic, data_dict, payload])
|
||||||
|
|
@ -1244,30 +1282,68 @@ class MainWindow(QMainWindow):
|
||||||
self.mqtt_client.publish_message(topic, serialized_payload)
|
self.mqtt_client.publish_message(topic, serialized_payload)
|
||||||
|
|
||||||
def handle_open_door(self, chamber_num):
|
def handle_open_door(self, chamber_num):
|
||||||
print(f"Requesting to open door for chamber {chamber_num}...")
|
# --- ADDED: Confirmation Dialog ---
|
||||||
request = rpcRequest(
|
reply = QMessageBox.question(self, 'Confirm Open Door',
|
||||||
ts=int(time.time()),
|
f"Are you sure you want to open the door for Chamber {chamber_num}?",
|
||||||
jobId=f"job_{int(time.time())}",
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||||
jobType=jobType_e.JOBTYPE_GATE_OPEN_CLOSE
|
QMessageBox.StandardButton.No)
|
||||||
)
|
|
||||||
request.slotInfo.slotId = chamber_num
|
# --- ADDED: Check the user's reply ---
|
||||||
request.slotInfo.state = 1
|
if reply == QMessageBox.StandardButton.Yes:
|
||||||
self._send_rpc_request(request)
|
# If user confirms, proceed with sending the command
|
||||||
|
print(f"Requesting to open door for chamber {chamber_num}...")
|
||||||
|
request = rpcRequest(
|
||||||
|
ts=int(time.time()),
|
||||||
|
jobId=f"job_{int(time.time())}",
|
||||||
|
jobType=jobType_e.JOBTYPE_GATE_OPEN_CLOSE
|
||||||
|
)
|
||||||
|
request.slotInfo.slotId = chamber_num
|
||||||
|
request.slotInfo.state = 1 # State 1 for OPEN
|
||||||
|
self._send_rpc_request(request)
|
||||||
|
else:
|
||||||
|
# If user cancels, log the cancellation
|
||||||
|
self.log_to_instance_view(f"Open Door command for Chamber {chamber_num} cancelled.")
|
||||||
|
|
||||||
|
# def handle_charger_control(self, chamber_num, state):
|
||||||
|
# action = "ON" if state else "OFF"
|
||||||
|
# print(f"Requesting to turn charger {action} for chamber {chamber_num}...")
|
||||||
|
# request = rpcRequest(
|
||||||
|
# ts=int(time.time()),
|
||||||
|
# jobId=f"job_{int(time.time())}",
|
||||||
|
# jobType=jobType_e.JOBTYPE_CHARGER_ENABLE_DISABLE
|
||||||
|
# )
|
||||||
|
# request.slotInfo.slotId = chamber_num
|
||||||
|
# request.slotInfo.state = 1 if state else 0
|
||||||
|
# self._send_rpc_request(request)
|
||||||
|
|
||||||
def handle_charger_control(self, chamber_num, state):
|
def handle_charger_control(self, chamber_num, state):
|
||||||
action = "ON" if state else "OFF"
|
action = "ON" if state else "OFF"
|
||||||
print(f"Requesting to turn charger {action} for chamber {chamber_num}...")
|
|
||||||
request = rpcRequest(
|
# --- ADDED: Confirmation Dialog ---
|
||||||
ts=int(time.time()),
|
reply = QMessageBox.question(self, f'Confirm Charger Control',
|
||||||
jobId=f"job_{int(time.time())}",
|
f"Are you sure you want to turn the charger {action} for Chamber {chamber_num}?",
|
||||||
jobType=jobType_e.JOBTYPE_CHARGER_ENABLE_DISABLE
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
||||||
)
|
QMessageBox.StandardButton.No)
|
||||||
request.slotInfo.slotId = chamber_num
|
|
||||||
request.slotInfo.state = 1 if state else 0
|
# --- ADDED: Check the user's reply ---
|
||||||
self._send_rpc_request(request)
|
if reply == QMessageBox.StandardButton.Yes:
|
||||||
|
# If user confirms, proceed with sending the command
|
||||||
|
print(f"Requesting to turn charger {action} for chamber {chamber_num}...")
|
||||||
|
request = rpcRequest(
|
||||||
|
ts=int(time.time()),
|
||||||
|
jobId=f"job_{int(time.time())}",
|
||||||
|
jobType=jobType_e.JOBTYPE_CHARGER_ENABLE_DISABLE
|
||||||
|
)
|
||||||
|
request.slotInfo.slotId = chamber_num
|
||||||
|
request.slotInfo.state = 1 if state else 0
|
||||||
|
self._send_rpc_request(request)
|
||||||
|
else:
|
||||||
|
# If user cancels, log the cancellation
|
||||||
|
self.log_to_instance_view(f"Charger {action} command for Chamber {chamber_num} cancelled.")
|
||||||
|
|
||||||
def reset_dashboard_ui(self):
|
def reset_dashboard_ui(self):
|
||||||
self.log_request("INFO", "Dashboard UI cleared by user.")
|
self.log_request("INFO", "Dashboard UI cleared by user.")
|
||||||
|
self.device_id_display_field.setText("---- ---- ---- ---- ----")
|
||||||
self.last_recv_ts_field.setText("No Data")
|
self.last_recv_ts_field.setText("No Data")
|
||||||
self.sdc_field.setText("")
|
self.sdc_field.setText("")
|
||||||
for chamber in self.chamber_widgets:
|
for chamber in self.chamber_widgets:
|
||||||
|
|
@ -1286,9 +1362,9 @@ class MainWindow(QMainWindow):
|
||||||
print("Cleaning up previous MQTT thread...")
|
print("Cleaning up previous MQTT thread...")
|
||||||
if self.mqtt_client:
|
if self.mqtt_client:
|
||||||
self.mqtt_client.disconnect_from_broker()
|
self.mqtt_client.disconnect_from_broker()
|
||||||
self.mqtt_client.cleanup()
|
# self.mqtt_client.cleanup()
|
||||||
self.mqtt_thread.quit()
|
self.mqtt_thread.quit()
|
||||||
self.mqtt_thread.wait()
|
self.mqtt_thread.wait(1000)
|
||||||
if self.save_logs_checkbox.isChecked():
|
if self.save_logs_checkbox.isChecked():
|
||||||
self.start_csv_logger()
|
self.start_csv_logger()
|
||||||
self.reset_dashboard_ui()
|
self.reset_dashboard_ui()
|
||||||
|
|
@ -1302,7 +1378,7 @@ class MainWindow(QMainWindow):
|
||||||
try:
|
try:
|
||||||
port = int(self.port_input.text())
|
port = int(self.port_input.text())
|
||||||
except ValueError:
|
except ValueError:
|
||||||
self.timestamp_label.setText("Error: Port must be a number.")
|
self.status_bar_device_id_label.setText("Error: Port must be a number.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# ==========================================================
|
# ==========================================================
|
||||||
|
|
@ -1378,6 +1454,18 @@ class MainWindow(QMainWindow):
|
||||||
self.csv_logger = None
|
self.csv_logger = None
|
||||||
self.logger_thread = None
|
self.logger_thread = None
|
||||||
|
|
||||||
|
def clear_instance_log(self):
|
||||||
|
"""Clears the text in the instance log area."""
|
||||||
|
if self.instance_log_area:
|
||||||
|
self.instance_log_area.clear()
|
||||||
|
|
||||||
|
def clear_all_logs(self):
|
||||||
|
"""Clears the text in both the request and event log areas."""
|
||||||
|
if self.request_log_area:
|
||||||
|
self.request_log_area.clear()
|
||||||
|
if self.event_log_area:
|
||||||
|
self.event_log_area.clear()
|
||||||
|
|
||||||
def on_connection_status_changed(self, is_connected, message):
|
def on_connection_status_changed(self, is_connected, message):
|
||||||
"""Handles connection status updates from the MQTT client."""
|
"""Handles connection status updates from the MQTT client."""
|
||||||
self.connect_button.setEnabled(not is_connected)
|
self.connect_button.setEnabled(not is_connected)
|
||||||
|
|
@ -1385,7 +1473,7 @@ class MainWindow(QMainWindow):
|
||||||
self.set_config_inputs_enabled(not is_connected)
|
self.set_config_inputs_enabled(not is_connected)
|
||||||
|
|
||||||
# Set the text of the label to the message from the client
|
# Set the text of the label to the message from the client
|
||||||
self.timestamp_label.setText(message)
|
self.status_bar_device_id_label.setText(message)
|
||||||
|
|
||||||
self.log_to_instance_view(message.strip("✅❌🔌🔴 "))
|
self.log_to_instance_view(message.strip("✅❌🔌🔴 "))
|
||||||
|
|
||||||
|
|
@ -1393,6 +1481,8 @@ class MainWindow(QMainWindow):
|
||||||
client_id = self.client_id_input.text()
|
client_id = self.client_id_input.text()
|
||||||
version = self.version_input.text()
|
version = self.version_input.text()
|
||||||
device_id = self.device_id_input.text()
|
device_id = self.device_id_input.text()
|
||||||
|
|
||||||
|
self.status_bar_device_id_label.setText(f"Device ID: {device_id}")
|
||||||
|
|
||||||
periodic_topic = f"VEC/{client_id}/{version}/{device_id}/PERIODIC"
|
periodic_topic = f"VEC/{client_id}/{version}/{device_id}/PERIODIC"
|
||||||
events_topic = f"VEC/{client_id}/{version}/{device_id}/EVENTS"
|
events_topic = f"VEC/{client_id}/{version}/{device_id}/EVENTS"
|
||||||
|
|
@ -1409,10 +1499,20 @@ class MainWindow(QMainWindow):
|
||||||
|
|
||||||
self.tabs.setCurrentWidget(self.main_tab)
|
self.tabs.setCurrentWidget(self.main_tab)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
QMessageBox.critical(self, "Connection Failed", message)
|
||||||
|
|
||||||
|
self.status_bar_device_id_label.setText("Device ID: --- |") # Clear the device ID label on disconnect
|
||||||
|
self.status_bar_timestamp_label.setText("Last Update: ---") # Clear the timestamp label on disconnect
|
||||||
|
|
||||||
|
|
||||||
def update_main_dashboard(self, data):
|
def update_main_dashboard(self, data):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ts = datetime.datetime.fromtimestamp(data.get('ts', datetime.datetime.now().timestamp())).strftime('%Y-%m-%d %H:%M:%S')
|
ts = datetime.datetime.fromtimestamp(data.get('ts', datetime.datetime.now().timestamp())).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
device_id = data.get("deviceId", "---- ---- ---- ---- ----")
|
||||||
|
self.device_id_display_field.setText(device_id)
|
||||||
self.last_recv_ts_field.setText(ts)
|
self.last_recv_ts_field.setText(ts)
|
||||||
slot_payloads = data.get("slotLevelPayload", [])
|
slot_payloads = data.get("slotLevelPayload", [])
|
||||||
for i, slot_data in enumerate(slot_payloads):
|
for i, slot_data in enumerate(slot_payloads):
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,8 @@ def get_light_theme_styles(scale=1.0):
|
||||||
#ChamberChgOffButton:hover {{ background-color: #d42318; }}
|
#ChamberChgOffButton:hover {{ background-color: #d42318; }}
|
||||||
|
|
||||||
/* Status & alarms */
|
/* Status & alarms */
|
||||||
QLabel[status="present"] {{ background-color: #2ecc71; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
|
QLabel[status="present"] {{ background-color: #2ecc71; color: white; border-radius: {int(4*scale)}px; padding: {int(6*scale)}px; }}
|
||||||
QLabel[status="absent"] {{ background-color: #e74c3c; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
|
QLabel[status="absent"] {{ background-color: #e74c3c; color: white; border-radius: {int(4*scale)}px; padding: {int(6*scale)}px; }}
|
||||||
QLabel[alarm="active"] {{ background-color: #e74c3c; color: white; font-weight: bold; border-radius: {int(4*scale)}px; padding: {int(2*scale)}px; }}
|
QLabel[alarm="active"] {{ background-color: #e74c3c; color: white; font-weight: bold; border-radius: {int(4*scale)}px; padding: {int(2*scale)}px; }}
|
||||||
QLabel[alarm="inactive"] {{ background-color: transparent; color: black; }}
|
QLabel[alarm="inactive"] {{ background-color: transparent; color: black; }}
|
||||||
QGroupBox#ChamberWidget {{ border: 2px solid #007bff; }}
|
QGroupBox#ChamberWidget {{ border: 2px solid #007bff; }}
|
||||||
|
|
@ -365,8 +365,8 @@ def get_dark_theme_styles(scale=1.0):
|
||||||
#SendAudioButton {{ background-color: #3498db; color: white; border: none; font-size: {max(10, int(14 * scale))}px; }}
|
#SendAudioButton {{ background-color: #3498db; color: white; border: none; font-size: {max(10, int(14 * scale))}px; }}
|
||||||
#SendAudioButton:hover {{ background-color: #5dade2; }}
|
#SendAudioButton:hover {{ background-color: #5dade2; }}
|
||||||
|
|
||||||
QLabel[status="present"] {{ background-color: #2ecc71; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
|
QLabel[status="present"] {{ background-color: #2ecc71; color: white; border-radius: {int(4*scale)}px; padding: {int(6*scale)}px; }}
|
||||||
QLabel[status="absent"] {{ background-color: #e74c3c; color: white; border-radius: {int(4*scale)}px; padding: {int(3*scale)}px; }}
|
QLabel[status="absent"] {{ background-color: #e74c3c; color: white; border-radius: {int(4*scale)}px; padding: {int(6*scale)}px; }}
|
||||||
QLabel[alarm="active"] {{ background-color: #e74c3c; color: white; font-weight: bold; border-radius: {int(4*scale)}px; padding: {int(2*scale)}px; }}
|
QLabel[alarm="active"] {{ background-color: #e74c3c; color: white; font-weight: bold; border-radius: {int(4*scale)}px; padding: {int(2*scale)}px; }}
|
||||||
QLabel[alarm="inactive"] {{ background-color: transparent; color: #f0f0f0; }}
|
QLabel[alarm="inactive"] {{ background-color: transparent; color: #f0f0f0; }}
|
||||||
QGroupBox#ChamberWidget {{ border: 2px solid #3498db; }}
|
QGroupBox#ChamberWidget {{ border: 2px solid #3498db; }}
|
||||||
|
|
|
||||||
105
ui/widgets.py
105
ui/widgets.py
|
|
@ -102,6 +102,8 @@ class ChamberWidget(QGroupBox):
|
||||||
self.door_status_field.setObjectName("DoorStatusField")
|
self.door_status_field.setObjectName("DoorStatusField")
|
||||||
self.door_status_field.setMaximumWidth(int(140 * scale))
|
self.door_status_field.setMaximumWidth(int(140 * scale))
|
||||||
button_layout.addWidget(self.door_status_field)
|
button_layout.addWidget(self.door_status_field)
|
||||||
|
|
||||||
|
self.door_status_field.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
|
||||||
# Part 2: Buttons
|
# Part 2: Buttons
|
||||||
self.open_door_btn = QPushButton("OPEN DOOR")
|
self.open_door_btn = QPushButton("OPEN DOOR")
|
||||||
|
|
@ -135,92 +137,67 @@ class ChamberWidget(QGroupBox):
|
||||||
return field
|
return field
|
||||||
|
|
||||||
def update_data(self, data):
|
def update_data(self, data):
|
||||||
# Retrieve battery and charger presence with fallback keys
|
# --- Get the status of the battery and charger first ---
|
||||||
battery_present_status = data.get("batteryPresent")
|
battery_is_present = data.get("batteryPresent") == 1
|
||||||
# <-- MODIFIED: Now checks for 'chargerPresent' key from your payload
|
charger_is_present = data.get("chargerPresent") == 1
|
||||||
charger_on_status = data.get("chargerPresent", data.get("chargerOn", data.get("chgStatus", 0)))
|
|
||||||
|
|
||||||
if battery_present_status == 1:
|
# --- Update Battery Status Label ---
|
||||||
|
if battery_is_present:
|
||||||
self.battery_status_label.setText("PRESENT")
|
self.battery_status_label.setText("PRESENT")
|
||||||
self.battery_status_label.setProperty("status", "present")
|
self.battery_status_label.setProperty("status", "present")
|
||||||
else:
|
else:
|
||||||
self.battery_status_label.setText("ABSENT")
|
self.battery_status_label.setText("ABSENT")
|
||||||
self.battery_status_label.setProperty("status", "absent")
|
self.battery_status_label.setProperty("status", "absent")
|
||||||
|
|
||||||
if charger_on_status == 1:
|
# --- Update Charger Status Label ---
|
||||||
|
if charger_is_present:
|
||||||
self.charger_status_label.setText("CHARGER ON")
|
self.charger_status_label.setText("CHARGER ON")
|
||||||
self.charger_status_label.setProperty("status", "present")
|
self.charger_status_label.setProperty("status", "present")
|
||||||
else:
|
else:
|
||||||
self.charger_status_label.setText("CHARGER OFF")
|
self.charger_status_label.setText("CHARGER OFF")
|
||||||
self.charger_status_label.setProperty("status", "absent")
|
self.charger_status_label.setProperty("status", "absent")
|
||||||
|
|
||||||
|
# Re-apply stylesheets for the status labels
|
||||||
for widget in [self.battery_status_label, self.charger_status_label]:
|
for widget in [self.battery_status_label, self.charger_status_label]:
|
||||||
widget.style().unpolish(widget)
|
widget.style().unpolish(widget)
|
||||||
widget.style().polish(widget)
|
widget.style().polish(widget)
|
||||||
|
|
||||||
self.id_field.setText(data.get("batteryIdentification", " "))
|
# --- Conditionally update battery-specific fields ---
|
||||||
self.soc_field.setText(f'{data.get("soc", 0)}%')
|
if battery_is_present:
|
||||||
self.voltage_field.setText(f'{data.get("voltage", 0) / 1000.0:.2f} V')
|
self.soc_field.setText(f'{data.get("soc", 0)}%')
|
||||||
self.temp_field.setText(f'{data.get("batteryMaxTemp", 0) / 10.0:.1f} °C')
|
self.voltage_field.setText(f'{data.get("voltage", 0) / 1000.0:.2f} V')
|
||||||
self.battery_fault_field.setText(str(data.get("batteryFaultCode", 0)))
|
self.temp_field.setText(f'{data.get("batteryMaxTemp", 0) / 10.0:.1f} °C')
|
||||||
|
self.battery_fault_field.setText(str(data.get("batteryFaultCode", 0)))
|
||||||
# <-- MODIFIED: Now checks for 'chargerMaxTemp' key from your payload
|
else:
|
||||||
charger_temp = data.get("chargerMaxTemp", data.get("chargerTemp", data.get("chgTemp", 0)))
|
# Clear the fields if the battery is absent
|
||||||
self.chg_temp_field.setText(f'{charger_temp / 10.0:.1f} °C')
|
self.soc_field.setText(" ")
|
||||||
self.charger_fault_field.setText(str(data.get("chargerFaultCode", 0)))
|
self.voltage_field.setText(" ")
|
||||||
self.current_field.setText(f'{data.get("current", 0) / 1000.0:.2f} A')
|
self.temp_field.setText(" ")
|
||||||
|
self.battery_fault_field.setText(" ")
|
||||||
slot_temp = data.get("slotTemperature", data.get("slotTemp", 0))
|
|
||||||
self.slot_temp_field.setText(f'{slot_temp / 10.0:.1f} °C')
|
|
||||||
|
|
||||||
|
# --- Conditionally update charger-specific fields ---
|
||||||
|
if charger_is_present:
|
||||||
|
charger_temp = data.get("chargerMaxTemp", 0)
|
||||||
|
self.chg_temp_field.setText(f'{charger_temp / 10.0:.1f} °C')
|
||||||
|
self.charger_fault_field.setText(str(data.get("chargerFaultCode", 0)))
|
||||||
|
self.current_field.setText(f'{data.get("current", 0) / 1000.0:.2f} A')
|
||||||
|
else:
|
||||||
|
# Clear the fields if the charger is off/absent
|
||||||
|
self.chg_temp_field.setText(" ")
|
||||||
|
self.charger_fault_field.setText(" ")
|
||||||
|
self.current_field.setText(" ")
|
||||||
|
|
||||||
|
# --- Update fields that are always visible ---
|
||||||
|
self.id_field.setText(data.get("batteryIdentification", " "))
|
||||||
|
slot_temp = data.get("slotTemperature", 0)
|
||||||
|
self.slot_temp_field.setText(f'{slot_temp / 10.0:.1f} °C')
|
||||||
|
|
||||||
door_status = "OPEN" if data.get("doorStatus") == 1 else "CLOSED"
|
door_status = "OPEN" if data.get("doorStatus") == 1 else "CLOSED"
|
||||||
self.door_status_field.setText(door_status)
|
self.door_status_field.setText(door_status)
|
||||||
|
|
||||||
if door_status == "OPEN":
|
if door_status == "OPEN":
|
||||||
self.door_status_field.setStyleSheet("background-color: #2E7D32; color: white; border-radius: 3px;")
|
self.door_status_field.setStyleSheet("background-color: #30ce72; color: white; border-radius: 3px;")
|
||||||
else:
|
else:
|
||||||
self.door_status_field.setStyleSheet("background-color: #C62828; color: white; border-radius: 3px;")
|
self.door_status_field.setStyleSheet("background-color: #fd5443; color: white; border-radius: 3px;")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# def update_data(self, data):
|
|
||||||
# if data.get("batteryPresent") == 1:
|
|
||||||
# self.battery_status_label.setText("PRESENT")
|
|
||||||
# self.battery_status_label.setProperty("status", "present")
|
|
||||||
# else:
|
|
||||||
# self.battery_status_label.setText("ABSENT")
|
|
||||||
# self.battery_status_label.setProperty("status", "absent")
|
|
||||||
|
|
||||||
# if data.get("chargerOn") == 1:
|
|
||||||
# self.charger_status_label.setText("CHARGER ON")
|
|
||||||
# self.charger_status_label.setProperty("status", "present")
|
|
||||||
# else:
|
|
||||||
# self.charger_status_label.setText("CHARGER OFF")
|
|
||||||
# self.charger_status_label.setProperty("status", "absent")
|
|
||||||
|
|
||||||
# for widget in [self.battery_status_label, self.charger_status_label]:
|
|
||||||
# widget.style().unpolish(widget)
|
|
||||||
# widget.style().polish(widget)
|
|
||||||
|
|
||||||
# self.id_field.setText(data.get("batteryIdentification", " "))
|
|
||||||
# self.soc_field.setText(f'{data.get("soc", 0)}%')
|
|
||||||
# self.voltage_field.setText(f'{data.get("voltage", 0) / 1000.0:.2f} V')
|
|
||||||
# self.temp_field.setText(f'{data.get("batteryMaxTemp", 0) / 10.0:.1f} °C')
|
|
||||||
# self.battery_fault_field.setText(str(data.get("batteryFaultCode", 0)))
|
|
||||||
|
|
||||||
# self.chg_temp_field.setText(f'{data.get("chargerTemp", 0) / 10.0:.1f} °C')
|
|
||||||
# self.charger_fault_field.setText(str(data.get("chargerFaultCode", 0)))
|
|
||||||
# self.current_field.setText(f'{data.get("current", 0) / 1000.0:.2f} A')
|
|
||||||
# self.slot_temp_field.setText(f'{data.get("slotTemperature", 0) / 10.0:.1f} °C')
|
|
||||||
|
|
||||||
# door_status = "OPEN" if data.get("doorStatus") == 1 else "CLOSED"
|
|
||||||
# self.door_status_field.setText(door_status)
|
|
||||||
|
|
||||||
# # --- ADDED: Set color based on door status ---
|
|
||||||
# if door_status == "OPEN":
|
|
||||||
# self.door_status_field.setStyleSheet("background-color: #2E7D32; color: white; border-radius: 3px;")
|
|
||||||
# else: # CLOSED
|
|
||||||
# self.door_status_field.setStyleSheet("background-color: #C62828; color: white; border-radius: 3px;")
|
|
||||||
|
|
||||||
def reset_to_default(self):
|
def reset_to_default(self):
|
||||||
"""Resets all fields in this chamber widget to their default state."""
|
"""Resets all fields in this chamber widget to their default state."""
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue