SXXXXXXX_ControlPanel/controlpanel/core/sfp_structures.py
2025-10-16 09:52:34 +02:00

162 lines
5.0 KiB
Python

# core/sfp_structures.py
"""
Defines the ctypes Structures for the Simple Fragmentation Protocol (SFP)
and the application-specific image data format.
This centralizes data structure definitions, allowing them to be shared
between the transport layer (which uses SFPHeader) and the application
layer (which interprets the image-specific structures).
"""
import ctypes
# --- SFP Transport Layer Structures ---
class SFPHeader(ctypes.Structure):
"""Structure representing the SFP header (32 bytes)."""
_pack_ = 1
_fields_ = [
("SFP_MARKER", ctypes.c_uint8),
("SFP_DIRECTION", ctypes.c_uint8),
("SFP_PROT_VER", ctypes.c_uint8),
("SFP_PT_SPARE", ctypes.c_uint8),
("SFP_TAG", ctypes.c_uint8),
("SFP_SRC", ctypes.c_uint8),
("SFP_FLOW", ctypes.c_uint8),
("SFP_TID", ctypes.c_uint8),
("SFP_FLAGS", ctypes.c_uint8),
("SFP_WIN", ctypes.c_uint8),
("SFP_ERR", ctypes.c_uint8),
("SFP_ERR_INFO", ctypes.c_uint8),
("SFP_TOTFRGAS", ctypes.c_uint16),
("SFP_FRAG", ctypes.c_uint16),
("SFP_RECTYPE", ctypes.c_uint8),
("SFP_RECSPARE", ctypes.c_uint8),
("SFP_PLDAP", ctypes.c_uint8),
("SFP_PLEXT", ctypes.c_uint8),
("SFP_RECCOUNTER", ctypes.c_uint16),
("SFP_PLSIZE", ctypes.c_uint16),
("SFP_TOTSIZE", ctypes.c_uint32),
("SFP_PLOFFSET", ctypes.c_uint32),
]
@classmethod
def size(cls):
"""Returns the size of the structure in bytes."""
return ctypes.sizeof(cls)
@staticmethod
def get_field_offset(field_name: str) -> int:
"""Returns the byte offset of a specific field within the structure."""
try:
return getattr(SFPHeader, field_name).offset
except AttributeError:
return -1
# --- Application Layer (Image Format) Structures ---
class DataTag(ctypes.Structure):
"""Structure representing a generic data tag (8 bytes)."""
_pack_ = 1
_fields_ = [
("ID", ctypes.c_uint8 * 2),
("VALID", ctypes.c_uint8),
("VERSION", ctypes.c_uint8),
("SIZE", ctypes.c_uint32),
]
@classmethod
def size(cls):
"""Returns the size of the structure in bytes."""
return ctypes.sizeof(cls)
class HeaderData(ctypes.Structure):
"""Structure representing the HEADER_DATA block (~48 bytes)."""
_pack_ = 1
_fields_ = [
("TYPE", ctypes.c_uint8),
("SUBTYPE", ctypes.c_uint8),
("LOGCOLORS", ctypes.c_uint8),
("IMG_RESERVED", ctypes.c_uint8),
("PRODINFO", ctypes.c_uint32 * 2),
("TOD", ctypes.c_uint16 * 2),
("RESERVED", ctypes.c_uint32),
("FCOUNTER", ctypes.c_uint32),
("TIMETAG", ctypes.c_uint32),
("NOMINALFRATE", ctypes.c_uint16),
("FRAMETAG", ctypes.c_uint16),
("OX", ctypes.c_uint16),
("OY", ctypes.c_uint16),
("DX", ctypes.c_uint16),
("DY", ctypes.c_uint16),
("STRIDE", ctypes.c_uint16),
("BPP", ctypes.c_uint8),
("COMP", ctypes.c_uint8),
("SPARE", ctypes.c_uint16),
("PALTYPE", ctypes.c_uint8),
("GAP", ctypes.c_uint8),
]
@classmethod
def size(cls):
"""Returns the size of the structure in bytes."""
return ctypes.sizeof(cls)
class GeoData(ctypes.Structure):
"""Structure representing the GEO_DATA block (~80 bytes)."""
_pack_ = 1
_fields_ = [
("INVMASK", ctypes.c_uint32),
("ORIENTATION", ctypes.c_float),
("LATITUDE", ctypes.c_float),
("LONGITUDE", ctypes.c_float),
("REF_X", ctypes.c_uint16),
("REF_Y", ctypes.c_uint16),
("SCALE_X", ctypes.c_float),
("SCALE_Y", ctypes.c_float),
("POI_ORIENTATION", ctypes.c_float),
("POI_LATITUDE", ctypes.c_float),
("POI_LONGITUDE", ctypes.c_float),
("POI_ALTITUDE", ctypes.c_float),
("POI_X", ctypes.c_uint16),
("POI_Y", ctypes.c_uint16),
("SPARE", ctypes.c_uint32 * 7),
]
@classmethod
def size(cls):
"""Returns the size of the structure in bytes."""
return ctypes.sizeof(cls)
class ImageLeaderData(ctypes.Structure):
"""Represents the complete Image Leader data structure (~1320 bytes)."""
_pack_ = 1
_fields_ = [
("HEADER_TAG", DataTag),
("HEADER_DATA", HeaderData),
("GEO_TAG", DataTag),
("GEO_DATA", GeoData),
("RESERVED_TAG", DataTag),
("RESERVED_DATA", ctypes.c_uint8 * 128),
("CM_TAG", DataTag),
("COLOUR_MAP", ctypes.c_uint32 * 256),
("PIXEL_TAG", DataTag),
]
@classmethod
def size(cls):
"""Returns the size of the structure in bytes."""
return ctypes.sizeof(cls)
@staticmethod
def get_reserved_data_size() -> int:
"""Returns the static size of the RESERVED_DATA field."""
return 128
@staticmethod
def get_colour_map_size() -> int:
"""Returns the static size of the COLOUR_MAP field in bytes."""
return 1024