import struct import ctypes class MessageBase: """ Abstract base class for all 1553 Messages. Manages the underlying data buffer (32 words of 16 bits) and handles serialization/deserialization ensuring Big Endian format (Network Byte Order) typically used in 1553 transmission over UDP. EXTENSIBILITY: All fields defined via descriptors (BitField, EnumField, ScaledField) are fully writable, enabling command generation and parameter injection for future control/simulation features. Field values modified via assignment are automatically reflected in pack() output. """ # Subaddress ID must be defined in subclasses SUBADDRESS = None # Direction (True=Transmit/RT->BC, False=Receive/BC->RT) IS_TRANSMIT = False # Rate in Hz (defined in subclasses, used by scheduler) RATE_HZ = None def __init__(self, raw_bytes=None): """ Initialize the message. Args: raw_bytes (bytes, optional): If provided, populates the message from raw bytes (e.g. received from UDP). """ # Internal storage: List of 32 integers (16-bit) # We initialize with 0. # Note: 1553 messages max length is 32 words. self._data = [0] * 32 # Store raw bytes for GUI display self._raw_data = None if raw_bytes: self.unpack(raw_bytes) @property def data(self): """ Exposes the internal data buffer to Field descriptors. The descriptors in fields.py expect an object with a 'data' attribute that supports indexing (get/set). """ return self._data def pack(self): """ Serializes the message words into bytes using Big Endian format. Returns: bytes: The binary representation of the 32 words (64 bytes). """ # Pack 32 unsigned shorts (H) in Big Endian (>) return struct.pack(f">{len(self._data)}H", *self._data) def unpack(self, raw_bytes): """ Populates the message words from raw bytes using Big Endian format. Args: raw_bytes (bytes): The raw data received. """ if not raw_bytes: return # Store raw bytes for GUI display self._raw_data = raw_bytes # Calculate how many words we can read num_bytes = len(raw_bytes) num_words = num_bytes // 2 # Clamp to 32 words max if num_words > 32: num_words = 32 # Unpack available words unpacked = struct.unpack(f">{num_words}H", raw_bytes[:num_words * 2]) # Update internal buffer for i in range(num_words): self._data[i] = unpacked[i] def __repr__(self): """ String representation for debugging. Shows Subaddress and non-zero words. """ direction = "TX" if self.IS_TRANSMIT else "RX" sa = self.SUBADDRESS if self.SUBADDRESS is not None else "??" # Show only first few words or non-zero ones for brevity words_str = " ".join([f"{w:04X}" for w in self._data[:8]]) return f"<{self.__class__.__name__} ({direction} SA:{sa}) Data[{words_str}...]>"