# -*- 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()