import time import sys import os import argparse import logging import tkinter as tk from .core.network import UdpHandler from .core.dispatcher import MessageDispatcher from .core.scheduler import TrafficScheduler from .core.controller import RadarController from .utils.printer import dump_message from .gui.main_window import BusMonitorApp from .lib1553.constants import StandbyStatus, IntBitStatus, DegradedStatus logger = logging.getLogger(__name__) #Configuration RX_IP = os.getenv("PYBM_RX_IP", "127.0.0.1") RX_PORT = int(os.getenv("PYBM_RX_PORT", str(61553))) TARGET_IP = os.getenv("PYBM_TARGET_IP", "127.0.0.1") TARGET_PORT = int(os.getenv("PYBM_TARGET_PORT", "51553")) def main_cli(): """Command-line interface mode (original behavior)""" print("--------------------------------------------------") print(" PyBusMonitor1553 - Active Controller (CLI Mode)") print("--------------------------------------------------") # 1. Initialize Components dispatcher = MessageDispatcher() network = UdpHandler(rx_ip=RX_IP, rx_port=RX_PORT) # 2. Initialize Radar Logic Controller radar_ctrl = RadarController() # 3. Initialize Scheduler with the Controller scheduler = TrafficScheduler(network, radar_ctrl, TARGET_IP, TARGET_PORT) # 4. Define the callback for received messages def on_packet(data, addr): header, messages = dispatcher.parse_packet(data) if messages: for msg in messages: if msg.IS_TRANSMIT: print(f"\n[RX] {msg.__class__.__name__} from RT{header.ta if header else '?'}") dump_message(msg) elif header and header.errors != 0: print(f"[RX] Server Error Code: {header.errors}") # 5. Start everything network.register_callback(on_packet) network.start() scheduler.start() print(f"System Running.") print(f"RX: {RX_IP}:{RX_PORT} | TX: {TARGET_IP}:{TARGET_PORT}") print("Press Ctrl+C to stop.") try: while True: time.sleep(1) except KeyboardInterrupt: print("\nStopping...") finally: scheduler.stop() network.stop() sys.exit(0) def main_gui(): """Graphical user interface mode""" # Enable quiet mode for scheduler from .core import scheduler as sched_module sched_module.QUIET_MODE = True # Initialize Tkinter root = tk.Tk() # Initialize Components first (radar_ctrl needed by GUI) dispatcher = MessageDispatcher() network = UdpHandler(rx_ip=RX_IP, rx_port=RX_PORT) radar_ctrl = RadarController() scheduler = TrafficScheduler(network, radar_ctrl, TARGET_IP, TARGET_PORT) # Create GUI with radar controller access app = BusMonitorApp(root, radar_controller=radar_ctrl) # Log banner AFTER TkinterLogger is initialized logger.info("=" * 50) logger.info("PyBusMonitor1553 - Active Controller (GUI Mode)") logger.info("=" * 50) # Counters for status bar tx_count = [0] rx_count = [0] # Define the callback for received messages def on_packet(data, addr): header, messages = dispatcher.parse_packet(data) rx_count[0] += 1 if messages: for msg in messages: if msg.IS_TRANSMIT: msg_name = msg.__class__.__name__ # Convert MsgB6 -> B6, MsgA1 -> A1, etc. short_name = msg_name.replace("Msg", "") # Log B7 status messages for debugging if short_name == "B7": logger.debug("=== Received B7 Status Tellback ===") try: mode_tb = msg.master_mode_tb mode_name = mode_tb.name if hasattr(mode_tb, 'name') else str(mode_tb) logger.debug(f" master_mode_tb = {mode_name} ({mode_tb})") stby = msg.standby_status stby_name = stby.name if hasattr(stby, 'name') else str(stby) logger.debug(f" standby_status = {stby_name} ({stby})") # CRITICAL: Monitor transition status during startup trans = msg.transition_status trans_name = trans.name if hasattr(trans, 'name') else str(trans) if startup_phase[0] > 0: logger.debug(f" [STARTUP Phase {startup_phase[0]}] transition_status = {trans_name}") desg = msg.designation_status desg_name = desg.name if hasattr(desg, 'name') else str(desg) logger.debug(f" designation_status = {desg_name}") ibit = msg.int_bit_status ibit_name = ibit.name if hasattr(ibit, 'name') else str(ibit) logger.debug(f" int_bit_status = {ibit_name}") freeze = msg.freeze_status freeze_name = freeze.name if hasattr(freeze, 'name') else str(freeze) logger.debug(f" freeze_status = {freeze_name}") degr = msg.degraded_status degr_name = degr.name if hasattr(degr, 'name') else str(degr) logger.debug(f" degraded_status = {degr_name}") # Warning if standby is still ON if stby != StandbyStatus.OFF and stby != 0: logger.warning(f"⚠ Radar still in STANDBY (status={stby_name})") # Warning if IBIT in progress or degraded if ibit != IntBitStatus.NORMAL and ibit != 0: logger.warning(f"⚠ IBIT in progress (status={ibit_name})") if degr != DegradedStatus.NORMAL and degr != 0: logger.warning(f"⚠ Radar degraded (status={degr_name})") except Exception as e: logger.error(f"Error logging B7 details: {e}") # Extract raw words if available raw_words = [] if hasattr(msg, '_raw_data') and msg._raw_data: import struct data_bytes = msg._raw_data for i in range(0, len(data_bytes), 2): if i + 1 < len(data_bytes): word = struct.unpack('RT messages (A messages) msg_name = msg.__class__.__name__ short_name = msg_name.replace("Msg", "") # Extract raw words from current message state raw_words = [] if hasattr(msg, '_data'): raw_words = list(msg._data)[:32] # Copy first 32 words # Accoda aggiornamento invece di usare root.after() diretto app.queue_message_update(short_name, msg, raw_words) scheduler.register_sent_callback(on_message_sent) # Wire up RUN/STOP button actions startup_phase = [0] # Track startup sequence: 0=stopped, 1-3=phases startup_cycle_count = [0] def advance_startup_phase(): """Advances through startup phases with proper timing""" if startup_phase[0] == 1: # Transition to Phase 2: Activate radar startup_phase[0] = 2 startup_cycle_count[0] = 0 radar_ctrl.apply_startup_sequence(2) # STANDBY→OFF, SILENCE→OFF # Schedule Phase 3 after 6 cycles (240ms @ 25Hz) root.after(240, advance_startup_phase) elif startup_phase[0] == 2: # Transition to Phase 3: Monitor completion startup_phase[0] = 3 radar_ctrl.apply_startup_sequence(3) logger.info("[STARTUP] ✓ Initialization complete - radar operational") def on_connect(): """Called when RUN button is pressed""" logger.info("Applying default radar configuration...") radar_ctrl.apply_defaults() # Sets PHASE 1: STANDBY=ON, SILENCE=ON # Initialize startup sequence startup_phase[0] = 1 startup_cycle_count[0] = 0 radar_ctrl.apply_startup_sequence(1) # Log Phase 1 logger.info("Starting network and scheduler...") network.start() scheduler.start() app.update_connection_status(True, tx_count[0], rx_count[0], "") # Schedule Phase 2 after 3 cycles (120ms @ 25Hz) root.after(120, advance_startup_phase) def on_disconnect(): """Called when STOP button is pressed""" startup_phase[0] = 0 # Reset startup sequence startup_cycle_count[0] = 0 logger.info("Stopping scheduler and network...") scheduler.stop() network.stop() app.update_connection_status(False, tx_count[0], rx_count[0], "") app._on_connect = on_connect app._on_disconnect = on_disconnect # Register callback but DON'T start automatically - wait for RUN button network.register_callback(on_packet) # Update initial status (disconnected) app.update_connection_status(False, 0, 0, "") logger.info(f"GUI Ready - RX: {RX_IP}:{RX_PORT} | TX: {TARGET_IP}:{TARGET_PORT}") logger.info("Press RUN to start communication") # Handle window close def on_closing(): if scheduler._running: scheduler.stop() if network._running: network.stop() app.shutdown() root.destroy() root.protocol("WM_DELETE_WINDOW", on_closing) # Run the GUI main loop try: root.mainloop() except KeyboardInterrupt: on_closing() def main(): """Main entry point with mode selection""" global TARGET_IP, TARGET_PORT parser = argparse.ArgumentParser( description='PyBusMonitor1553 - MIL-STD-1553 Bus Monitor', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=''' Examples: python -m pybusmonitor1553 # Launch GUI mode (default) python -m pybusmonitor1553 --cli # Launch CLI mode python -m pybusmonitor1553 --gui # Launch GUI mode explicitly ''' ) parser.add_argument('--cli', action='store_true', help='Run in command-line interface mode') parser.add_argument('--gui', action='store_true', help='Run in graphical user interface mode (default)') parser.add_argument('--target', type=str, default=None, help=f'Target IP address (default: {TARGET_IP})') parser.add_argument('--port', type=int, default=None, help=f'Target port (default: {TARGET_PORT})') args = parser.parse_args() # Update globals if specified if args.target: TARGET_IP = args.target if args.port: TARGET_PORT = args.port if args.cli: main_cli() else: main_gui() if __name__ == "__main__": main()