def __init__(self, assistant, doctor, control_period=None): labour.BaseLabour.__init__(self, IAssistant(assistant)) self._doctor = IDoctor(doctor) self._patients = {} # {AGENT_ID: Patient} self._control_period = control_period or DEFAULT_CONTROL_PERIOD self._task = None self._last_check_epoch = None self._skip_checks = None
class IntensiveCare(labour.BaseLabour): classProvides(IIntensiveCareFactory) implements(IIntensiveCare) def __init__(self, assistant, doctor, control_period=None): labour.BaseLabour.__init__(self, IAssistant(assistant)) self._doctor = IDoctor(doctor) self._patients = {} # {AGENT_ID: Patient} self._control_period = control_period or DEFAULT_CONTROL_PERIOD self._task = None self._last_check_epoch = None self._skip_checks = None ### Public Methods ### @replay.side_effect def beat(self, agent_id): if agent_id in self._patients: self._patients[agent_id].beat(self.patron.get_time()) ### IHeartMonitor Methods ### @replay.side_effect def startup(self): self.resume() @replay.side_effect def cleanup(self): self.pause() @replay.side_effect def pause(self): if self._task: self._task.cancel() self._task = None @replay.side_effect def resume(self): if self._task is None: agent = self.patron beat_time = agent.get_time() for patient in self._patients.itervalues(): patient.reset(beat_time) agent.register_interest(HeartBeatCollector, self) self._task = agent.initiate_protocol(CheckPatientTask, self, self._control_period) @replay.side_effect def has_patient(self, identifier): if IRecipient.providedBy(identifier): identifier = identifier.key return identifier in self._patients @replay.side_effect def add_patient(self, recipient, location, period=None, dying_skips=None, death_skips=None, patient_type=None): agent_id = recipient.key assert agent_id not in self._patients, \ "Patient already added to intensive care" self.debug("Start agent's %s heart monitoring", agent_id) patient = Patient(recipient, location, self.patron.get_time(), period=period, dying_skips=dying_skips, death_skips=death_skips, patient_type=patient_type) self._patients[agent_id] = patient self._doctor.on_patient_added(patient) @replay.side_effect def remove_patient(self, identifier): if IRecipient.providedBy(identifier): identifier = identifier.key if identifier in self._patients: self.debug("Stop agent's %s heart monitoring", identifier) patient = self._patients[identifier] self._doctor.on_patient_removed(patient) del self._patients[identifier] def check_patients(self): ref_time = self.patron.get_time() # Detect wakeup from the suspend if (self._last_check_epoch is not None and ref_time - self._last_check_epoch > 2 * self._control_period): # when this happens skip checking patient state for 3 runs self.info("Detected monitor wakeup, I will skip next 3 checks") self._skip_checks = 3 self._last_check_epoch = ref_time if self._skip_checks: self._skip_checks -= 1 self.debug("Skipping check of intensive care, %d left to skip", self._skip_checks) return for patient in self._patients.itervalues(): recipient = patient.recipient agent_id = recipient.key before, after = patient.check(ref_time) if before == after: continue if before == PatientState.alive: if after == PatientState.dying: self.log("Agent %s heart not responding", agent_id) self._doctor.on_patient_dying(patient) continue if after == PatientState.dead: self.log("Agent %s heart failed", agent_id) self._doctor.on_patient_died(patient) continue if after == PatientState.alive: self.log("Agent %s heart restarted", agent_id) self._doctor.on_patient_resurrected(patient) continue def get_patient(self, identifier): if IRecipient.providedBy(identifier): identifier = identifier.key return self._patients.get(identifier) def iter_patients(self): return self._patients.itervalues()