feat: Improve terminal logging and add build script
parent
c296932450
commit
d660cc67f7
|
|
@ -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 ---
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
13
utils.py
13
utils.py
|
|
@ -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)
|
||||
Loading…
Reference in New Issue