reduce update rate on error
[uccdoor.git] / server.py
1 #! /usr/bin/env python
2 # UCC Door Server - hardware interface server
3 # reads status of reed switches and provides a D-BUS interface to read them
4 # David Adam <[email protected]>
5 # LAT interface based on work by Mark Tearle <[email protected]>
6 # Released under an MIT-style license; see COPYING for details.
7
8 # statuses: 1 means open circuit, 0 means closed circuit, -1 means error
9
10 import syslog
11 import dbus, dbus.service
12 import gobject
13 from LATClient import LATClient
14 from select import select
15 from threading import Timer
16
17 def check_LAT_service(servicename, retries=3):
18     latclient = LATClient(service=servicename.upper())
19     rfh, wfh = latclient.get_fh()
20     results = {'error': retries, 'closed': retries, 'open': retries}
21     test_string = "got wombles?"
22     syslog.syslog(142, "start check_LAT_service:" + servicename)
23     
24     # Only return after n(retries) consistent results, although not necessarily
25     # in order. This means we poll at least (retries) and up to (retries ** 2) times.
26     # This is mtearle's original code. No idea if it's the Right Thing.
27     while True:
28         syslog.syslog(142, "running c_L_s loop:" + servicename)
29         try:
30             wfh.write(test_string)
31             wfh.flush()
32         except:
33             results['error'] -= 1
34             syslog.syslog(142, "c_L_s write error:" + servicename)
35             if results['error'] == 0: return -1
36             continue
37         syslog.syslog(142, "c_Ls written data to " + servicename)
38         rr, wr, er = select([rfh], [], [], 3.0)
39         if rfh not in rr:
40             results['open'] -= 1
41             if results['open'] == 0: return 1
42             syslog.syslog(142, "c_L_s open: " + servicename)
43             continue
44         syslog.syslog(142, "c_L_s select() succeeded: " + servicename)
45         recv = rfh.read(len(test_string))
46         syslog.syslog(142, "c_L_s read() succeeded: " + servicename)
47         if recv <> test_string:
48             syslog.syslog(142, "c_L_s not the data we sent " + servicename)
49             results['error'] -= 1
50             if results['error'] == 0: return -1
51             continue
52         results['closed'] -= 1
53         syslog.syslog(142, "c_L_s closed " + servicename)
54         if results['closed'] == 0: return 0
55
56 class Door(dbus.service.Object):
57     
58     def __init__(self, doorname, bus):
59         self.interval = 10 # seconds
60         self.service = doorname
61         self.status = -1
62         self.retries = 3
63         if 'pir' in self.service:
64             self.retries = 1
65         
66         # set up D-BUS service name
67         object_path = '/au/asn/ucc/doors/%s' % doorname
68         dbus.service.Object.__init__(self, bus, object_path)
69         
70         # get initial state in a new thread
71         self.timeout = Timer(0, self.poll)
72         # daemon threads will be killed when the mainloop exits
73         # the timeout threads will inherit this value
74         self.timeout.setDaemon(True)
75         self.timeout.start()
76     
77     def poll(self):
78         try:
79             newstatus = check_LAT_service(self.service, self.retries)
80         except:
81             newstatus = -1
82         
83         syslog.syslog(142, "status, newstatus for %s is %i, %i" % (self.service, self.status, newstatus))
84         if newstatus != self.status:
85             syslog.syslog(142, "status changed detected for  %s" % self.service)
86             self.status = newstatus
87             # emit signal
88             self.status_changed(newstatus)
89             # back off if broken
90             self.interval = [10,10,20][newstatus]
91         
92         # set up timeout again
93         self.timeout = Timer(self.interval, self.poll)
94         self.timeout.start()
95     
96     @dbus.service.signal('au.asn.ucc.DoorInterface', signature='n')
97     def status_changed(self, newstatus):
98         pass
99     
100     @dbus.service.method('au.asn.ucc.DoorInterface', in_signature='',
101                          out_signature='n')
102     def get_status(self):
103         return self.status
104
105 if __name__ == '__main__':
106     gobject.threads_init()
107     doors = ('uccdoor', 'unisfadoor', 'chdoor', 'mrdoor', 'uccpir')
108     
109     from dbus.mainloop.glib import DBusGMainLoop
110     DBusGMainLoop(set_as_default=True)
111     # get on the bus
112     system_bus = dbus.SystemBus()
113     system_bus.request_name('au.asn.ucc.DoorServer')
114     
115     door_objects = []
116
117     for door in doors:
118         door_objects.append(Door(door, system_bus))
119
120     # engage!
121     loop = gobject.MainLoop()
122     try:
123         loop.run()
124     except KeyboardInterrupt:
125         loop.quit()
126     except:
127         pass

UCC git Repository :: git.ucc.asn.au