4 @todo Look into supporting more states
11 import util.go_utils as gobject_utils
12 import util.coroutines as coroutines
16 _moduleLogger = logging.getLogger("gvoice.state_machine")
19 def to_milliseconds(**kwd):
20 if "milliseconds" in kwd:
21 return kwd["milliseconds"]
22 elif "seconds" in kwd:
23 return kwd["seconds"] * 1000
24 elif "minutes" in kwd:
25 return kwd["minutes"] * 1000 * 60
27 return kwd["hours"] * 1000 * 60 * 60
28 raise KeyError("Unknown arg: %r" % kwd)
31 def to_seconds(**kwd):
32 if "milliseconds" in kwd:
33 return kwd["milliseconds"] / 1000
34 elif "seconds" in kwd:
36 elif "minutes" in kwd:
37 return kwd["minutes"] * 60
39 return kwd["hours"] * 60 * 60
40 raise KeyError("Unknown arg: %r" % kwd)
43 class NopStateStrategy(object):
48 def initialize_state(self):
51 def increment_state(self):
56 return UpdateStateMachine.INFINITE_PERIOD
59 class ConstantStateStrategy(object):
61 def __init__(self, timeout):
62 assert 0 < timeout or timeout == UpdateStateMachine.INFINITE_PERIOD
63 self._timeout = timeout
65 def initialize_state(self):
68 def increment_state(self):
76 class GeometricStateStrategy(object):
78 def __init__(self, init, min, max):
79 assert 0 < init and init < max and init != UpdateStateMachine.INFINITE_PERIOD
80 assert 0 < min and min != UpdateStateMachine.INFINITE_PERIOD
81 assert min < max or max == UpdateStateMachine.INFINITE_PERIOD
87 def initialize_state(self):
88 self._current = self._min
90 def increment_state(self):
91 if self._max == UpdateStateMachine.INFINITE_PERIOD:
94 self._current = min(2 * self._current, self._max - self._init)
98 timeout = self._init + self._current
102 class StateMachine(object):
104 STATE_ACTIVE = 0, "active"
105 STATE_IDLE = 1, "idle"
109 raise NotImplementedError("Abstract")
112 raise NotImplementedError("Abstract")
115 raise NotImplementedError("Abstract")
117 def set_state(self, state):
118 raise NotImplementedError("Abstract")
122 raise NotImplementedError("Abstract")
125 class MasterStateMachine(StateMachine):
129 self._state = self.STATE_ACTIVE
131 def append_machine(self, machine):
132 self._machines.append(machine)
135 # Confirm we are all on the same page
136 for machine in self._machines:
137 machine.set_state(self._state)
138 for machine in self._machines:
142 for machine in self._machines:
146 for machine in self._machines:
149 def set_state(self, state):
151 for machine in self._machines:
152 machine.set_state(state)
159 class UpdateStateMachine(StateMachine):
160 # Making sure the it is initialized is finicky, be careful
166 def __init__(self, updateItems, name=""):
168 self._updateItems = updateItems
170 self._state = self.STATE_ACTIVE
171 self._timeoutId = None
173 self._strategies = {}
174 self._callback = coroutines.func_sink(
175 coroutines.expand_positional(
176 self._request_reset_timers
180 def set_state_strategy(self, state, strategy):
181 self._strategies[state] = strategy
184 assert self._timeoutId is None
185 for strategy in self._strategies.itervalues():
186 strategy.initialize_state()
187 if self._strategy.timeout != self.INFINITE_PERIOD:
188 self._timeoutId = gobject.idle_add(self._on_timeout)
189 _moduleLogger.info("%s Starting State Machine" % (self._name, ))
192 _moduleLogger.info("%s Stopping State Machine" % (self._name, ))
196 assert self._timeoutId is None
197 self._callback = None
199 def set_state(self, newState):
200 if self._state == newState:
202 oldState = self._state
203 _moduleLogger.info("%s Transitioning from %s to %s" % (self._name, oldState, newState))
205 self._state = newState
212 def reset_timers(self):
213 _moduleLogger.info("%s Resetting State Machine" % (self._name, ))
217 def request_reset_timers(self):
218 return self._callback
222 return self._strategies[self._state]
224 @gtk_toolbox.log_exception(_moduleLogger)
225 def _request_reset_timers(self, *args):
228 def _schedule_update(self):
229 assert self._timeoutId is None
230 self._strategy.increment_state()
231 nextTimeout = self._strategy.timeout
232 if nextTimeout != self.INFINITE_PERIOD:
233 self._timeoutId = gobject_utils.timeout_add_seconds(nextTimeout, self._on_timeout)
234 _moduleLogger.info("%s Next update in %s seconds" % (self._name, nextTimeout, ))
236 def _stop_update(self):
237 if self._timeoutId is None:
239 gobject.source_remove(self._timeoutId)
240 self._timeoutId = None
242 def _reset_timers(self):
243 if self._timeoutId is None:
244 return # not started yet
246 self._strategy.initialize_state()
247 self._schedule_update()
249 @gtk_toolbox.log_exception(_moduleLogger)
250 def _on_timeout(self):
251 self._timeoutId = None
252 self._schedule_update()
253 for item in self._updateItems:
255 item.update(force=True)
257 _moduleLogger.exception("Update failed for %r" % item)
258 return False # do not continue