SXXXXXXX_PyBusMonitor1553/pybusmonitor1553/__main__.py

311 lines
12 KiB
Python

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('<H', data_bytes[i:i+2])[0]
raw_words.append(word)
# Accoda aggiornamento invece di usare root.after() diretto
app.queue_message_update(short_name, msg, raw_words)
# Accoda aggiornamento status invece di usare root.after() diretto
app.queue_status_update(True, tx_count[0], rx_count[0], "")
# Wrap scheduler send to count TX
original_send = network.send
def counted_send(data, ip, port):
tx_count[0] += 1
return original_send(data, ip, port)
network.send = counted_send
# Register callback for sent messages (A messages)
def on_message_sent(msg):
if not msg.IS_TRANSMIT: # BC->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()