120 lines
4.4 KiB
Python
120 lines
4.4 KiB
Python
# actors/cortex.py
|
||
import time
|
||
from actor import Actor
|
||
|
||
|
||
class Cortex(Actor):
|
||
def __init__(self, cid, exoself_pid, sensor_pids, neuron_pids, actuator_pids):
|
||
super().__init__(f"Cortex-{cid}")
|
||
self.cid = cid
|
||
self.sensors = sensor_pids
|
||
self.neurons = neuron_pids
|
||
self.actuators = actuator_pids
|
||
self.exoself_pid = exoself_pid
|
||
|
||
self.awaiting_sync = set() # AIDs, von denen wir im aktuellen Schritt noch Sync erwarten
|
||
self.fitness_acc = 0.0 # akkumulierte Fitness über die Episode
|
||
self.ef_acc = 0 # akkumulierte EndFlags im aktuellen Schritt/Episode
|
||
self.cycle_acc = 0 # Anzahl abgeschlossener Sense-Think-Act Zyklen
|
||
self.active = False # aktiv/inaktiv (wartet auf reactivate)
|
||
self._t0 = None # Startzeit der Episode
|
||
|
||
async def _kick_sensors(self):
|
||
for s in self.sensors:
|
||
await s.send(("sync",))
|
||
|
||
def _reset_for_new_cycle(self):
|
||
self.awaiting_sync = set(a.aid for a in self.actuators)
|
||
|
||
def _reset_for_new_episode(self):
|
||
self.fitness_acc = 0.0
|
||
self.ef_acc = 0
|
||
self.cycle_acc = 0
|
||
self._reset_for_new_cycle()
|
||
self._t0 = time.perf_counter()
|
||
self.active = True
|
||
|
||
async def run(self):
|
||
# Initialisierung: falls Actuatoren schon gesetzt sind, Episode starten
|
||
if self.actuators:
|
||
self._reset_for_new_episode()
|
||
await self._kick_sensors()
|
||
|
||
while True:
|
||
msg = await self.inbox.get()
|
||
tag = msg[0]
|
||
|
||
# Optional: Exoself kann AIDs registrieren, bevor wir starten
|
||
if tag == "register_actuators":
|
||
_, aids = msg
|
||
# Map aids auf echte Actuatoren falls nötig; hier übernehmen wir sie direkt
|
||
self.awaiting_sync = set(aids)
|
||
if not self.active:
|
||
self._reset_for_new_episode()
|
||
await self._kick_sensors()
|
||
continue
|
||
|
||
if tag == "sync" and self.active:
|
||
print("CORTEX: got sync message: ", msg)
|
||
# Aktuator meldet Schrittabschluss
|
||
_t, aid, fitness, halt_flag = msg
|
||
|
||
print("----------------")
|
||
print("_t:", _t)
|
||
print("aid:", aid)
|
||
print("fitness:", fitness)
|
||
print("halt_flag:", halt_flag)
|
||
print("----------------")
|
||
|
||
# akkumulieren
|
||
self.fitness_acc += float(fitness)
|
||
self.ef_acc += int(halt_flag)
|
||
|
||
# diesen Aktuator als "eingetroffen" markieren
|
||
if aid in self.awaiting_sync:
|
||
self.awaiting_sync.remove(aid)
|
||
|
||
print("CORTEX: awaiting sync: ", self.awaiting_sync)
|
||
|
||
# Wenn alle Aktuatoren gemeldet haben, ist ein Zyklus fertig
|
||
if not self.awaiting_sync:
|
||
print("CORTEX: cycle completed.")
|
||
self.cycle_acc += 1
|
||
|
||
# Episodenende, wenn irgendein Aktuator EndFlag setzt
|
||
if self.ef_acc > 0:
|
||
elapsed = time.perf_counter() - self._t0
|
||
# An Exoself berichten
|
||
await self.exoself_pid.send((
|
||
"evaluation_completed",
|
||
self.fitness_acc,
|
||
self.cycle_acc,
|
||
elapsed
|
||
))
|
||
# inaktiv werden – warten auf reactivate
|
||
self.active = False
|
||
# Flags für nächste Episode werden erst bei reactivate gesetzt
|
||
else:
|
||
# Nächsten Zyklus starten
|
||
self.ef_acc = 0
|
||
self._reset_for_new_cycle()
|
||
await self._kick_sensors()
|
||
|
||
continue
|
||
|
||
if tag == "reactivate":
|
||
# neue Episode starten
|
||
self._reset_for_new_episode()
|
||
await self._kick_sensors()
|
||
continue
|
||
|
||
if tag == "terminate":
|
||
# geordnet alle Kinder beenden
|
||
for a in (self.sensors + self.actuators + self.neurons):
|
||
await a.send(("terminate",))
|
||
return
|
||
|
||
elif tag == "backup_from_neuron":
|
||
# vom Neuron an Cortex -> an Exoself weiterreichen
|
||
await self.exoself_pid.send(msg)
|