feat: Improve terminal logging and add build script

main
Kirubakaran 2025-08-22 21:53:21 +05:30
parent c296932450
commit d660cc67f7
4 changed files with 91 additions and 27 deletions

View File

@ -2,6 +2,7 @@
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 ---

57
generate_exe.py Normal file
View File

@ -0,0 +1,57 @@
import subprocess
import sys
def generate_executable():
"""Prompts for a version and generates a single-file executable."""
# 1. Ask the user for the version number
version = ""
while not version:
version = input("Enter the version for the executable (e.g., 4.1): ")
if not version:
print("Version cannot be empty. Please try again.")
executable_name = f"SwapStationDashboard_v{version}"
print(f"Generating executable with name: {executable_name}")
# Check if pyinstaller is installed
try:
subprocess.run([sys.executable, "-m", "PyInstaller", "--version"], check=True, capture_output=True)
except (subprocess.CalledProcessError, FileNotFoundError):
print("PyInstaller is not found. Please install it using: pip install pyinstaller")
return
print("Starting executable generation...")
# 2. Use the version to create the command
command = [
sys.executable,
"-m", "PyInstaller",
f"--name={executable_name}",
"--onefile",
"--icon=assets/icon.ico",
"--add-data=logo;logo",
"--add-data=assets;assets",
"--add-data=proto;proto",
"--hidden-import=paho.mqtt",
"--hidden-import=google.protobuf",
"--hidden-import=PyQt6",
"--hidden-import=PyQt6.Qt6",
"--hidden-import=PyQt6.sip",
"--hidden-import=setuptools",
"main.py"
]
try:
# 3. Execute the command
subprocess.run(command, check=True)
print("\n✅ Executable generated successfully!")
print(f"Look for '{executable_name}.exe' in the 'dist' folder.")
except subprocess.CalledProcessError as e:
print("\n❌ An error occurred during executable generation.")
print(f"Command failed with return code: {e.returncode}")
except FileNotFoundError:
print("\n❌ Error: The 'main.py' file was not found. Please run this script from the project's root directory.")
if __name__ == "__main__":
generate_executable()

View File

@ -3,6 +3,7 @@ import json
import os
import sys
import time
import uuid
from functools import partial
from PyQt6.QtCore import pyqtSignal, QThread, Qt, QPropertyAnimation, QEasingCurve, QSettings, pyqtSlot
@ -1061,6 +1062,7 @@ class MainWindow(QMainWindow):
decoded_payload = periodicData()
decoded_payload.ParseFromString(payload)
data_dict = MessageToDict(decoded_payload, preserving_proto_field_name=True)
# print(data_dict)
self._log_periodic_to_terminal(decoded_payload, data_dict)
self.update_main_dashboard(data_dict)
if self.save_logs_checkbox.isChecked():
@ -1314,6 +1316,7 @@ class MainWindow(QMainWindow):
print("✅ Configuration saved.")
# ==========================================================
client_id = f"SwapStationDashboard-{str(uuid.uuid4())}"
self.mqtt_thread = QThread()
self.mqtt_client = MqttClient(broker, port, user, password, client_id)
self.mqtt_client.moveToThread(self.mqtt_thread)
@ -1428,36 +1431,52 @@ class MainWindow(QMainWindow):
except Exception as e:
print(f"Error updating dashboard: {e}")
def _log_periodic_to_terminal(self, decoded_payload, data_dict):
"""Formats and prints the periodic data to the terminal as a clean table."""
try:
current_time = datetime.datetime.fromtimestamp(decoded_payload.ts).strftime('%Y-%m-%d %H:%M:%S')
device_id = data_dict.get("deviceId", "N/A")
backup_supply = "ON" if decoded_payload.backupSupplyStatus == 1 else "OFF"
station_sdc = decoded_payload.stationDiagnosticCode
# --- Main Information ---
print("\n\033[1m" + "="*50 + " PERIODIC DATA " + "="*50 + "\033[0m")
print(f"\033[1m Timestamp:\033[0m {current_time} | \033[1mDevice ID:\033[0m {device_id}")
print(f"\033[1m Backup Supply:\033[0m {decoded_payload.backupSupplyStatus} | \033[1mStation SDC:\033[0m {decoded_payload.stationDiagnosticCode}")
print(f"\033[1m Timestamp:\033[0m {current_time} | \033[1mDevice ID:\033[0m {device_id}")
print(f"\033[1m Backup Supply:\033[0m {backup_supply} | \033[1mStation SDC:\033[0m {station_sdc}")
print("-" * 120)
# --- Table Header ---
header = "| {:^7} | {:^18} | {:^8} | {:^8} | {:^7} | {:^10} | {:^10} | {:^12} | {:^10} |"
print(header.format("Chamber", "Battery ID", "Present", "Charging", "SOC", "Voltage", "Current", "Temp (°C)", "Door"))
header = "| {:^7} | {:^18} | {:^8} | {:^8} | {:^7} | {:^10} | {:^10} | {:^10} | {:^10} | {:^10} |"
print(header.format("Chamber", "Battery ID", "Present", "Charging", "SOC", "Voltage", "Current", "Slot Temp", "Bat Temp", "Door"))
print("-" * 120)
# --- Table Rows ---
row_format = "| {:^7} | {:<18} | {:^8} | {:^8} | {:>5}% | {:>8} V | {:>8} A | {:>10}°C | {:^10} |"
for i, chamber in enumerate(data_dict.get("slotLevelPayload", []), start=1):
row_format = "| {:^7} | {:<18} | {:^8} | {:^8} | {:>5}% | {:>8}V | {:>8}A | {:>8}°C | {:>8}°C | {:^10} |"
for i, chamber_data in enumerate(data_dict.get("slotLevelPayload", []), start=1):
is_present = chamber_data.get("batteryPresent") == 1
is_charging = chamber_data.get("chargingStatus") == 1
is_door_open = chamber_data.get("doorStatus") == 1
# Get and format the Slot Temperature
slot_temp_raw = chamber_data.get('slotTemperature', 'N/A')
slot_temp_celsius = f"{slot_temp_raw / 10:.1f}" if isinstance(slot_temp_raw, int) else "N/A"
# Get and format the Battery Temperature
battery_temp_raw = chamber_data.get('batteryMaxTemp', 'N/A')
battery_temp_celsius = f"{battery_temp_raw / 10:.1f}" if isinstance(battery_temp_raw, int) else "N/A"
print(row_format.format(
i,
chamber.get('batteryIdentification', 'N/A'),
"" if chamber.get("batteryPresent") == 1 else "",
"" if chamber.get("chargingStatus") == 1 else "",
chamber.get('soc', 'N/A'),
chamber.get('batVoltage', 'N/A'),
chamber.get('current', 'N/A'),
chamber.get('batteryTemp', 'N/A'),
"OPEN" if chamber.get("doorStatus") == 1 else "CLOSED"
chamber_data.get('batteryIdentification', 'N/A'),
"" if is_present else "",
"" if is_charging else "",
chamber_data.get('soc', 'N/A'),
chamber_data.get('batVoltage', 'N/A'),
chamber_data.get('current', 'N/A'),
slot_temp_celsius,
battery_temp_celsius,
"Open" if is_door_open else "Closed"
))
print("=" * 120 + "\n")

View File

@ -1,13 +0,0 @@
import sys
import os
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
# If not running as a bundled exe, use the normal script path
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)