b3d1e2acf46a88b748a4cdad16a8fe24906a4642
[saartuer.git] / statemachine.py
1 from libtuer import ThreadFunction, logger
2 from actor import Actor
3
4 # logger.{debug,info,warning,error,critical}
5
6 # StateOpening constants
7 OPEN_REPEAT_TIMEOUT = 8
8 OPEN_REPEAT_NUMBER = 3
9
10 def play_sound (what):
11         print ("I would now play the sound %s... IF I HAD SOUNDS!" % what)
12
13 # StateAboutToOpen constants
14 ABOUTOPEN_NERVLIST = [(5, lambda : play_sound("heydrückdenknopf.mp3")), (10, lambda:play_sound("alterichmeinsernst.mp3"))]
15 # TODO: erzeuge mehr nerv
16
17 class StateMachine():
18         # commands you can send
19         CMD_PINS = 0
20         CMD_BUZZ = 1
21         CMD_OPEN = 2
22         CMD_WAKEUP = 3
23         CMD_LAST = 4
24         
25         class State():
26                 def __init__(self, state_machine):
27                         self.state_machine = state_machine
28                         self.time_entered = time.time()
29                         self.theDict = None
30                 def handle_pins_event(self):
31                         pass # one needn't implement this
32                 def handle_buzz_event(self,arg): # this shouldn't be overwritten
33                         self.actor.act(Actor.CMD_BUZZ)
34                         arg("200 okay: buzz executed")
35                 def handle_open_event(self,arg):
36                         if arg is not None:
37                                 arg("412 Precondition Failed: The current state (%s) cannot handle the OPEN event" % self.__class__.__name__)
38                 def handle_wakeup_event(self):
39                         pass # one needn't implement this
40                 def pins(self):
41                         return self.state_machine.pins
42                 def actor(self):
43                         return self.state_machine.actor
44                 def handle_event(self,ev,arg):
45                         if arg is CMD_PINS:
46                                 self.handle_pins_event()
47                         elif arg is CMD_BUZZ:
48                                 self.handle_buzz_event(arg)
49                         elif arg is CMD_OPEN:
50                                 self.handle_open_event(arg)
51                         elif arg is CMD_WAKEUP:
52                                 self.handle_wakeup_event()
53                         else:
54                                 raise Exception("Unknown command number: %d" % ev)
55         
56         class StateStart(State):
57                 def __init__(self, sm):
58                         State.__init__(self,sm)
59                 def handle_pins_event(self):
60                         thepins = self.pins()
61                         for pin in thepins:
62                                 if pin is None:
63                                         return None
64                         if thepins.door_locked:
65                                 return StateZu
66                         else:
67                                 return StateAuf
68
69         class StateZu(State):
70                 def __init__(self,sm):
71                         State.__init__(self,sm)
72                 def handle_pins_event(self):
73                         pins = self.pins()
74                         if not pins.door_locked:
75                                 return StateAboutToOpen(self.state_machine)
76                 def handle_open_event(self,callback):
77                         return StateOpening(callback,self.state_machine)
78         
79         class StateOpening(State):
80                 def __init__(self,callback,sm):
81                         State.__init__(self,sm)
82                         self.callbacks=[callback]
83                         self.tries = 0
84                         self.actor().act(Actor.CMD_OPEN)
85                 def notify(self, did_it_work):
86                         s = "200 okay: door open" if did_it_work else ("500 internal server error: Couldn't open door with %d tries à %f seconds" % (OPEN_REPEAT_NUMBER,OPEN_REPEAT_TIMEOUT))
87                         for cb in self.callbacks:
88                                 if cb is not None:
89                                         cb(s)
90                 def handle_pins_event(self):
91                         pins = self.pins()
92                         if not pins.door_locked:
93                                 self.notify(True)
94                                 return StateAboutToOpen(self.state_machine)
95                 def handle_open_event(self,callback):
96                         self.callbacks.append(callback)
97                 def handle_wakeup_event(self):
98                         over = time.time() - self.time_entered
99                         nexttry = (self.tries+1) * OPEN_REPEAT_TIMEOUT
100                         if over > nexttry:
101                                 if self.tries < OPEN_REPEAT_NUMBER:
102                                         self.actor().act(Actor.CMD_OPEN)
103                                         self.tries += 1
104                                 else:
105                                         #TODO: LOG ERROR und EMAIL an Admins
106                                         self.notify(False)
107                                         return StateZu(self.state_machine)
108         
109         class StateAboutToOpen(State):
110                 def __init__(self, sm):
111                         State.__init__(sm)
112                 def handle_pins_event(self):
113                         pins = self.pins()
114                         if pins.door_locked:
115                                 return StateZu(self.state_machine)
116                         elif pins.space_active:
117                                 return StateAuf(self.state_machine)
118                         else:
119                                 over = time.time() - self.time_entered
120                                 # TODO: Nerv
121                                 logger.debug("AboutToOpen since %f seconds. TODO: nerv the user" % over)
122                 # TODO
123         
124         class StateAuf(State):
125                 #TODO
126                 pass
127         
128         class StateClosing(State):
129                 #TODO
130                 pass
131         
132         class StateAboutToLeave(State):
133                 #TODO
134                 pass
135         
136         class StateLeaving(State):
137                 #TODO
138                 pass
139         
140         def __init__(self, actor):
141                 self.actor = actor
142                 self.callback = ThreadFunction(self._callback)
143                 self.current_state = None
144                 self.pins = None
145         
146         def stop (self):
147                 self.callback.stop()
148         
149         def _callback(self, cmd, arg=None):
150                 # update pins
151                 if cmd == StateMachine.CMD_PINS:
152                         self.pins = arg
153                 # handle stuff
154                 newstate = self.current_state.handle_event(cmd,arg) # returns None or an instance of the new state
155                 while newstate is not None:
156                         logger.info("StateMachine: new state = %s" % newstate.__class__.__name__)
157                         self.current_state = newstate
158                         newstate = self.current_state.handle_event(StateMachine.CMD_PINS, self.pins)