class IntervalTrigger(): '''Generate signals at a regular interval up to total_signals''' total_signals = IntProperty(title="Total Number of Signals", default=-1) interval = TimeDeltaProperty(title='Interval', default={'seconds': 1}) def __init__(self): super().__init__() self._stop_event = Event() self.counter = None def start(self): super().start() self.counter = 0 self._stop_event.clear() spawn(self.run) def run(self): # We'll keep track of when each iteration is expected to finish interval_secs = self.interval.total_seconds() expected_finish = time() + interval_secs while not self._stop_event.is_set(): sigs = self.generate_signals() # If a generator is returned, build the list if not isinstance(sigs, list): sigs = list(sigs) # Add however many signals were generated (in case multiple # signals mixin was used) to the counter and notify them self.counter += len(sigs) self.notify_signals(sigs) if self.total_signals > 0 and self.counter >= self.total_signals: # We have reached our total, stop break # sleep the exact correct amount of time time_remaining = expected_finish - time() if time_remaining > 0: # If we have time remaining, sleep until the next iteration sleep(time_remaining) # One iteration is complete, increment our next "expected finish" # time. This expected_finish could fall behind the actual time # if it takes longer than interval_secs to generate the signals expected_finish += interval_secs def stop(self): """ Stop the simulator thread. """ self._stop_event.set() super().stop()
class SafeTrigger(): """ Guarantees notifying signals every interval, regardless of count """ interval = TimeDeltaProperty(title='Interval', default={'seconds': 1}) max_count = IntProperty(title='Max Count', default=1) def __init__(self): super().__init__() self._job = None self.stop_event = Event() self.signal_lock = Lock() def start(self): super().start() self._job = Job(self._emit, self.interval, True) # Run an emit cycle immediately, but in a new thread since it # might take some time and we don't want it to hold up start spawn(self._emit) def stop(self): """ Stop the simulator thread and signal generation """ if self._job: self._job.cancel() self.stop_event.set() super().stop() def _emit(self): """ Called every *interval* to generate then notify the signals """ self._logger.debug("New generation cycle requested") count = 0 signals = [] # Stop any currently running simulator threads self.stop_event.set() # We only want one simulator thread simulating at a time with self.signal_lock: # Ok, we're running, so clear the event and wait self.stop_event.clear() self._logger.debug("Starting generation...") while count < self.max_count and not self.stop_event.is_set(): signals.extend(self.generate_signals(1)) count += 1 self._logger.debug("Notifying {} signals".format(len(signals))) self.notify_signals(signals)