-
Notifications
You must be signed in to change notification settings - Fork 0
/
BT
executable file
·228 lines (163 loc) · 7.59 KB
/
BT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#!/usr/bin/python3
# License GPL 2. Copyright Paul D. Gilbert, 2017, 2018
"""
Run on racing boats to receive course from RC and set LED signals.
This progam should be set up to start automatically when the LED control
gadget's system is booted. It needs a setting to find other modules, e.g.
export PYTHONPATH=/path/to/Vcourse/lib
Configuration for the local course setup is read from BTconfig.
"""
# consider pbr module for maintaining version.
import logging
#in decreasing order CRITICAL, ERROR, WARNING. INFO, DEBUG
# level logs everything higher. NOTSET looks to parent levels
# basicConfigcan only be set once in a python session. Additional calls ignored.
#logging.basicConfig(level=logging.DEBUG, format='(%(threadName)-9s) %(message)s')
logging.basicConfig(level=logging.INFO, format='(%(threadName)-9s) %(message)s')
logging.info('message level info.')
logging.debug('message level debug.')
import threading
import signal
import sys
import time
import json
import gpsd
import os
import LEDs
from gpsPos import gpsPos
from gpsPos import gpsConnection
from distribution import distributionCheck
import stadiumBT as stadium
import stadium2BT as stadium2
# filemode='w' will start a new log file each run. Otherwise appending is done.
#logging.basicConfig(filename='BT.log', level=logging.DEBUG, format=logFormat,)
# cid needs to be global as it is used and changed by both zoneSignal and
# distributionCheck threads. The usual update would be in zoneSignal, after
# an update has been signalled by distributionCheck. But distributionCheck
# will set it when first intiallizing
# ACTUALLY, ZONESIGNAL MAY NOT NEED IT
cid = None
class ZSsupervisor(threading.Thread):
"""
This thread class is used by BT. It waits for course update fron distributionCheck(),
loads the zoneObj, stops any running zoneSignal and starts a new one.
It also handles track.txt and checks gps if no course is running.
"""
def __init__(self, update, shutdown):
threading.Thread.__init__(self)
global cid
self.name='ZSsupervisor'
self.update = update
self.shutdown = shutdown
self.stoprequest = threading.Event()
# this try allows recovery after restart, using an already saved file.
try :
with open("activeBTzoneObj.json","r") as f: self.zoneObj = json.load(f)
#globals().update(self.zoneObj)
logging.info("Pre-existing zoneObj loaded.")
# setting global, not used here but distributionCheck needs it.
cid = str(self.zoneObj["cid"])
logging.info(' cid =' + cid)
except:
self.zoneObj = None
cid = None
logging.info("No pre-existing zoneObj.")
self.t = open("track.txt","w")
# The ZSsupervisor does not use the gps but should occasionally test it
# when a zoneObj is not running, so sailors know if there is a problem
# before leaving harbour.
config = json.load(open('GPSconfig')) # read config
logging.debug('Using GPS at ' + config['GPS_HOST'] + ':' + str(config['GPS_PORT']))
self.GPScon = gpsConnection(config['GPS_HOST'], int(config['GPS_PORT']))
logging.info('ZSsupervisor initialized.')
def run(self):
logging.info('ZSsupervisor started.')
if self.zoneObj is None:
zs = NoCourse_zoneSignal(self.stoprequest, self.GPScon, self.t)
elif self.zoneObj['zoneType'] == 'stadium' :
zs = stadium.zoneSignal(self.zoneObj, self.stoprequest, self.GPScon, self.t)
elif self.zoneObj['zoneType'] == 'stadium2' :
zs = stadium2.zoneSignal(self.zoneObj, self.stoprequest, self.GPScon, self.t)
elif self.zoneObj['zoneType'] == 'NoCourse' :
# usually this would not be a distributed object but None above
zs = NoCourse_zoneSignal(self.stoprequest, self.GPScon, self.t)
else : raise Exception('zoneType not recognized.')
zs.start()
while not self.shutdown.is_set():
# Next wait could be much longer, just need to occassionally check gps
# and for shutdown. Make it short while debugging because of frequent shutdown.
if self.update.wait(5):
self.stoprequest.set()
zs.join()
logging.info('updating to new zoneObj')
with open("activeBTzoneObj.json","r") as f: self.zoneObj = json.load(f)
LEDs.setLEDs('update')
self.update.clear()
logging.debug('update is clear:')
cid = str(self.zoneObj["cid"])
if cid is None :
raise Exception('something is wrong. cid is None after update with zoneObj).')
self.t.write('new course loaded: ' + cid + '\n' )
logging.info(' new cid: ' + cid)
if self.zoneObj['zoneType'] == 'stadium' :
zs = stadium.zoneSignal(self.zoneObj, self.stoprequest, self.GPScon, self.t)
elif self.zoneObj['zoneType'] == 'stadium2' :
zs = stadium2.zoneSignal(self.zoneObj, self.stoprequest, self.GPScon, self.t)
elif self.zoneObj['zoneType'] == 'NoCourse' :
zs = NoCourse_zoneSignal(self.stoprequest, self.GPScon, self.t)
else :
raise Exception("zoneType '" + self.zoneObj['zoneType'] + "' not recognized.")
zs.start()
self.stoprequest.set()
LEDs.cleanup()
self.t.close()
logging.info('ZSsupervisor shutdown.')
class NoCourse_zoneSignal(threading.Thread):
"""This thread class reads the gps sets No GPS fix LED signals if not working."""
def __init__(self, stoprequest, GPScon, t):
threading.Thread.__init__(self)
self.name='NoCourse_zoneSignal'
self.stoprequest = stoprequest
self.GPScon = GPScon
self.t = t
logging.info('NoCourse_zoneSignal initialized.')
def run(self):
logging.info('NoCourse_zoneSignal started.')
sk =0 # skip counter for recording track
while not self.stoprequest.is_set():
p = self.GPScon.getGPS()
if p is None:
logging.info('p is None.')
tr = 'No GPS fix. LED status noGPSfix'
LEDs.setLEDs('noGPSfix', x='. No GPS fix.')
else:
#logging.info('p is ' + str(p.lat))
tr = str(p.lat) + ' ' + str(p.lon) + ' @ ' + p.time +' LED status off.'
LEDs.setLEDs('off', x='. No Course set.')
if sk is 0 : self.t.write(tr + '\n' )
sk = (sk + 1) % 50
time.sleep(1)
self.stoprequest.clear()
logging.info('Exiting NoCourse_zoneSignal thread, for new course update or shutdown')
if __name__ == '__main__':
with open(os.getenv("HOME") + '/.BT.pid', 'w') as f:
f.write(str(os.getpid()) +'\n')
f.write(os.getenv("PWD") +'\n')
logging.info('main thread starting. ' +time.strftime('%Y-%m-%d %H:%M:%S %Z'))
update = threading.Event()
shutdown = threading.Event()
distributionCheck(update, shutdown).start()
ZSsupervisor(update, shutdown).start()
logging.debug(threading.enumerate())
def shutdownHandler(signum, frame):
logging.info('main thread setting shutdown signal.')
shutdown.set() # to exit threads
time.sleep(5)
logging.info('main thread exit.' +time.strftime('%Y-%m-%d %H:%M:%S %Z')+ '\n')
logging.debug('threads still running:')
logging.debug(threading.enumerate())
sys.exit()
# ^C works if process is not deamonized with &
signal.signal(signal.SIGINT, shutdownHandler) # ^C, kill -2
signal.signal(signal.SIGTERM, shutdownHandler) # kill -15 (default)
while True: time.sleep(600) # Ctrl+c or kill to exit