# -*- 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 # 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 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) # Setup basic logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('logs/artos.log'), logging.StreamHandler() ] ) 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) # 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.") # Graceful shutdown handler def shutdown(): """Cleanup on application exit.""" logger.info("Shutting down ARTOS...") # Stop time update thread global _time_update_running if _time_update_running: _time_update_running = False logger.info("Stopping date/time synchronization thread...") if _time_update_thread and _time_update_thread.is_alive(): _time_update_thread.join(timeout=3.0) # Stop bus session if running if bus_module.is_running: bus_module.stop_session() # Export performance metrics logger.info("Exporting performance metrics...") save_stats_to_csv("performance_report.csv") print_stats() logger.info("ARTOS shutdown complete.") app.root.quit() app.root.destroy() app.root.protocol("WM_DELETE_WINDOW", shutdown) # 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()