# morphology.py import time from typing import Any, Callable, Dict, List, Union MorphologyType = Union[str, Callable[[str], List[Dict[str, Any]]]] def generate_id() -> float: """ Zeitbasierte float-ID (ähnlich wie im Buch). """ now = time.time() return 1.0 / now # ------------------------------------------------------------ # Morphologie-API (duck-typed, wie im Erlang-Original) # ------------------------------------------------------------ def get_InitSensor(morphology: MorphologyType) -> Dict[str, Any]: sensors = get_Sensors(morphology) if not sensors: raise ValueError("Morphology has no sensors.") return sensors[0] def get_InitActuator(morphology: MorphologyType) -> Dict[str, Any]: actuators = get_Actuators(morphology) if not actuators: raise ValueError("Morphology has no actuators.") return actuators[0] def get_Sensors(morphology: MorphologyType) -> List[Dict[str, Any]]: fn = _resolve_morphology(morphology) return fn("sensors") def get_Actuators(morphology: MorphologyType) -> List[Dict[str, Any]]: fn = _resolve_morphology(morphology) return fn("actuators") def _resolve_morphology(morphology: MorphologyType) -> Callable[[str], List[Dict[str, Any]]]: """ Akzeptiert: - eine Callable Morphologie-Funktion (z.B. xor_mimic) - einen String (z.B. "xor_mimic") -> per Registry auflösen - ein Modul-Objekt (z.B. import morphology) -> Standard 'xor_mimic' aus dem Modul """ # 1) Bereits eine Callable-Funktion? (z.B. xor_mimic) if callable(morphology): return morphology # 2) String -> Registry if isinstance(morphology, str): reg = { "xor_mimic": xor_mimic, # weitere Morphologien hier registrieren... } if morphology in reg: return reg[morphology] raise ValueError(f"Unknown morphology name: {morphology}") # 3) Modul-Objekt: versuche eine Standard-Morphologie-Funktion daraus zu nehmen # Hier nehmen wir 'xor_mimic' als Default (du kannst das generalisieren). try: # Ist es ein Modul mit einer Funktion 'xor_mimic'? if hasattr(morphology, "xor_mimic") and callable(getattr(morphology, "xor_mimic")): return getattr(morphology, "xor_mimic") except Exception: pass raise TypeError("morphology must be a callable, a module with 'xor_mimic', or a registered string key") # ------------------------------------------------------------ # Beispiel-Morphologie: XOR (wie im Buch) # ------------------------------------------------------------ def xor_mimic(kind: str) -> List[Dict[str, Any]]: """ Liefert je nach 'kind' ('sensors' | 'actuators') die Definitionen. Felder sind so benannt, dass sie direkt mit unserem Genotype/Phenotype-Code kompatibel sind (vector_length statt 'vl', etc.). """ if kind == "sensors": return [ { "id": generate_id(), "name": "xor_GetInput", # Sensorfunktion (muss in deinem Sensor-Actor implementiert sein) "vector_length": 2, "scape": {"private": "xor_sim"} # optional, falls du Scapes nutzt } ] elif kind == "actuators": return [ { "id": generate_id(), "name": "xor_SendOutput", # Aktuatorfunktion (muss in deinem Actuator-Actor implementiert sein) "vector_length": 1, "scape": {"private": "xor_sim"} # optional } ] else: raise ValueError(f"xor_mimic: unsupported kind '{kind}', expected 'sensors' or 'actuators'")