-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathupdateThread.py
More file actions
executable file
·325 lines (232 loc) · 11.1 KB
/
updateThread.py
File metadata and controls
executable file
·325 lines (232 loc) · 11.1 KB
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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# import from python library
from random import randint
import time
import math
# import all the neccessary PyQt5 modules - requires PyQt5
from PyQt5.QtCore import QThread, pyqtSignal
# import neccesssary mqtt module - requires paho-mqtt
import paho.mqtt.client as mqtt
# import can - requires python-can
import can
# import class to interface with mqtt
from mosquitto_sender import Mosquitto_Sender
# create a can bus on the can0 interface
# requires interface be set up before opening as detailed here (done on Pi already): http://skpang.co.uk/blog/archives/1220
# you may need to "bring up" the interface first with: sudo /sbin/ip link set can0 up type can bitrate 500000
# ^ not yet tested without doing this - if required will need to be automated
import os
bus=can.interface.Bus(channel='can0', bustype='socketcan_native')
import arbitration_ids
from toggleable_warnings import *
MSTIMEOUT = 100 # in milliseconds
WARNING_DISPLAY_DURATION = 0.75 # in seconds, as most things in this app
WARNINGS_DEMO = False
import random as rn # for the purposes of the warning demo
class UpdateThread(QThread):
# create signals that can be linked to update functions for the UI
gear = pyqtSignal(str)
speed = pyqtSignal(str)
rpm = pyqtSignal(int)
statusText = pyqtSignal(str)
statusColor = pyqtSignal(str)
chargePercent = pyqtSignal(int)
fuelPercent = pyqtSignal(int)
def __init__(self,parent=None):
super(UpdateThread,self).__init__(parent)
self.exiting = False
self.timeout_warning = Toggleable_warning("Timed out", "T/O")
self.warnings = [self.timeout_warning]
self.current_warning = None
self.sender = Mosquitto_Sender()
self.sender.connect("192.168.1.30")
self.sender.start_handler()
if WARNINGS_DEMO:
# this block for warning demo stuff
example_texts = ["COLD", "HOT", "FUEL", "CHRG", "OOPS", "LEAN", "OUCH", "RICH", "SIN", "COS"]
self.example_warnings = [Toggleable_warning("Example warning %d" % n, example_texts[n]) for n in range(10)]
self.demo_interval = 5
self.demo_altered = time.time()
self.warnings += self.example_warnings
def __del__(self):
self.wait()
def stop(self):
self.exiting = True
self.terminate()
def run(self):
while not self.exiting:
self.checkForUpdates()
return
def set_status_colour(self, status):
if status.upper() == "OK":
self.statusColor.emit(str("#07DD07")) # green
elif status.upper() == "TIMED OUT":
self.statusColor.emit(str("#880707")) # red
elif status.upper() == "WARNING":
self.statusColor.emit(str("#DD6607")) # orange
else:
raise Error("Unrecognized status colour: %s" % status)
def active_warnings(self):
"""Returns a list of the active warnings, sorted in order of last time displayed"""
return sorted([wn for wn in self.warnings if wn.on], key=lambda wn: wn.last_focused)
def wfocus(self, warning):
"""Switches the currently displayed warning"""
self.current_warning = warning
if not warning is None:
self.statusText.emit(warning.code)
warning.mark_focused()
def display_status(self):
"""Displays the least recently displayed active warning.
Returns warning displayed, or None"""
active_warnings = self.active_warnings()
if active_warnings == []: # status is OK
self.statusText.emit(str("OK"))
self.set_status_colour("OK")
self.wfocus(None) # make sure we know we're not displaying a warning
return None
elif self.timeout_warning:
self.set_status_colour("TIMED OUT")
else:
self.set_status_colour("WARNING")
last_warning = active_warnings[-1] # the least recently focused active warning
if len(active_warnings) == 1:
if not self.current_warning is last_warning:
# That means we are switching to this warning for some reason
self.wfocus(last_warning)
else:
# This is the only warning and we continue to display it
last_warning.mark_seen()
return last_warning
elif last_warning.last_seen - last_warning.last_focused <= WARNING_DISPLAY_DURATION:
# continue displaying last warning
last_warning.mark_seen()
return last_warning
else: # switch it up
warning = active_warnings[0]
self.wfocus(warning)
return warning
def checkForUpdates(self):
# see if the server is connected and if not retry
#if(self.sender.connection_success == False):
# self.sender.retry_connect()
# recieve a message on the bus and timeout after MSTIMEOUT ms, in which case the gui will show a timeout
message = bus.recv(MSTIMEOUT/1000)
if message is None: # bus timed out
# set the statusText to TIMED OUT and red
self.timeout_warning.switch_on()
self.display_status()
return None
else:
self.timeout_warning.switch_off()
# Commented out to display coolant temperature in the warning zone.
# self.display_status()
if WARNINGS_DEMO:
# demo the warning system:
aw = self.active_warnings()
#if aw != []: timer = time.time() - aw[-1].last_focused
#else: timer = None
#print("Warnings: %s Timer?: %s" % (aw, timer))
if time.time() - self.demo_altered > self.demo_interval:
self.example_warnings[rn.randint(0,9)].toggle()
self.demo_altered = time.time()
self.demo_interval = rn.uniform(3,10)
if rn.uniform(0,1) < 0.333:
for warning in self.example_warnings:
warning.switch_off()
if message.arbitration_id == arbitration_ids.rpm:
# rpm is sent as two bits which needs to be combined again after
rpm = int(message.data[6]<<8 | message.data[7])
self.sender.send("hybrid/dash/rpm", str(time.time()) + ":" + str(rpm))
# rpmAngle is what is sent to the UI current which is from -150 to 150 corresponding to 0 to 12000
rpmAngle = rpm/40.0-150.0
self.rpm.emit(rpmAngle)
# pulse width is the the next two bits / 1000
pw = str(int(message.data[2]<<8 | message.data[3])/1000.0)
self.sender.send("hybrid/engine/pw", str(time.time()) + ":" + str(pw))
elif message.arbitration_id == arbitration_ids.groundspeed:
powersplit = str(message.data[0])
self.sender.send("hybrid/dash/powersplit", str(time.time()) + ":" + str(powersplit))
gear = str(message.data[6]&0b1111)
self.gear.emit(gear) # gear is a string
self.sender.send("hybrid/dash/gear", str(time.time()) + ":" + str(gear))
speed = str(message.data[4])
self.speed.emit(speed) # speed is is a string
self.sender.send("hybrid/dash/speed", str(time.time()) + ":" + str(speed))
chargePercent = message.data[5]
self.chargePercent.emit(chargePercent) # chargePercent is an integer
self.sender.send("hybrid/dash/charge", str(time.time()) + ":" + str(chargePercent))
elif message.arbitration_id == arbitration_ids.fuel:
fuelPercent = int(message.data[4])
self.fuelPercent.emit(fuelPercent) # fuelPercent is an integer
self.sender.send("hybrid/engine/fuel", str(time.time()) + ":" + str(fuelPercent))
elif message.arbitration_id == arbitration_ids.coolant:
coolantTemp = str(int(message.data[6]<<8 | message.data[7])/10.0)
self.statusText.emit(coolantTemp) # status text needs to come out as strin
self.sender.send("hybrid/engine/temperature", str(time.time()) + ":" + str(coolantTemp))
MAT = str(int(message.data[4]<<8 | message.data[5])/10.0)
self.sender.send("hybrid/engine/MAT", str(time.time()) + ":" + str(MAT))
elif message.arbitration_id == arbitration_ids.vehicle_slow:
# Send the throttle driver input
driverThrottle = int(message.data[2])
self.sender.send("hybrid/driverinputs/throttle", str(time.time()) + ":" + str(driverThrottle))
# Send the brake driver input
driverBrake = int(message.data[3])
self.sender.send("hybrid/driverinputs/brake", str(time.time()) + ":" + str(driverBrake))
elif message.arbitration_id == arbitration_ids.tps:
# Send the throttle position
TPS = str(int(message.data[0]<<8 | message.data[1])/10.0)
self.sender.send("hybrid/engine/TPS", str(time.time()) + ":" + str(TPS))
# Send the AFR
AFR = int(message.data[4]<<8 | message.data[5])/10.0
self.sender.send("hybrid/engine/AFR", str(time.time()) + ":" + str(AFR))
# Send the battery voltage
GLVolts = int(message.data[2]<<8 | message.data[3])/10.0
self.sender.send("hybrid/dash/GLVoltage", str(time.time()) + ":" + str(GLVolts))
elif message.arbitration_id == arbitration_ids.advance:
# Send the spark advance
spkadv = str(int(message.data[0]<<8 | message.data[1])/10.0)
self.sender.send("hybrid/engine/spkadv", str(time.time()) + ":" + str(spkadv))
# Send the target AFR
AFRtgt = int(message.data[4])/10.0
self.sender.send("hybrid/engine/AFRtgt", str(time.time()) + ":" + str(AFRtgt))
elif message.arbitration_id == arbitration_ids.ams1:
# Accumulator voltage
voltage = int(message.data[0] << 8 | message.data[1])/100.0
self.sender.send("hybrid/ams/voltage", str(time.time()) + ":" + str(voltage))
# Accumulator current
current1 = ((message.data[5]<<8 | message.data[6])) #combining bytes
if (current1 & (1 << (16 -1))) != 0: #turing 2s comp into regular binary
current1 = current1-(1<<16)
current1 = int(current1)/100.0
self.sender.send("hybrid/ams/current", str(time.time()) + ":" + str(current1))
# AMS Status
ams_status = int(message.data[4] & 0b00000010 >> 1)
self.sender.send("hybrid/ams/ams_status", str(time.time()) + ":" + str(ams_status))
# AMS regen status
regen_status = int(message.data[4] & 0b00000001)
self.sender.send("hybrid/ams/regen_status", str(time.time()) + ":" + str(regen_status))
elif message.arbitration_id == arbitration_ids.ams2:
# Number of the cell with the highest voltage
max_cell_num = int(message.data[0])
self.sender.send("hybrid/ams/max_cell_num", str(time.time()) + ":" + str(max_cell_num))
# Voltage of the cell with the highest voltage
max_cell_volts = int(message.data[1] << 8 | message.data[2])/1000.0
self.sender.send("hybrid/ams/max_cell_volts", str(time.time()) + ":" + str(max_cell_volts))
# Number of the cell with the lowest voltage
min_cell_num = int(message.data[3])
self.sender.send("hybrid/ams/min_cell_num", str(time.time()) + ":" + str(min_cell_num))
# Voltage of the cell with the lowest voltage
min_cell_volts = int(message.data[4] << 8 | message.data[5])/1000.0
self.sender.send("hybrid/ams/min_cell_volts", str(time.time()) + ":" + str(min_cell_volts))
elif message.arbitration_id == arbitration_ids.datalog3:
# FL suspension sensor
FL_susp = int(message.data[4])*2
self.sender.send("hybrid/chassic/FL_susp", str(time.time()) + ":" + str(FL_susp))
# FR suspension sensor
FR_susp = int(message.data[5])*2
self.sender.send("hybrid/chassic/FR_susp", str(time.time()) + ":" + str(FR_susp))
# RL suspension sensor
RL_susp = int(message.data[6])*2
self.sender.send("hybrid/chassic/RL_susp", str(time.time()) + ":" + str(RL_susp))
# RR suspension sensor
RR_susp = int(message.data[7])*2
self.sender.send("hybrid/chassic/RR_susp", str(time.time()) + ":" + str(RR_susp))