chore: initial project setup
- Added base project structure (core, ui, proto, assets, logs, utils) - Added requirements.txt for dependencies - Added main.py entry point - Configured .gitignore to exclude __pycache__, build, dist, venv, logs, and .spec filesmain
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Python cache
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
|
||||||
|
# Build / distribution
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
*.egg-info/
|
||||||
|
|
||||||
|
# PyInstaller spec files
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Virtual environment
|
||||||
|
venv/
|
||||||
|
.env/
|
||||||
|
.venv/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# IDE / editor specific
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
|
@ -0,0 +1,159 @@
|
||||||
|
# In core/csv_logger.py
|
||||||
|
|
||||||
|
import csv
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import queue
|
||||||
|
from datetime import datetime
|
||||||
|
from PyQt6.QtCore import QObject, pyqtSlot, QTimer
|
||||||
|
from google.protobuf.json_format import MessageToDict
|
||||||
|
from proto.vec_payload_chgSt_pb2 import eventPayload, rpcRequest
|
||||||
|
|
||||||
|
class CsvLogger(QObject):
|
||||||
|
def __init__(self, base_log_directory, session_name):
|
||||||
|
super().__init__()
|
||||||
|
self.base_log_directory = base_log_directory
|
||||||
|
self.session_name = session_name
|
||||||
|
os.makedirs(self.base_log_directory, exist_ok=True)
|
||||||
|
|
||||||
|
self.files = {}
|
||||||
|
self.writers = {}
|
||||||
|
self.queue = queue.Queue()
|
||||||
|
self.timer = QTimer(self)
|
||||||
|
self.timer.timeout.connect(self._process_queue)
|
||||||
|
self.num_slots = 9
|
||||||
|
|
||||||
|
def start_logging(self):
|
||||||
|
self.timer.start(100)
|
||||||
|
print(f"✅ CSV logging service started for session: {self.session_name}")
|
||||||
|
|
||||||
|
def _get_writer(self, device_id, file_group):
|
||||||
|
writer_key = (device_id, file_group)
|
||||||
|
if writer_key in self.writers:
|
||||||
|
return self.writers[writer_key]
|
||||||
|
|
||||||
|
try:
|
||||||
|
session_dir = os.path.join(self.base_log_directory, device_id, self.session_name)
|
||||||
|
os.makedirs(session_dir, exist_ok=True)
|
||||||
|
filepath = os.path.join(session_dir, f"{file_group}.csv")
|
||||||
|
|
||||||
|
self.files[writer_key] = open(filepath, 'w', newline='', encoding='utf-8')
|
||||||
|
writer = csv.writer(self.files[writer_key])
|
||||||
|
self.writers[writer_key] = writer
|
||||||
|
|
||||||
|
if file_group == 'PERIODIC':
|
||||||
|
# --- Programmatically build the WIDE header ---
|
||||||
|
base_header = ["Timestamp", "DeviceID", "StationDiagnosticCode"]
|
||||||
|
slot_fields = [
|
||||||
|
"BatteryID", "BatteryPresent", "ChargerPresent", "DoorStatus", "DoorLockStatus",
|
||||||
|
"Voltage_V", "Current_A", "SOC_Percent", "BatteryTemp_C", "SlotTemp_C",
|
||||||
|
"BatteryFaultCode", "ChargerFaultCode", "BatteryMode", "ChargerMode"
|
||||||
|
]
|
||||||
|
slot_header = [f"Slot{i}_{field}" for i in range(1, self.num_slots + 1) for field in slot_fields]
|
||||||
|
header = base_header + slot_header + ["RawHexPayload"]
|
||||||
|
else: # Header for EVENTS_RPC
|
||||||
|
header = ["Timestamp", "Topic", "Payload_JSON", "RawHexPayload"]
|
||||||
|
|
||||||
|
writer.writerow(header)
|
||||||
|
print(f"---> New log file created: {filepath}")
|
||||||
|
return writer
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Failed to create CSV writer for {writer_key}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
@pyqtSlot(list)
|
||||||
|
def log_data(self, data_list):
|
||||||
|
self.queue.put(data_list)
|
||||||
|
|
||||||
|
def _process_queue(self):
|
||||||
|
while not self.queue.empty():
|
||||||
|
# ==========================================================
|
||||||
|
# ===== EDITED SECTION STARTS HERE =========================
|
||||||
|
# ==========================================================
|
||||||
|
item = None # Define item outside the try block for better error reporting
|
||||||
|
try:
|
||||||
|
# First, get the whole item from the queue.
|
||||||
|
item = self.queue.get()
|
||||||
|
|
||||||
|
# Now, try to unpack it. This is where the ValueError can happen.
|
||||||
|
timestamp_obj, topic, data, raw_payload = item
|
||||||
|
|
||||||
|
# The rest of your logic remains the same
|
||||||
|
parts = topic.split('/')
|
||||||
|
if len(parts) < 5: continue
|
||||||
|
device_id = parts[3]
|
||||||
|
file_group = 'PERIODIC' if topic.endswith('/PERIODIC') else 'EVENTS_RPC'
|
||||||
|
|
||||||
|
writer = self._get_writer(device_id, file_group)
|
||||||
|
if not writer: continue
|
||||||
|
|
||||||
|
if file_group == 'PERIODIC':
|
||||||
|
# --- Build one single WIDE row ---
|
||||||
|
row_data = [
|
||||||
|
datetime.fromtimestamp(data.get("ts")).strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
device_id,
|
||||||
|
data.get("stationDiagnosticCode", "N/A")
|
||||||
|
]
|
||||||
|
|
||||||
|
all_slots_data = []
|
||||||
|
slots = data.get("slotLevelPayload", [])
|
||||||
|
num_slot_fields = 14
|
||||||
|
|
||||||
|
for i in range(self.num_slots):
|
||||||
|
if i < len(slots):
|
||||||
|
slot = slots[i]
|
||||||
|
all_slots_data.extend([
|
||||||
|
slot.get('batteryIdentification', ''),
|
||||||
|
"TRUE" if slot.get("batteryPresent") == 1 else "FALSE",
|
||||||
|
"TRUE" if slot.get("chargerPresent") == 1 else "FALSE",
|
||||||
|
"OPEN" if slot.get("doorStatus") == 1 else "CLOSED",
|
||||||
|
"LOCKED" if slot.get("doorLockStatus") == 1 else "UNLOCKED",
|
||||||
|
slot.get('voltage', 0) / 1000.0,
|
||||||
|
slot.get('current', 0) / 1000.0,
|
||||||
|
slot.get('soc', 0),
|
||||||
|
slot.get('batteryMaxTemp', 0) / 10.0,
|
||||||
|
slot.get('slotTemperature', 0) / 10.0,
|
||||||
|
slot.get('batteryFaultCode', 0),
|
||||||
|
slot.get('chargerFaultCode', 0),
|
||||||
|
slot.get('batteryMode', 0),
|
||||||
|
slot.get('chargerMode', 0)
|
||||||
|
])
|
||||||
|
else:
|
||||||
|
all_slots_data.extend([''] * num_slot_fields)
|
||||||
|
|
||||||
|
final_row = row_data + all_slots_data + [raw_payload.hex()]
|
||||||
|
writer.writerow(final_row)
|
||||||
|
else:
|
||||||
|
# Logic for EVENTS and RPC remains the same
|
||||||
|
payload_json_string = json.dumps(data)
|
||||||
|
row = [
|
||||||
|
timestamp_obj.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3],
|
||||||
|
topic,
|
||||||
|
payload_json_string,
|
||||||
|
raw_payload.hex()
|
||||||
|
]
|
||||||
|
writer.writerow(row)
|
||||||
|
|
||||||
|
writer_key = (device_id, file_group)
|
||||||
|
if file_handle := self.files.get(writer_key):
|
||||||
|
file_handle.flush()
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
# This specifically catches the unpacking error and prints a helpful message.
|
||||||
|
print(f"❌ Error: Malformed item in log queue. Expected 4 values, but got {len(item)}. Item: {item}")
|
||||||
|
continue # Continue to the next item in the queue
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# A general catch-all for any other unexpected errors.
|
||||||
|
# This message is safe because it doesn't use variables from the try block.
|
||||||
|
print(f"❌ An unexpected error occurred in the logger thread: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
def stop_logging(self):
|
||||||
|
self.timer.stop()
|
||||||
|
self._process_queue()
|
||||||
|
for file in self.files.values():
|
||||||
|
file.close()
|
||||||
|
self.files.clear()
|
||||||
|
self.writers.clear()
|
||||||
|
print(f"🛑 CSV logging stopped for session: {self.session_name}")
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
# In core/mqtt_client.py
|
||||||
|
import socket
|
||||||
|
from PyQt6.QtCore import QObject, pyqtSignal, pyqtSlot
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
|
||||||
|
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!")
|
||||||
|
# --- MODIFIED EMIT: Send a success message ---
|
||||||
|
self.connection_status_changed.emit(True, "✅ Connected")
|
||||||
|
else:
|
||||||
|
print(f"Failed to connect, return code {rc}\n")
|
||||||
|
# --- MODIFIED EMIT: Send a failure message ---
|
||||||
|
self.connection_status_changed.emit(False, f"❌ Connection failed (Code: {rc})")
|
||||||
|
self.stop_logging_signal.emit() # Emit the signal
|
||||||
|
self.disconnected.emit()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def on_disconnect(self, client, userdata, flags, rc, properties):
|
||||||
|
print("Disconnected from MQTT Broker.")
|
||||||
|
# Change the icon in the line below from 🔌 to 🔴 ❌ 🚫 💔
|
||||||
|
self.connection_status_changed.emit(False, "💔 Disconnected")
|
||||||
|
self.disconnected.emit()
|
||||||
|
|
||||||
|
# --- 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, 60)
|
||||||
|
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
|
||||||
|
self.client.connect(self.broker, self.port, 60)
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
|
@ -0,0 +1,16 @@
|
||||||
|
import sys
|
||||||
|
from PyQt6.QtWidgets import QApplication
|
||||||
|
from ui.main_window import MainWindow
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
# --- DYNAMIC SCALING LOGIC ---
|
||||||
|
BASE_HEIGHT = 1080.0
|
||||||
|
screen = app.primaryScreen()
|
||||||
|
available_height = screen.availableGeometry().height()
|
||||||
|
scale_factor = max(0.7, available_height / BASE_HEIGHT)
|
||||||
|
|
||||||
|
window = MainWindow(scale_factor=scale_factor)
|
||||||
|
window.showMaximized()
|
||||||
|
sys.exit(app.exec())
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
slotLevelPayload.batteryIdentification max_length: 32
|
||||||
|
|
||||||
|
mainPayload.switchStatus max_count: 9 fixed_count:true
|
||||||
|
mainPayload.sessionId max_length: 64
|
||||||
|
mainPayload.slotLevelPayload max_count:9 fixed_count:true
|
||||||
|
mainPayload.deviceId max_length: 30
|
||||||
|
|
||||||
|
eventPayload.deviceId max_length: 30
|
||||||
|
eventPayload.sessionId max_length: 64
|
||||||
|
|
||||||
|
eventData_s.nfcUuid max_length: 64
|
||||||
|
eventData_s.batteryIdentification max_length: 32
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
rpcResponse.deviceId max_length:30
|
||||||
|
rpcResponse.jobId max_length:64
|
||||||
|
|
||||||
|
|
||||||
|
rpcRequest.jobId max_length:64
|
||||||
|
|
||||||
|
rpcData_s.slotsData max_count: 18 fixed_count:true
|
||||||
|
rpcData_s.sessionId max_length: 64
|
||||||
|
|
||||||
|
|
||||||
|
nfcPayload_s.manufacturingData max_length: 16
|
||||||
|
nfcPayload_s.customData max_length: 128
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
enum eventType_e {
|
||||||
|
EVENT_SWAP_START = 0x200;
|
||||||
|
EVENT_BATTERY_ENTRY = 0x201;
|
||||||
|
EVENT_BATTERY_EXIT = 0x202;
|
||||||
|
EVENT_ACTIVITY_FAILED = 0x203;
|
||||||
|
EVENT_SWAP_ABORTED = 0x204;
|
||||||
|
EVENT_BATFAULT_ALARM = 0x205;
|
||||||
|
EVENT_SLOT_LOCK_ENEGAGED = 0x206;
|
||||||
|
EVENT_SWAP_ENDED = 0x207;
|
||||||
|
EVENT_CHGFAULT_ALARM = 0x208;
|
||||||
|
EVENT_NFC_SCAN = 0x209;
|
||||||
|
EVENT_SLOT_LOCK_DISENEGAGED = 0x20A;
|
||||||
|
EVENT_REVERSE_SWAP = 0x20B;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum jobType_e {
|
||||||
|
JOBTYPE_NONE = 0;
|
||||||
|
JOBTYPE_GET_STATUS_OF_A_JOB = 0x01;
|
||||||
|
JOBTYPE_SWAP_START = 0x100;
|
||||||
|
JOBTYPE_CHARGER_ENABLE_DISABLE = 0x101;
|
||||||
|
JOBTYPE_GATE_OPEN_CLOSE = 0x102;
|
||||||
|
JOBTYPE_TRANSACTION_ABORT = 0x103;
|
||||||
|
JOBTYPE_REBOOT = 0x104;
|
||||||
|
JOBTYPE_SWAP_DENY = 0x105;
|
||||||
|
JOBTYPE_LANGUAGE_UPDATE = 0x106;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum jobResult_e {
|
||||||
|
JOB_RESULT_UNKNOWN = 0;
|
||||||
|
JOB_RESULT_SUCCESS = 1;
|
||||||
|
JOB_RESULT_REJECTED = 2;
|
||||||
|
JOB_RESULT_TIMEOUT = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum jobStatus_e {
|
||||||
|
JOB_STATUS_IDLE = 0;
|
||||||
|
JOB_STATUS_PENDING = 1;
|
||||||
|
JOB_STATUS_EXECUTING = 2;
|
||||||
|
JOB_STATUS_EXECUTED = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message slotLevelPayload{
|
||||||
|
optional uint32 batteryPresent = 1;
|
||||||
|
optional uint32 chargerPresent = 2;
|
||||||
|
optional uint32 doorLockStatus = 3;
|
||||||
|
optional uint32 doorStatus = 4;
|
||||||
|
optional uint32 voltage = 5;
|
||||||
|
optional int32 current = 6;
|
||||||
|
optional uint32 batteryFaultCode = 7;
|
||||||
|
optional uint32 chargerFaultCode = 8;
|
||||||
|
optional int32 batteryMaxTemp = 9;
|
||||||
|
optional int32 chargerMaxTemp= 10;
|
||||||
|
optional string batteryIdentification = 11;
|
||||||
|
optional uint32 batteryMode = 12;
|
||||||
|
optional uint32 chargerMode = 13;
|
||||||
|
optional int32 slotTemperature = 14;
|
||||||
|
optional uint32 gasSensor = 15;
|
||||||
|
optional uint32 soc=16;
|
||||||
|
optional uint32 ts = 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
message mainPayload{
|
||||||
|
required uint32 ts = 1;
|
||||||
|
required string deviceId = 2;
|
||||||
|
required string sessionId = 3;
|
||||||
|
repeated slotLevelPayload slotLevelPayload = 4;
|
||||||
|
optional uint32 backupSupplyStatus = 5;
|
||||||
|
repeated uint32 switchStatus = 6;
|
||||||
|
optional uint32 stationStatus = 7;
|
||||||
|
optional uint32 stationDiagnosticCode = 8;
|
||||||
|
repeated float coordinates = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum swapAbortReason_e{
|
||||||
|
ABORT_UNKNOWN=0;
|
||||||
|
ABORT_BAT_EXIT_TIMEOUT=1;
|
||||||
|
ABORT_BAT_ENTRY_TIMEOUT=2;
|
||||||
|
ABORT_DOOR_CLOSE_TIMEOUT=3;
|
||||||
|
ABORT_DOOR_OPEN_TIMEOUT=4;
|
||||||
|
ABORT_INVALID_PARAM=5;
|
||||||
|
ABORT_REMOTE_REQUESTED=6;
|
||||||
|
ABORT_INVALID_BATTERY=7;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum swapDenyReason_e{
|
||||||
|
SWAP_DENY_INSUFFICIENT_BAL=1;
|
||||||
|
SWAP_DENY_INVALID_NFC=2;
|
||||||
|
SWAP_DENY_BATTERY_UNAVAILABLE=3;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum languageType_e{
|
||||||
|
LANGUAGE_TYPE_ENGLISH = 1;
|
||||||
|
LANGUAGE_TYPE_HINDI = 2;
|
||||||
|
LANGUAGE_TYPE_KANNADA = 3;
|
||||||
|
LANGUAGE_TYPE_TELUGU = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message nfcPayload_s{
|
||||||
|
required string manufacturingData = 1;
|
||||||
|
required string customData = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message eventData_s {
|
||||||
|
optional nfcPayload_s nfcData = 1;
|
||||||
|
optional string batteryIdentification = 2;
|
||||||
|
optional uint32 activityFailureReason = 3;
|
||||||
|
optional swapAbortReason_e swapAbortReason = 4;
|
||||||
|
optional uint32 swapTime = 5;
|
||||||
|
optional uint32 faultCode = 6;
|
||||||
|
optional uint32 doorStatus = 7;
|
||||||
|
optional uint32 slotId = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message eventPayload {
|
||||||
|
required uint32 ts = 1;
|
||||||
|
required string deviceId = 2;
|
||||||
|
required eventType_e eventType = 3;
|
||||||
|
required string sessionId = 4;
|
||||||
|
optional eventData_s eventData = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message rpcData_s {
|
||||||
|
optional string sessionId = 1;
|
||||||
|
repeated uint32 slotsData = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message slotControl_s {
|
||||||
|
required uint32 slotId = 1;
|
||||||
|
required uint32 state = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message getJobStatusByJobId_s{
|
||||||
|
required string jobId = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message rpcRequest {
|
||||||
|
required uint32 ts = 1;
|
||||||
|
required string jobId = 2;
|
||||||
|
required jobType_e jobType = 3;
|
||||||
|
optional rpcData_s rpcData = 4;
|
||||||
|
optional slotControl_s slotInfo = 5;
|
||||||
|
optional swapDenyReason_e swapDeny = 8;
|
||||||
|
optional getJobStatusByJobId_s getJobStatusByJobId = 9;
|
||||||
|
optional languageType_e languageType = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
message jobStatusByJobIdResponse_s{
|
||||||
|
required string jobId = 1;
|
||||||
|
required jobStatus_e jobStatus = 2;
|
||||||
|
required jobResult_e jobResult = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message rpcResponse {
|
||||||
|
required uint32 ts = 1;
|
||||||
|
required string deviceId = 2;
|
||||||
|
required string jobId = 3;
|
||||||
|
required jobStatus_e jobStatus = 4;
|
||||||
|
required jobResult_e jobResult = 5;
|
||||||
|
optional jobStatusByJobIdResponse_s jobStatusByJobIdResponse = 6;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,287 @@
|
||||||
|
from google.protobuf.internal import containers as _containers
|
||||||
|
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
|
||||||
|
from google.protobuf import descriptor as _descriptor
|
||||||
|
from google.protobuf import message as _message
|
||||||
|
from collections.abc import Iterable as _Iterable, Mapping as _Mapping
|
||||||
|
from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union
|
||||||
|
|
||||||
|
DESCRIPTOR: _descriptor.FileDescriptor
|
||||||
|
|
||||||
|
class eventType_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||||
|
__slots__ = ()
|
||||||
|
EVENT_SWAP_START: _ClassVar[eventType_e]
|
||||||
|
EVENT_BATTERY_ENTRY: _ClassVar[eventType_e]
|
||||||
|
EVENT_BATTERY_EXIT: _ClassVar[eventType_e]
|
||||||
|
EVENT_ACTIVITY_FAILED: _ClassVar[eventType_e]
|
||||||
|
EVENT_SWAP_ABORTED: _ClassVar[eventType_e]
|
||||||
|
EVENT_BATFAULT_ALARM: _ClassVar[eventType_e]
|
||||||
|
EVENT_SLOT_LOCK_ENEGAGED: _ClassVar[eventType_e]
|
||||||
|
EVENT_SWAP_ENDED: _ClassVar[eventType_e]
|
||||||
|
EVENT_CHGFAULT_ALARM: _ClassVar[eventType_e]
|
||||||
|
EVENT_NFC_SCAN: _ClassVar[eventType_e]
|
||||||
|
EVENT_SLOT_LOCK_DISENEGAGED: _ClassVar[eventType_e]
|
||||||
|
EVENT_REVERSE_SWAP: _ClassVar[eventType_e]
|
||||||
|
|
||||||
|
class jobType_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||||
|
__slots__ = ()
|
||||||
|
JOBTYPE_NONE: _ClassVar[jobType_e]
|
||||||
|
JOBTYPE_GET_STATUS_OF_A_JOB: _ClassVar[jobType_e]
|
||||||
|
JOBTYPE_SWAP_START: _ClassVar[jobType_e]
|
||||||
|
JOBTYPE_CHARGER_ENABLE_DISABLE: _ClassVar[jobType_e]
|
||||||
|
JOBTYPE_GATE_OPEN_CLOSE: _ClassVar[jobType_e]
|
||||||
|
JOBTYPE_TRANSACTION_ABORT: _ClassVar[jobType_e]
|
||||||
|
JOBTYPE_REBOOT: _ClassVar[jobType_e]
|
||||||
|
JOBTYPE_SWAP_DENY: _ClassVar[jobType_e]
|
||||||
|
JOBTYPE_LANGUAGE_UPDATE: _ClassVar[jobType_e]
|
||||||
|
|
||||||
|
class jobResult_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||||
|
__slots__ = ()
|
||||||
|
JOB_RESULT_UNKNOWN: _ClassVar[jobResult_e]
|
||||||
|
JOB_RESULT_SUCCESS: _ClassVar[jobResult_e]
|
||||||
|
JOB_RESULT_REJECTED: _ClassVar[jobResult_e]
|
||||||
|
JOB_RESULT_TIMEOUT: _ClassVar[jobResult_e]
|
||||||
|
|
||||||
|
class jobStatus_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||||
|
__slots__ = ()
|
||||||
|
JOB_STATUS_IDLE: _ClassVar[jobStatus_e]
|
||||||
|
JOB_STATUS_PENDING: _ClassVar[jobStatus_e]
|
||||||
|
JOB_STATUS_EXECUTING: _ClassVar[jobStatus_e]
|
||||||
|
JOB_STATUS_EXECUTED: _ClassVar[jobStatus_e]
|
||||||
|
|
||||||
|
class swapAbortReason_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||||
|
__slots__ = ()
|
||||||
|
ABORT_UNKNOWN: _ClassVar[swapAbortReason_e]
|
||||||
|
ABORT_BAT_EXIT_TIMEOUT: _ClassVar[swapAbortReason_e]
|
||||||
|
ABORT_BAT_ENTRY_TIMEOUT: _ClassVar[swapAbortReason_e]
|
||||||
|
ABORT_DOOR_CLOSE_TIMEOUT: _ClassVar[swapAbortReason_e]
|
||||||
|
ABORT_DOOR_OPEN_TIMEOUT: _ClassVar[swapAbortReason_e]
|
||||||
|
ABORT_INVALID_PARAM: _ClassVar[swapAbortReason_e]
|
||||||
|
ABORT_REMOTE_REQUESTED: _ClassVar[swapAbortReason_e]
|
||||||
|
ABORT_INVALID_BATTERY: _ClassVar[swapAbortReason_e]
|
||||||
|
|
||||||
|
class swapDenyReason_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||||
|
__slots__ = ()
|
||||||
|
SWAP_DENY_INSUFFICIENT_BAL: _ClassVar[swapDenyReason_e]
|
||||||
|
SWAP_DENY_INVALID_NFC: _ClassVar[swapDenyReason_e]
|
||||||
|
SWAP_DENY_BATTERY_UNAVAILABLE: _ClassVar[swapDenyReason_e]
|
||||||
|
|
||||||
|
class languageType_e(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||||
|
__slots__ = ()
|
||||||
|
LANGUAGE_TYPE_ENGLISH: _ClassVar[languageType_e]
|
||||||
|
LANGUAGE_TYPE_HINDI: _ClassVar[languageType_e]
|
||||||
|
LANGUAGE_TYPE_KANNADA: _ClassVar[languageType_e]
|
||||||
|
LANGUAGE_TYPE_TELUGU: _ClassVar[languageType_e]
|
||||||
|
EVENT_SWAP_START: eventType_e
|
||||||
|
EVENT_BATTERY_ENTRY: eventType_e
|
||||||
|
EVENT_BATTERY_EXIT: eventType_e
|
||||||
|
EVENT_ACTIVITY_FAILED: eventType_e
|
||||||
|
EVENT_SWAP_ABORTED: eventType_e
|
||||||
|
EVENT_BATFAULT_ALARM: eventType_e
|
||||||
|
EVENT_SLOT_LOCK_ENEGAGED: eventType_e
|
||||||
|
EVENT_SWAP_ENDED: eventType_e
|
||||||
|
EVENT_CHGFAULT_ALARM: eventType_e
|
||||||
|
EVENT_NFC_SCAN: eventType_e
|
||||||
|
EVENT_SLOT_LOCK_DISENEGAGED: eventType_e
|
||||||
|
EVENT_REVERSE_SWAP: eventType_e
|
||||||
|
JOBTYPE_NONE: jobType_e
|
||||||
|
JOBTYPE_GET_STATUS_OF_A_JOB: jobType_e
|
||||||
|
JOBTYPE_SWAP_START: jobType_e
|
||||||
|
JOBTYPE_CHARGER_ENABLE_DISABLE: jobType_e
|
||||||
|
JOBTYPE_GATE_OPEN_CLOSE: jobType_e
|
||||||
|
JOBTYPE_TRANSACTION_ABORT: jobType_e
|
||||||
|
JOBTYPE_REBOOT: jobType_e
|
||||||
|
JOBTYPE_SWAP_DENY: jobType_e
|
||||||
|
JOBTYPE_LANGUAGE_UPDATE: jobType_e
|
||||||
|
JOB_RESULT_UNKNOWN: jobResult_e
|
||||||
|
JOB_RESULT_SUCCESS: jobResult_e
|
||||||
|
JOB_RESULT_REJECTED: jobResult_e
|
||||||
|
JOB_RESULT_TIMEOUT: jobResult_e
|
||||||
|
JOB_STATUS_IDLE: jobStatus_e
|
||||||
|
JOB_STATUS_PENDING: jobStatus_e
|
||||||
|
JOB_STATUS_EXECUTING: jobStatus_e
|
||||||
|
JOB_STATUS_EXECUTED: jobStatus_e
|
||||||
|
ABORT_UNKNOWN: swapAbortReason_e
|
||||||
|
ABORT_BAT_EXIT_TIMEOUT: swapAbortReason_e
|
||||||
|
ABORT_BAT_ENTRY_TIMEOUT: swapAbortReason_e
|
||||||
|
ABORT_DOOR_CLOSE_TIMEOUT: swapAbortReason_e
|
||||||
|
ABORT_DOOR_OPEN_TIMEOUT: swapAbortReason_e
|
||||||
|
ABORT_INVALID_PARAM: swapAbortReason_e
|
||||||
|
ABORT_REMOTE_REQUESTED: swapAbortReason_e
|
||||||
|
ABORT_INVALID_BATTERY: swapAbortReason_e
|
||||||
|
SWAP_DENY_INSUFFICIENT_BAL: swapDenyReason_e
|
||||||
|
SWAP_DENY_INVALID_NFC: swapDenyReason_e
|
||||||
|
SWAP_DENY_BATTERY_UNAVAILABLE: swapDenyReason_e
|
||||||
|
LANGUAGE_TYPE_ENGLISH: languageType_e
|
||||||
|
LANGUAGE_TYPE_HINDI: languageType_e
|
||||||
|
LANGUAGE_TYPE_KANNADA: languageType_e
|
||||||
|
LANGUAGE_TYPE_TELUGU: languageType_e
|
||||||
|
|
||||||
|
class slotLevelPayload(_message.Message):
|
||||||
|
__slots__ = ("batteryPresent", "chargerPresent", "doorLockStatus", "doorStatus", "voltage", "current", "batteryFaultCode", "chargerFaultCode", "batteryMaxTemp", "chargerMaxTemp", "batteryIdentification", "batteryMode", "chargerMode", "slotTemperature", "gasSensor", "soc", "ts")
|
||||||
|
BATTERYPRESENT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
CHARGERPRESENT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DOORLOCKSTATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DOORSTATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
VOLTAGE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
CURRENT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BATTERYFAULTCODE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
CHARGERFAULTCODE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BATTERYMAXTEMP_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
CHARGERMAXTEMP_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BATTERYIDENTIFICATION_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BATTERYMODE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
CHARGERMODE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SLOTTEMPERATURE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
GASSENSOR_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SOC_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
TS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
batteryPresent: int
|
||||||
|
chargerPresent: int
|
||||||
|
doorLockStatus: int
|
||||||
|
doorStatus: int
|
||||||
|
voltage: int
|
||||||
|
current: int
|
||||||
|
batteryFaultCode: int
|
||||||
|
chargerFaultCode: int
|
||||||
|
batteryMaxTemp: int
|
||||||
|
chargerMaxTemp: int
|
||||||
|
batteryIdentification: str
|
||||||
|
batteryMode: int
|
||||||
|
chargerMode: int
|
||||||
|
slotTemperature: int
|
||||||
|
gasSensor: int
|
||||||
|
soc: int
|
||||||
|
ts: int
|
||||||
|
def __init__(self, batteryPresent: _Optional[int] = ..., chargerPresent: _Optional[int] = ..., doorLockStatus: _Optional[int] = ..., doorStatus: _Optional[int] = ..., voltage: _Optional[int] = ..., current: _Optional[int] = ..., batteryFaultCode: _Optional[int] = ..., chargerFaultCode: _Optional[int] = ..., batteryMaxTemp: _Optional[int] = ..., chargerMaxTemp: _Optional[int] = ..., batteryIdentification: _Optional[str] = ..., batteryMode: _Optional[int] = ..., chargerMode: _Optional[int] = ..., slotTemperature: _Optional[int] = ..., gasSensor: _Optional[int] = ..., soc: _Optional[int] = ..., ts: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class mainPayload(_message.Message):
|
||||||
|
__slots__ = ("ts", "deviceId", "sessionId", "slotLevelPayload", "backupSupplyStatus", "switchStatus", "stationStatus", "stationDiagnosticCode", "coordinates")
|
||||||
|
TS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DEVICEID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SESSIONID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SLOTLEVELPAYLOAD_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BACKUPSUPPLYSTATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SWITCHSTATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
STATIONSTATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
STATIONDIAGNOSTICCODE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
COORDINATES_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ts: int
|
||||||
|
deviceId: str
|
||||||
|
sessionId: str
|
||||||
|
slotLevelPayload: _containers.RepeatedCompositeFieldContainer[slotLevelPayload]
|
||||||
|
backupSupplyStatus: int
|
||||||
|
switchStatus: _containers.RepeatedScalarFieldContainer[int]
|
||||||
|
stationStatus: int
|
||||||
|
stationDiagnosticCode: int
|
||||||
|
coordinates: _containers.RepeatedScalarFieldContainer[float]
|
||||||
|
def __init__(self, ts: _Optional[int] = ..., deviceId: _Optional[str] = ..., sessionId: _Optional[str] = ..., slotLevelPayload: _Optional[_Iterable[_Union[slotLevelPayload, _Mapping]]] = ..., backupSupplyStatus: _Optional[int] = ..., switchStatus: _Optional[_Iterable[int]] = ..., stationStatus: _Optional[int] = ..., stationDiagnosticCode: _Optional[int] = ..., coordinates: _Optional[_Iterable[float]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class nfcPayload_s(_message.Message):
|
||||||
|
__slots__ = ("manufacturingData", "customData")
|
||||||
|
MANUFACTURINGDATA_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
CUSTOMDATA_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
manufacturingData: str
|
||||||
|
customData: str
|
||||||
|
def __init__(self, manufacturingData: _Optional[str] = ..., customData: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class eventData_s(_message.Message):
|
||||||
|
__slots__ = ("nfcData", "batteryIdentification", "activityFailureReason", "swapAbortReason", "swapTime", "faultCode", "doorStatus", "slotId")
|
||||||
|
NFCDATA_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
BATTERYIDENTIFICATION_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ACTIVITYFAILUREREASON_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SWAPABORTREASON_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SWAPTIME_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
FAULTCODE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DOORSTATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SLOTID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
nfcData: nfcPayload_s
|
||||||
|
batteryIdentification: str
|
||||||
|
activityFailureReason: int
|
||||||
|
swapAbortReason: swapAbortReason_e
|
||||||
|
swapTime: int
|
||||||
|
faultCode: int
|
||||||
|
doorStatus: int
|
||||||
|
slotId: int
|
||||||
|
def __init__(self, nfcData: _Optional[_Union[nfcPayload_s, _Mapping]] = ..., batteryIdentification: _Optional[str] = ..., activityFailureReason: _Optional[int] = ..., swapAbortReason: _Optional[_Union[swapAbortReason_e, str]] = ..., swapTime: _Optional[int] = ..., faultCode: _Optional[int] = ..., doorStatus: _Optional[int] = ..., slotId: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class eventPayload(_message.Message):
|
||||||
|
__slots__ = ("ts", "deviceId", "eventType", "sessionId", "eventData")
|
||||||
|
TS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DEVICEID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
EVENTTYPE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SESSIONID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
EVENTDATA_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ts: int
|
||||||
|
deviceId: str
|
||||||
|
eventType: eventType_e
|
||||||
|
sessionId: str
|
||||||
|
eventData: eventData_s
|
||||||
|
def __init__(self, ts: _Optional[int] = ..., deviceId: _Optional[str] = ..., eventType: _Optional[_Union[eventType_e, str]] = ..., sessionId: _Optional[str] = ..., eventData: _Optional[_Union[eventData_s, _Mapping]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class rpcData_s(_message.Message):
|
||||||
|
__slots__ = ("sessionId", "slotsData")
|
||||||
|
SESSIONID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SLOTSDATA_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
sessionId: str
|
||||||
|
slotsData: _containers.RepeatedScalarFieldContainer[int]
|
||||||
|
def __init__(self, sessionId: _Optional[str] = ..., slotsData: _Optional[_Iterable[int]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class slotControl_s(_message.Message):
|
||||||
|
__slots__ = ("slotId", "state")
|
||||||
|
SLOTID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
STATE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
slotId: int
|
||||||
|
state: int
|
||||||
|
def __init__(self, slotId: _Optional[int] = ..., state: _Optional[int] = ...) -> None: ...
|
||||||
|
|
||||||
|
class getJobStatusByJobId_s(_message.Message):
|
||||||
|
__slots__ = ("jobId",)
|
||||||
|
JOBID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
jobId: str
|
||||||
|
def __init__(self, jobId: _Optional[str] = ...) -> None: ...
|
||||||
|
|
||||||
|
class rpcRequest(_message.Message):
|
||||||
|
__slots__ = ("ts", "jobId", "jobType", "rpcData", "slotInfo", "swapDeny", "getJobStatusByJobId", "languageType")
|
||||||
|
TS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
JOBID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
JOBTYPE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
RPCDATA_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SLOTINFO_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
SWAPDENY_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
GETJOBSTATUSBYJOBID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
LANGUAGETYPE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ts: int
|
||||||
|
jobId: str
|
||||||
|
jobType: jobType_e
|
||||||
|
rpcData: rpcData_s
|
||||||
|
slotInfo: slotControl_s
|
||||||
|
swapDeny: swapDenyReason_e
|
||||||
|
getJobStatusByJobId: getJobStatusByJobId_s
|
||||||
|
languageType: languageType_e
|
||||||
|
def __init__(self, ts: _Optional[int] = ..., jobId: _Optional[str] = ..., jobType: _Optional[_Union[jobType_e, str]] = ..., rpcData: _Optional[_Union[rpcData_s, _Mapping]] = ..., slotInfo: _Optional[_Union[slotControl_s, _Mapping]] = ..., swapDeny: _Optional[_Union[swapDenyReason_e, str]] = ..., getJobStatusByJobId: _Optional[_Union[getJobStatusByJobId_s, _Mapping]] = ..., languageType: _Optional[_Union[languageType_e, str]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class jobStatusByJobIdResponse_s(_message.Message):
|
||||||
|
__slots__ = ("jobId", "jobStatus", "jobResult")
|
||||||
|
JOBID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
JOBSTATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
JOBRESULT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
jobId: str
|
||||||
|
jobStatus: jobStatus_e
|
||||||
|
jobResult: jobResult_e
|
||||||
|
def __init__(self, jobId: _Optional[str] = ..., jobStatus: _Optional[_Union[jobStatus_e, str]] = ..., jobResult: _Optional[_Union[jobResult_e, str]] = ...) -> None: ...
|
||||||
|
|
||||||
|
class rpcResponse(_message.Message):
|
||||||
|
__slots__ = ("ts", "deviceId", "jobId", "jobStatus", "jobResult", "jobStatusByJobIdResponse")
|
||||||
|
TS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
DEVICEID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
JOBID_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
JOBSTATUS_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
JOBRESULT_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
JOBSTATUSBYJOBIDRESPONSE_FIELD_NUMBER: _ClassVar[int]
|
||||||
|
ts: int
|
||||||
|
deviceId: str
|
||||||
|
jobId: str
|
||||||
|
jobStatus: jobStatus_e
|
||||||
|
jobResult: jobResult_e
|
||||||
|
jobStatusByJobIdResponse: jobStatusByJobIdResponse_s
|
||||||
|
def __init__(self, ts: _Optional[int] = ..., deviceId: _Optional[str] = ..., jobId: _Optional[str] = ..., jobStatus: _Optional[_Union[jobStatus_e, str]] = ..., jobResult: _Optional[_Union[jobResult_e, str]] = ..., jobStatusByJobIdResponse: _Optional[_Union[jobStatusByJobIdResponse_s, _Mapping]] = ...) -> None: ...
|
||||||
|
|
@ -0,0 +1,218 @@
|
||||||
|
# --- Dynamic Theme Stylesheets ---
|
||||||
|
|
||||||
|
def get_light_theme_styles(scale=1.0):
|
||||||
|
|
||||||
|
log_font_size = max(10, int(11 * scale))
|
||||||
|
button_font_size = max(7, int(10 * scale))
|
||||||
|
|
||||||
|
return f"""
|
||||||
|
QMainWindow, QWidget {{
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #000;
|
||||||
|
}}
|
||||||
|
#LogPanel {{
|
||||||
|
font-family: "Courier New", Consolas, monospace;
|
||||||
|
font-size: {log_font_size}pt;
|
||||||
|
background-color: #212121;
|
||||||
|
color: #eceff1;
|
||||||
|
border: 1px solid #455a64;
|
||||||
|
}}
|
||||||
|
QGroupBox {{
|
||||||
|
font-family: Arial;
|
||||||
|
border: 1px solid #4a4a4a;
|
||||||
|
border-radius: {int(8 * scale)}px;
|
||||||
|
margin-top: {int(6 * scale)}px;
|
||||||
|
}}
|
||||||
|
QGroupBox::title {{
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
subcontrol-position: top center;
|
||||||
|
padding: 0 {int(10 * scale)}px;
|
||||||
|
color: #000;
|
||||||
|
}}
|
||||||
|
QTabWidget::pane {{ border-top: 2px solid #c8c8c8; }}
|
||||||
|
QTabBar::tab {{
|
||||||
|
background: #e1e1e1; border: 1px solid #c8c8c8;
|
||||||
|
padding: {int(6 * scale)}px {int(15 * scale)}px;
|
||||||
|
border-top-left-radius: {int(4 * scale)}px; border-top-right-radius: {int(4 * scale)}px;
|
||||||
|
}}
|
||||||
|
QTabBar::tab:selected {{ background: #f0f0f0; border-bottom-color: #f0f0f0; }}
|
||||||
|
QFormLayout::label {{ color: #000; padding-top: {int(3 * scale)}px; }}
|
||||||
|
QLineEdit, QPlainTextEdit, QComboBox {{
|
||||||
|
background-color: #fff; border: 1px solid #c8c8c8;
|
||||||
|
border-radius: {int(4 * scale)}px; padding: {int(4 * scale)}px;
|
||||||
|
font-size: {max(7, int(9 * scale))}pt;
|
||||||
|
}}
|
||||||
|
QLineEdit:read-only {{ background-color: #e9e9e9; }}
|
||||||
|
QPushButton {{
|
||||||
|
background-color: #e1e1e1; border: 1px solid #c8c8c8;
|
||||||
|
padding: {int(5 * scale)}px {int(10 * scale)}px;
|
||||||
|
border-radius: {int(4 * scale)}px;
|
||||||
|
}}
|
||||||
|
QPushButton:hover {{ background-color: #dcdcdc; }}
|
||||||
|
QPushButton:pressed {{ background-color: #c8c8c8; }}
|
||||||
|
#RefreshButton, #ResetButton {{
|
||||||
|
padding: {int(6 * scale)}px {int(16 * scale)}px;
|
||||||
|
font-size: {button_font_size * 1.3}pt;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: {int(4*scale)}px;
|
||||||
|
}}
|
||||||
|
#RefreshButton {{
|
||||||
|
background-color: #2e7d32; /* A slightly darker green */
|
||||||
|
}}
|
||||||
|
#ResetButton {{
|
||||||
|
background-color: #c62828; /* A slightly darker red */
|
||||||
|
}}
|
||||||
|
#ChamberOpenDoorButton, #ChamberChgOnButton, #ChamberChgOffButton {{
|
||||||
|
padding: {int(8 * scale)}px;
|
||||||
|
font-size: {button_font_size}pt;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: {int(4*scale)}px;
|
||||||
|
}}
|
||||||
|
|
||||||
|
#ChamberOpenDoorButton {{ background-color: #E1E1E1; }}
|
||||||
|
#ChamberChgOnButton {{ background-color: #E1E1E1; }}
|
||||||
|
#ChamberChgOffButton {{ background-color: #E1E1E1; }}
|
||||||
|
|
||||||
|
#ChamberOpenDoorButton:hover {{ background-color: #3498DB; }}
|
||||||
|
#ChamberChgOnButton:hover {{ background-color: #229954; }}
|
||||||
|
#ChamberChgOffButton:hover {{ background-color: #c0392b; }}
|
||||||
|
|
||||||
|
QPushButton:disabled {{ background-color: #d3d3d3; color: #a0a0a0; }}
|
||||||
|
|
||||||
|
#ConnectButton, #DisconnectButton {{
|
||||||
|
padding: {int(6 * scale)}px {int(16 * scale)}px;
|
||||||
|
font-size: {button_font_size}pt;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: {int(4 * scale)}px;
|
||||||
|
color: white;
|
||||||
|
}}
|
||||||
|
|
||||||
|
#ConnectButton {{ background-color: #27ae60; }} /* Green */
|
||||||
|
#DisconnectButton {{ background-color: #c0392b; }} /* Red */
|
||||||
|
|
||||||
|
#ConnectButton:hover {{ background-color: #52be80; }}
|
||||||
|
#DisconnectButton:hover {{ background-color: #cd6155; }}
|
||||||
|
|
||||||
|
#ConnectButton:pressed {{ background-color: #52be80; }}
|
||||||
|
#DisconnectButton:pressed {{ background-color: #cd6155; }}
|
||||||
|
|
||||||
|
#ConnectButton:disabled, #DisconnectButton:disabled {{
|
||||||
|
background-color: #546e7a;
|
||||||
|
color: #90a4ae;
|
||||||
|
}}
|
||||||
|
|
||||||
|
#RefreshButton, #StartSwapButton {{ background-color: #27ae60; color: white; border: none; }}
|
||||||
|
#RefreshButton:hover, #StartSwapButton:hover {{ background-color: #229954; }}
|
||||||
|
#ResetButton, #AbortSwapButton {{ background-color: #c0392b; color: white; border: none; }}
|
||||||
|
#ResetButton:hover, #AbortSwapButton:hover {{ background-color: #c0392b; }}
|
||||||
|
#SendAudioButton {{ background-color: #3498db; color: white; border: none; font-size: {max(10, int(14 * scale))}px; }}
|
||||||
|
#SendAudioButton:hover {{ background-color: #2980b9; }}
|
||||||
|
QLabel[status="present"] {{ background-color: #2ecc71; 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(3*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; }}
|
||||||
|
QGroupBox#ChamberWidget {{ border: 2px solid #3498db; }}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_dark_theme_styles(scale=1.0):
|
||||||
|
|
||||||
|
log_font_size = max(10, int(11 * scale))
|
||||||
|
button_font_size = max(7, int(10 * scale))
|
||||||
|
|
||||||
|
return f"""
|
||||||
|
QMainWindow, QWidget {{ background-color: #2b2b2b; color: #f0f0f0; }}
|
||||||
|
#LogPanel {{
|
||||||
|
font-family: "Courier New", Consolas, monospace;
|
||||||
|
font-size: {log_font_size}pt;
|
||||||
|
background-color: #212121;
|
||||||
|
color: #eceff1;
|
||||||
|
border: 1px solid #455a64;
|
||||||
|
}}
|
||||||
|
QGroupBox {{
|
||||||
|
font-family: Arial; border: 1px solid #4a4a4a;
|
||||||
|
border-radius: {int(8 * scale)}px; margin-top: {int(6 * scale)}px;
|
||||||
|
}}
|
||||||
|
QGroupBox::title {{ subcontrol-origin: margin; subcontrol-position: top center; padding: 0 {int(10 * scale)}px; color: #f0f0f0; }}
|
||||||
|
QTabWidget::pane {{ border-top: 2px solid #4a4a4a; }}
|
||||||
|
QTabBar::tab {{
|
||||||
|
background: #3c3c3c; border: 1px solid #4a4a4a; color: #f0f0f0;
|
||||||
|
padding: {int(6 * scale)}px {int(15 * scale)}px;
|
||||||
|
border-top-left-radius: {int(4 * scale)}px; border-top-right-radius: {int(4 * scale)}px;
|
||||||
|
}}
|
||||||
|
QTabBar::tab:selected {{ background: #2b2b2b; border-bottom-color: #2b2b2b; }}
|
||||||
|
QFormLayout::label {{ color: #f0f0f0; padding-top: {int(3 * scale)}px; }}
|
||||||
|
QLineEdit, QPlainTextEdit, QComboBox {{
|
||||||
|
background-color: #3c3c3c; border: 1px solid #4a4a4a;
|
||||||
|
border-radius: {int(4 * scale)}px; padding: {int(4 * scale)}px; color: #f0f0f0;
|
||||||
|
font-size: {max(7, int(9 * scale))}pt;
|
||||||
|
}}
|
||||||
|
QLineEdit:read-only {{ background-color: #333333; }}
|
||||||
|
QPushButton {{
|
||||||
|
background-color: #555555; border: 1px solid #4a4a4a;
|
||||||
|
padding: {int(5 * scale)}px {int(10 * scale)}px;
|
||||||
|
border-radius: {int(4 * scale)}px; color: #f0f0f0;
|
||||||
|
}}
|
||||||
|
QPushButton:hover {{ background-color: #6a6a6a; }}
|
||||||
|
QPushButton:pressed {{ background-color: #4a4a4a; }}
|
||||||
|
QPushButton:disabled {{ background-color: #404040; color: #888888; }}
|
||||||
|
#RefreshButton, #ResetButton {{
|
||||||
|
padding: {int(6 * scale)}px {int(16 * scale)}px;
|
||||||
|
font-size: {button_font_size * 1.3}pt;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: {int(4*scale)}px;
|
||||||
|
}}
|
||||||
|
#RefreshButton {{
|
||||||
|
background-color: #2e7d32; /* A slightly darker green */
|
||||||
|
}}
|
||||||
|
#ResetButton {{
|
||||||
|
background-color: #c62828; /* A slightly darker red */
|
||||||
|
}}
|
||||||
|
#ChamberOpenDoorButton, #ChamberChgOnButton, #ChamberChgOffButton {{
|
||||||
|
padding: {int(8 * scale)}px;
|
||||||
|
font-size: {button_font_size}pt;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: {int(4*scale)}px;
|
||||||
|
}}
|
||||||
|
|
||||||
|
#ChamberOpenDoorButton {{ background-color: #3C3C3C; }}
|
||||||
|
#ChamberChgOnButton {{ background-color: #3C3C3C; }}
|
||||||
|
#ChamberChgOffButton {{ background-color: #3C3C3C; }}
|
||||||
|
|
||||||
|
#ChamberOpenDoorButton:hover {{ background-color: #607d8b; }}
|
||||||
|
#ChamberChgOnButton:hover {{ background-color: #52be80; }}
|
||||||
|
#ChamberChgOffButton:hover {{ background-color: #cd6155; }}
|
||||||
|
|
||||||
|
#ConnectButton, #DisconnectButton {{
|
||||||
|
padding: {int(6 * scale)}px {int(16 * scale)}px;
|
||||||
|
font-size: {button_font_size}pt;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: {int(4*scale)}px;
|
||||||
|
color: white;
|
||||||
|
}}
|
||||||
|
|
||||||
|
#ConnectButton {{ background-color: #27ae60; }} /* Green */
|
||||||
|
#DisconnectButton {{ background-color: #c0392b; }} /* Red */
|
||||||
|
|
||||||
|
#ConnectButton:hover {{ background-color: #52be80; }}
|
||||||
|
#DisconnectButton:hover {{ background-color: #cd6155; }}
|
||||||
|
|
||||||
|
#ConnectButton:pressed {{ background-color: #52be80; }}
|
||||||
|
#DisconnectButton:pressed {{ background-color: #cd6155; }}
|
||||||
|
|
||||||
|
#ConnectButton:disabled, #DisconnectButton:disabled {{
|
||||||
|
background-color: #546e7a;
|
||||||
|
color: #90a4ae;
|
||||||
|
}}
|
||||||
|
|
||||||
|
#RefreshButton, #StartSwapButton {{ background-color: #27ae60; color: white; border: none; }}
|
||||||
|
#RefreshButton:hover, #StartSwapButton:hover {{ background-color: #52be80; }}
|
||||||
|
#ResetButton, #AbortSwapButton {{ background-color: #c0392b; color: white; border: none; }}
|
||||||
|
#ResetButton:hover, #AbortSwapButton:hover {{ background-color: #cd6155; }}
|
||||||
|
#SendAudioButton {{ background-color: #3498db; color: white; border: none; font-size: {max(10, int(14 * scale))}px; }}
|
||||||
|
#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="absent"] {{ background-color: #e74c3c; color: white; border-radius: {int(4*scale)}px; padding: {int(3*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; }}
|
||||||
|
QGroupBox#ChamberWidget {{ border: 2px solid #3498db; }}
|
||||||
|
"""
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
# --- REPLACE the entire content of ui/widgets.py with this ---
|
||||||
|
|
||||||
|
from PyQt6.QtWidgets import (
|
||||||
|
QGroupBox, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QFrame, QPushButton, QFormLayout
|
||||||
|
)
|
||||||
|
from PyQt6.QtCore import Qt, pyqtSignal # <-- IMPORT pyqtSignal
|
||||||
|
from PyQt6.QtGui import QFont
|
||||||
|
|
||||||
|
class ChamberWidget(QGroupBox):
|
||||||
|
# --- ADD SIGNALS HERE ---
|
||||||
|
open_door_requested = pyqtSignal()
|
||||||
|
chg_on_requested = pyqtSignal()
|
||||||
|
chg_off_requested = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, title="CHAMBER - X", scale=1.0):
|
||||||
|
super().__init__(title)
|
||||||
|
self.setObjectName("ChamberWidget")
|
||||||
|
self.setFont(QFont("Arial", max(8, int(9 * scale)), QFont.Weight.Bold))
|
||||||
|
|
||||||
|
main_layout = QVBoxLayout(self)
|
||||||
|
main_layout.setSpacing(max(2, int(4 * scale)))
|
||||||
|
|
||||||
|
# This section creates the 'id_field' that was missing
|
||||||
|
id_layout = QHBoxLayout()
|
||||||
|
id_layout.addWidget(QLabel("BAT ID: "))
|
||||||
|
self.id_field = self._create_data_field(scale) # This line ensures self.id_field exists
|
||||||
|
id_layout.addWidget(self.id_field)
|
||||||
|
main_layout.addLayout(id_layout)
|
||||||
|
|
||||||
|
columns_layout = QHBoxLayout()
|
||||||
|
battery_form_layout = QFormLayout()
|
||||||
|
battery_form_layout.setVerticalSpacing(max(2, int(4 * scale)))
|
||||||
|
self.battery_status_label = self._create_status_label("ABSENT", scale)
|
||||||
|
self.battery_status_label.setProperty("status", "absent")
|
||||||
|
self.soc_field = self._create_data_field(scale)
|
||||||
|
self.voltage_field = self._create_data_field(scale)
|
||||||
|
self.temp_field = self._create_data_field(scale)
|
||||||
|
self.battery_fault_field = self._create_data_field(scale)
|
||||||
|
|
||||||
|
battery_form_layout.addRow("Status:", self.battery_status_label)
|
||||||
|
battery_form_layout.addRow("SOC:", self.soc_field)
|
||||||
|
battery_form_layout.addRow("Voltage:", self.voltage_field)
|
||||||
|
battery_form_layout.addRow("Temp:", self.temp_field)
|
||||||
|
battery_form_layout.addRow("Fault:", self.battery_fault_field)
|
||||||
|
|
||||||
|
separator_line = QFrame()
|
||||||
|
separator_line.setFrameShape(QFrame.Shape.VLine)
|
||||||
|
separator_line.setFrameShadow(QFrame.Shadow.Sunken)
|
||||||
|
|
||||||
|
charger_form_layout = QFormLayout()
|
||||||
|
charger_form_layout.setVerticalSpacing(max(2, int(4 * scale)))
|
||||||
|
self.charger_status_label = self._create_status_label("OFF", scale)
|
||||||
|
self.charger_status_label.setProperty("status", "absent")
|
||||||
|
self.slot_temp_field = self._create_data_field(scale)
|
||||||
|
self.chg_temp_field = self._create_data_field(scale)
|
||||||
|
self.door_status_field = self._create_data_field(scale)
|
||||||
|
self.charger_fault_field = self._create_data_field(scale)
|
||||||
|
|
||||||
|
charger_form_layout.addRow("Chg Status:", self.charger_status_label)
|
||||||
|
charger_form_layout.addRow("Chg Temp:", self.chg_temp_field)
|
||||||
|
charger_form_layout.addRow("Slot Temp:", self.slot_temp_field)
|
||||||
|
charger_form_layout.addRow("Door Status:", self.door_status_field)
|
||||||
|
charger_form_layout.addRow("Fault:", self.charger_fault_field)
|
||||||
|
|
||||||
|
columns_layout.addLayout(battery_form_layout)
|
||||||
|
columns_layout.addWidget(separator_line)
|
||||||
|
columns_layout.addLayout(charger_form_layout)
|
||||||
|
main_layout.addLayout(columns_layout)
|
||||||
|
|
||||||
|
main_layout.addStretch()
|
||||||
|
|
||||||
|
button_layout = QHBoxLayout()
|
||||||
|
self.open_door_btn = QPushButton("OPEN DOOR")
|
||||||
|
self.chg_on_btn = QPushButton("CHG ON")
|
||||||
|
self.chg_off_btn = QPushButton("CHG OFF")
|
||||||
|
|
||||||
|
self.open_door_btn.setObjectName("ChamberOpenDoorButton")
|
||||||
|
self.chg_on_btn.setObjectName("ChamberChgOnButton")
|
||||||
|
self.chg_off_btn.setObjectName("ChamberChgOffButton")
|
||||||
|
|
||||||
|
self.open_door_btn.clicked.connect(self.open_door_requested.emit)
|
||||||
|
self.chg_on_btn.clicked.connect(self.chg_on_requested.emit)
|
||||||
|
self.chg_off_btn.clicked.connect(self.chg_off_requested.emit)
|
||||||
|
|
||||||
|
button_layout.addWidget(self.open_door_btn)
|
||||||
|
button_layout.addWidget(self.chg_on_btn)
|
||||||
|
button_layout.addWidget(self.chg_off_btn)
|
||||||
|
main_layout.addLayout(button_layout)
|
||||||
|
|
||||||
|
# ... (the rest of the class is unchanged) ...
|
||||||
|
def _create_status_label(self, text, scale):
|
||||||
|
label = QLabel(text)
|
||||||
|
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
label.setFont(QFont("Arial", max(7, int(8 * scale)), QFont.Weight.Bold))
|
||||||
|
return label
|
||||||
|
|
||||||
|
def _create_data_field(self, scale):
|
||||||
|
field = QLineEdit("N/A")
|
||||||
|
field.setReadOnly(True)
|
||||||
|
field.setFont(QFont("Arial", max(7, int(8 * scale))))
|
||||||
|
return field
|
||||||
|
|
||||||
|
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("chargerPresent") == 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", "N/A"))
|
||||||
|
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.slot_temp_field.setText(f'{data.get("slotTemperature", 0) / 10.0:.1f} °C')
|
||||||
|
self.chg_temp_field.setText(f'{data.get("chargerTemp", 0) / 10.0:.1f} °C')
|
||||||
|
self.charger_fault_field.setText(str(data.get("chargerFaultCode", 0)))
|
||||||
|
door_status = "CLOSED" if data.get("doorStatus") == 1 else "OPEN"
|
||||||
|
self.door_status_field.setText(door_status)
|
||||||
|
|
||||||
|
def reset_to_default(self):
|
||||||
|
"""Resets all fields in this chamber widget to their default state."""
|
||||||
|
self.battery_status_label.setText("ABSENT")
|
||||||
|
self.battery_status_label.setProperty("status", "absent")
|
||||||
|
self.charger_status_label.setText("CHARGER OFF")
|
||||||
|
self.charger_status_label.setProperty("status", "absent")
|
||||||
|
|
||||||
|
# Re-apply the stylesheet for the status labels
|
||||||
|
for widget in [self.battery_status_label, self.charger_status_label]:
|
||||||
|
widget.style().unpolish(widget)
|
||||||
|
widget.style().polish(widget)
|
||||||
|
|
||||||
|
self.id_field.setText("N/A")
|
||||||
|
self.soc_field.setText("N/A")
|
||||||
|
self.voltage_field.setText("N/A")
|
||||||
|
self.temp_field.setText("N/A")
|
||||||
|
self.battery_fault_field.setText("N/A")
|
||||||
|
self.slot_temp_field.setText("N/A")
|
||||||
|
self.chg_temp_field.setText("N/A")
|
||||||
|
self.door_status_field.setText("N/A")
|
||||||
|
self.charger_fault_field.setText("N/A")
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
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)
|
||||||