235 lines
9.4 KiB
Python
235 lines
9.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
ARTOS - Advanced Radar Test & Orchestration System
|
|
Main entry point for the modular docking GUI.
|
|
"""
|
|
import sys
|
|
import os
|
|
import logging
|
|
import threading
|
|
import time
|
|
from datetime import datetime
|
|
|
|
# Add external logger module to path
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '_external', 'externals', 'python-tkinter-logger'))
|
|
|
|
# Ensure local imports work correctly
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from pymsc.core.bus_1553_module import Bus1553Module
|
|
from pymsc.gui.main_docking_window import MainDockingWindow
|
|
from pymsc.utils.profiler import print_stats, save_stats_to_csv
|
|
|
|
|
|
# Global flag to control the time update thread
|
|
_time_update_running = False
|
|
_time_update_thread = None
|
|
_logger_system = None
|
|
|
|
|
|
def update_radar_datetime_periodically(bus_module):
|
|
"""
|
|
Background thread that updates msg_a1 date and time every 2 seconds.
|
|
This keeps the radar's mission clock synchronized with system time.
|
|
|
|
Args:
|
|
bus_module: Bus1553Module instance to access MessageDB
|
|
"""
|
|
global _time_update_running
|
|
|
|
logger = logging.getLogger('ARTOS.TimeSync')
|
|
logger.info("Date/time update thread started (2-second interval)")
|
|
|
|
# Get MessageDB from BusMonitorCore (the correct instance that's being used)
|
|
try:
|
|
messagedb = bus_module.core._messagedb
|
|
if messagedb is None:
|
|
logger.error("MessageDB not cached in BusMonitorCore - date/time sync disabled")
|
|
return
|
|
except AttributeError as e:
|
|
logger.error(f"Could not access MessageDB from bus_module: {e}")
|
|
return
|
|
|
|
# Wait for MessageDB to be populated
|
|
max_wait = 10 # seconds
|
|
waited = 0
|
|
while _time_update_running and waited < max_wait:
|
|
all_messages = messagedb.getAllMessages()
|
|
if "A1" in all_messages:
|
|
logger.info(f"A1 message found in MessageDB - starting date/time synchronization")
|
|
break
|
|
time.sleep(0.5)
|
|
waited += 0.5
|
|
|
|
if waited >= max_wait:
|
|
available_keys = list(messagedb.getAllMessages().keys()) if messagedb else []
|
|
logger.error(f"A1 message not found in MessageDB after {max_wait}s. Available: {available_keys}")
|
|
return
|
|
|
|
while _time_update_running:
|
|
try:
|
|
now = datetime.now()
|
|
|
|
# Get the actual A1 message singleton that gets sent to the radar
|
|
all_messages = messagedb.getAllMessages()
|
|
if "A1" not in all_messages:
|
|
logger.warning("A1 message disappeared from MessageDB")
|
|
time.sleep(2.0)
|
|
continue
|
|
|
|
a1_msg = messagedb.getMessage("A1")
|
|
|
|
# Update date fields in the actual message structure
|
|
year = now.year % 100
|
|
a1_msg.message.date_of_mission.set_year_of_mission(year)
|
|
a1_msg.message.date_of_mission.set_month_of_mission(now.month)
|
|
a1_msg.message.date_of_mission.set_day_of_mission(now.day)
|
|
|
|
# Update time field: seconds since midnight divided by 2 (LSB = 2s)
|
|
seconds_since_midnight = now.hour * 3600 + now.minute * 60 + now.second
|
|
a1_msg.message.time_of_mission.set(seconds_since_midnight // 2)
|
|
|
|
# The message will be sent automatically by the periodic frame sender
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error updating date/time: {e}", exc_info=True)
|
|
|
|
# Sleep for 2 seconds before next update
|
|
time.sleep(2.0)
|
|
|
|
logger.info("Date/time update thread stopped")
|
|
|
|
|
|
def main():
|
|
"""
|
|
ARTOS Application Entry Point.
|
|
Initializes the 1553 bus module and launches the modular docking GUI.
|
|
"""
|
|
# Create logs directory first
|
|
os.makedirs('logs', exist_ok=True)
|
|
|
|
# Initialize TkinterLogger IMMEDIATELY (before any logging)
|
|
# This ensures consistent formatting from the start
|
|
from tkinter_logger import TkinterLogger
|
|
|
|
# Create logger system without Tkinter widget initially (console + file only)
|
|
global _logger_system
|
|
_logger_system = TkinterLogger(tk_root=None)
|
|
_logger_system.setup(
|
|
enable_console=True,
|
|
enable_file=True,
|
|
file_path='logs/artos.log',
|
|
file_max_bytes=10 * 1024 * 1024, # 10MB
|
|
file_backup_count=5,
|
|
enable_tkinter=False, # Will add Tkinter handler later
|
|
root_level=logging.INFO
|
|
)
|
|
|
|
logger = logging.getLogger('ARTOS')
|
|
logger.info("=" * 60)
|
|
logger.info("Starting ARTOS - Advanced Radar Test & Orchestration System")
|
|
logger.info("=" * 60)
|
|
|
|
try:
|
|
# Initialize the 1553 bus module
|
|
logger.info("Initializing Bus 1553 Module...")
|
|
bus_module = Bus1553Module()
|
|
|
|
# Configure bus communication
|
|
config = {
|
|
'udp_send_ip': '127.0.0.1',
|
|
'udp_send_port': 51553,
|
|
'udp_recv_ip': '0.0.0.0',
|
|
'udp_recv_port': 61553
|
|
}
|
|
|
|
if bus_module.initialize(config):
|
|
logger.info("Bus 1553 Module initialized successfully.")
|
|
else:
|
|
logger.warning("Bus 1553 Module initialization returned False.")
|
|
|
|
# Start 1553 communication session automatically
|
|
logger.info("Starting 1553 Bus communication session...")
|
|
bus_module.start_session()
|
|
logger.info("1553 Bus session started - ready to communicate with radar.")
|
|
|
|
# Initialize date and time of mission with current date/time
|
|
logger.info("Setting current date and time in mission parameters...")
|
|
|
|
# Wait a moment for MessageDB to be fully populated
|
|
time.sleep(0.5)
|
|
|
|
try:
|
|
# Get MessageDB from BusMonitorCore (the correct instance that's being used)
|
|
messagedb = bus_module.core._messagedb
|
|
if messagedb is None:
|
|
logger.warning("MessageDB not cached in BusMonitorCore")
|
|
logger.info("Date/time will be set by periodic update thread once MessageDB is available")
|
|
else:
|
|
# Check if A1 message exists in MessageDB
|
|
all_messages = messagedb.getAllMessages()
|
|
if "A1" not in all_messages:
|
|
logger.warning(f"A1 message not yet in MessageDB. Available messages: {list(all_messages.keys())}")
|
|
logger.info("Date/time will be set by periodic update thread once A1 is available")
|
|
else:
|
|
now = datetime.now()
|
|
|
|
# Get the actual A1 message singleton that gets sent to the radar
|
|
a1_msg = messagedb.getMessage("A1")
|
|
|
|
# Set date: year (last 2 digits), month, day
|
|
year = now.year % 100 # Last 2 digits (e.g., 2026 -> 26)
|
|
a1_msg.message.date_of_mission.set_year_of_mission(year)
|
|
a1_msg.message.date_of_mission.set_month_of_mission(now.month)
|
|
a1_msg.message.date_of_mission.set_day_of_mission(now.day)
|
|
|
|
# Set time: seconds since midnight divided by 2 (LSB = 2s)
|
|
seconds_since_midnight = now.hour * 3600 + now.minute * 60 + now.second
|
|
a1_msg.message.time_of_mission.set(seconds_since_midnight // 2)
|
|
|
|
logger.info(f"Mission date/time set to: {now.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
logger.info(f" - Year: {year}, Month: {now.month}, Day: {now.day}")
|
|
logger.info(f" - Time value (secs//2): {seconds_since_midnight // 2}")
|
|
except Exception as e:
|
|
logger.error(f"Failed to initialize date/time: {e}", exc_info=True)
|
|
|
|
# Start periodic date/time update thread (every 2 seconds)
|
|
# This keeps msg_a1 (Radar Settings) date/time synchronized with system time
|
|
global _time_update_running, _time_update_thread
|
|
_time_update_running = True
|
|
_time_update_thread = threading.Thread(
|
|
target=update_radar_datetime_periodically,
|
|
args=(bus_module,), # Pass bus_module to access MessageDB
|
|
daemon=True,
|
|
name="RadarTimeSync"
|
|
)
|
|
_time_update_thread.start()
|
|
logger.info("Started periodic date/time synchronization (msg_a1, every 2 seconds)")
|
|
|
|
# Create and launch the modular docking GUI
|
|
logger.info("Launching ARTOS Docking GUI...")
|
|
app = MainDockingWindow(bus_module, logger_system=_logger_system)
|
|
|
|
# Start widget refresh loops to display live data
|
|
logger.info("Starting GUI refresh loops...")
|
|
app._start_refresh()
|
|
logger.info("GUI refresh active - widgets will update automatically.")
|
|
|
|
# Store cleanup references in app for use in _on_close
|
|
app._cleanup_time_thread = lambda: setattr(globals(), '_time_update_running', False)
|
|
app._time_update_thread = _time_update_thread
|
|
|
|
# Note: MainDockingWindow handles WM_DELETE_WINDOW with its own _on_close()
|
|
# which calls os._exit(0) for immediate termination
|
|
|
|
# Start the GUI main loop
|
|
logger.info("ARTOS GUI ready. Starting main loop...")
|
|
app.run()
|
|
|
|
except Exception as e:
|
|
logger.error(f"Critical error during ARTOS startup: {e}", exc_info=True)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |