SXXXXXXX_PyMsc/pymsc/core/introspection.py
2025-12-10 11:47:46 +01:00

143 lines
5.4 KiB
Python

import ctypes
from enum import Enum
def inspect_structure(obj):
"""
Analizza ricorsivamente una struttura o unione ctypes e ritorna un dizionario
che descrive la gerarchia dei dati, i valori attuali e i metadati per la GUI.
"""
# 1. Gestione Smart Types (es. Velocity, Semicircle)
# Se la classe ha una property 'value' o 'degrees', la trattiamo come un valore singolo
cls = obj.__class__
if hasattr(cls, 'value') and isinstance(getattr(cls, 'value'), property):
return {
"type": "smart_value",
"value": obj.value,
"obj_ref": obj, # Riferimento all'oggetto per il setter
"attr_name": "value" # Nome dell'attributo da settare
}
if hasattr(cls, 'degrees') and isinstance(getattr(cls, 'degrees'), property):
return {
"type": "smart_angle",
"value": obj.degrees,
"obj_ref": obj,
"attr_name": "degrees"
}
# 2. Gestione Strutture Complesse (Structure o Union)
if hasattr(obj, "_fields_"):
children = {}
# Recuperiamo eventuali metadati per gli Enum definiti nella classe
# Esempio: _enums_ = {"target_history": TargetHistory}
enums_map = getattr(obj, "_enums_", {})
for field_desc in obj._fields_:
field_name = field_desc[0]
field_type = field_desc[1]
# Saltiamo i campi 'raw' se esiste una vista 'bits' o 'fields' (tipico delle Union)
if field_name == "raw" and (hasattr(obj, "bits") or hasattr(obj, "fields")):
continue
field_val = getattr(obj, field_name)
# Caso A: Campo mappato su Enum
if field_name in enums_map:
enum_cls = enums_map[field_name]
try:
current_enum = enum_cls(field_val)
except ValueError:
# Se il valore raw non è nell'enum, gestiamo l'errore o usiamo un fallback
current_enum = field_val
children[field_name] = {
"type": "enum",
"value": current_enum,
"enum_cls": enum_cls,
"obj_ref": obj,
"attr_name": field_name
}
# Caso B: Altra Struttura/Union (Ricorsione)
elif hasattr(field_val, "_fields_"):
children[field_name] = inspect_structure(field_val)
# Caso C: Primitiva (int, float, array fissi)
else:
# Gestione array ctypes semplici
if hasattr(field_type, "_length_"):
# Per ora semplifichiamo gli array come stringa o lista non editabile
val_repr = str(list(field_val))
children[field_name] = {"type": "readonly", "value": val_repr}
else:
children[field_name] = {
"type": "primitive",
"value": field_val,
"obj_ref": obj,
"attr_name": field_name
}
return {"type": "compound", "children": children}
# Fallback
return {"type": "unknown", "value": str(obj)}
def structure_to_dict(obj):
"""
Convert a ctypes.Structure (possibly containing nested structures and enums)
into a nested Python dict with primitive values and enum names where possible.
"""
# Handle smart types first similar to inspect_structure
cls = obj.__class__
if hasattr(cls, 'value') and isinstance(getattr(cls, 'value'), property):
return obj.value
if hasattr(cls, 'degrees') and isinstance(getattr(cls, 'degrees'), property):
return obj.degrees
if hasattr(obj, '_fields_'):
result = {}
enums_map = getattr(obj, '_enums_', {})
for field_desc in obj._fields_:
field_name = field_desc[0]
field_type = field_desc[1]
# Skip raw fields exposed by unions
if field_name == 'raw' and (hasattr(obj, 'bits') or hasattr(obj, 'fields')):
continue
try:
val = getattr(obj, field_name)
except Exception:
result[field_name] = None
continue
if field_name in enums_map:
enum_cls = enums_map[field_name]
try:
result[field_name] = enum_cls(val).name
except Exception:
result[field_name] = val
elif hasattr(val, '_fields_'):
result[field_name] = structure_to_dict(val)
else:
# ctypes arrays -> convert to list
if hasattr(field_type, '_length_'):
try:
result[field_name] = list(val)
except Exception:
result[field_name] = str(val)
else:
# Primitive
# Convert c_uint8/16/32 etc. to Python int
try:
if hasattr(val, 'value'):
result[field_name] = val.value
else:
result[field_name] = val
except Exception:
result[field_name] = val
return result
# Fallback: return string
return str(obj)