7 import util.go_utils as gobject_utils
8 import util.coroutines as coroutines
12 _moduleLogger = logging.getLogger("gvoice.state_machine")
15 def to_milliseconds(**kwd):
16 if "milliseconds" in kwd:
17 return kwd["milliseconds"]
18 elif "seconds" in kwd:
19 return kwd["seconds"] * 1000
20 elif "minutes" in kwd:
21 return kwd["minutes"] * 1000 * 60
23 return kwd["hours"] * 1000 * 60 * 60
24 raise KeyError("Unknown arg: %r" % kwd)
27 def to_seconds(**kwd):
28 if "milliseconds" in kwd:
29 return kwd["milliseconds"] / 1000
30 elif "seconds" in kwd:
32 elif "minutes" in kwd:
33 return kwd["minutes"] * 60
35 return kwd["hours"] * 60 * 60
36 raise KeyError("Unknown arg: %r" % kwd)
39 class NopStateStrategy(object):
44 def initialize_state(self):
47 def increment_state(self):
52 return UpdateStateMachine.INFINITE_PERIOD
55 class ConstantStateStrategy(object):
57 def __init__(self, timeout):
58 assert 0 < timeout or timeout == UpdateStateMachine.INFINITE_PERIOD
59 self._timeout = timeout
61 def initialize_state(self):
64 def increment_state(self):
72 class GeometricStateStrategy(object):
74 def __init__(self, init, min, max):
75 assert 0 < init and init < max and init != UpdateStateMachine.INFINITE_PERIOD
76 assert 0 < min and min != UpdateStateMachine.INFINITE_PERIOD
77 assert min < max or max == UpdateStateMachine.INFINITE_PERIOD
83 def initialize_state(self):
84 self._current = self._min
86 def increment_state(self):
87 if self._max == UpdateStateMachine.INFINITE_PERIOD:
90 self._current = min(2 * self._current, self._max - self._init)
94 timeout = self._init + self._current
98 class StateMachine(object):
100 STATE_ACTIVE = 0, "active"
101 STATE_IDLE = 1, "idle"
105 raise NotImplementedError("Abstract")
108 raise NotImplementedError("Abstract")
111 raise NotImplementedError("Abstract")
113 def set_state(self, state):
114 raise NotImplementedError("Abstract")
118 raise NotImplementedError("Abstract")
121 class MasterStateMachine(StateMachine):
125 self._state = self.STATE_ACTIVE
127 def append_machine(self, machine):
128 self._machines.append(machine)
131 # Confirm we are all on the same page
132 for machine in self._machines:
133 machine.set_state(self._state)
134 for machine in self._machines:
138 for machine in self._machines:
142 for machine in self._machines:
145 def set_state(self, state):
147 for machine in self._machines:
148 machine.set_state(state)
155 class UpdateStateMachine(StateMachine):
156 # Making sure the it is initialized is finicky, be careful
162 def __init__(self, updateItems, name=""):
164 self._updateItems = updateItems
166 self._state = self.STATE_ACTIVE
167 self._timeoutId = None
169 self._strategies = {}
170 self._callback = coroutines.func_sink(
171 coroutines.expand_positional(
172 self._request_reset_timers
176 def set_state_strategy(self, state, strategy):
177 self._strategies[state] = strategy
180 assert self._timeoutId is None
181 for strategy in self._strategies.itervalues():
182 strategy.initialize_state()
183 if self._strategy.timeout != self.INFINITE_PERIOD:
184 self._timeoutId = gobject.idle_add(self._on_timeout)
185 _moduleLogger.info("%s Starting State Machine" % (self._name, ))
188 _moduleLogger.info("%s Stopping State Machine" % (self._name, ))
192 assert self._timeoutId is None
193 self._callback = None
195 def set_state(self, newState):
196 if self._state == newState:
198 oldState = self._state
199 _moduleLogger.info("%s Transitioning from %s to %s" % (self._name, oldState, newState))
201 self._state = newState
208 def reset_timers(self):
209 _moduleLogger.info("%s Resetting State Machine" % (self._name, ))
213 def request_reset_timers(self):
214 return self._callback
218 return self._strategies[self._state]
220 @gtk_toolbox.log_exception(_moduleLogger)
221 def _request_reset_timers(self, *args):
224 def _schedule_update(self):
225 assert self._timeoutId is None
226 self._strategy.increment_state()
227 nextTimeout = self._strategy.timeout
228 if nextTimeout != self.INFINITE_PERIOD:
229 self._timeoutId = gobject_utils.timeout_add_seconds(nextTimeout, self._on_timeout)
230 _moduleLogger.info("%s Next update in %s seconds" % (self._name, nextTimeout, ))
232 def _stop_update(self):
233 if self._timeoutId is None:
235 gobject.source_remove(self._timeoutId)
236 self._timeoutId = None
238 def _reset_timers(self):
239 if self._timeoutId is None:
240 return # not started yet
242 self._strategy.initialize_state()
243 self._schedule_update()
245 @gtk_toolbox.log_exception(_moduleLogger)
246 def _on_timeout(self):
247 self._timeoutId = None
248 self._schedule_update()
249 for item in self._updateItems:
251 item.update(force=True)
253 _moduleLogger.exception("Update failed for %r" % item)
254 return False # do not continue