-
Notifications
You must be signed in to change notification settings - Fork 0
/
ewa.py
296 lines (267 loc) · 10.1 KB
/
ewa.py
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
import argparse
import canopen
import logging
import signal
import sys
import time
import traceback
from bmslistener import BMSListener
from canopen import nmt
from display import DisplayApp
from enum import Enum
from forcemapping import ForceMapping
from ropespeed import RopeSpeed
from threading import Thread
class State(Enum):
OFFLINE = 0
INIT = 1
ONLINE = 2
class Ewa(object):
def __init__(self):
self._PDO = False
self._mapping = ForceMapping()
self._display = None
self._run = True
self._state = State.OFFLINE
self._network = None
self._controller = None
self._main_thread = Thread(target=self.mainloop)
self._read = False
self._read_thread = None
self._monitor_thread = None
self._heartbeat = False
self._received_data = False
def start(self, args):
self._devel = args.d
self._mapping.read()
self._display = DisplayApp(args.d, self._mapping)
self._network = canopen.Network()
self._network.listeners = self._network.listeners + [BMSListener(self._display)]
self._network.connect(bustype='socketcan', channel=args.dev)
self._controller = self._network.add_node(7, 'CANopenSocket.eds')
# main EWA thread here
self._main_thread.start()
# blocks until the UI ends
try:
self._display.run()
except BaseException as e:
logging.error(traceback.format_exc())
def stop(self):
self._run = False
self._read = False
self._mapping.write()
if self._monitor_thread:
self._monitor_thread.join()
self._monitor_thread = None
if self._read_thread:
self._read_thread.join()
self._read_thread = None
if self._main_thread:
self._main_thread.join()
self._main_thread = None
if self._controller:
self._controller.pdo.tx[1].stop()
self._controller.nmt.state = 'STOPPED'
self._controller = None
if self._network:
self._network.disconnect()
self._network = None
if self._display:
self._display.stop()
self._display = None
def mainloop(self):
next_state = State.OFFLINE
while self._run:
# print('%s' % self._state)
if State.OFFLINE == self._state:
next_state = self.offline()
if State.INIT == self._state:
next_state = self.init()
if State.ONLINE == self._state:
next_state = self.online()
self._state = next_state
def offline(self):
if not self._PDO:
if self._read:
self._read = False
print('Stop')
self._read_thread.join()
self._read_thread = None
self.connected(False)
if self._monitor_thread:
self._monitor_thread.join()
self._monitor_thread = None
nmt_state = None
try:
self._controller.nmt.state = 'PRE-OPERATIONAL'
self._controller.sdo['Producer heartbeat time'].raw = 1000
except canopen.sdo.SdoCommunicationError as e:
logging.exception('Failed to configure heartbeat.')
except BaseException as e:
logging.error(traceback.format_exc())
try:
nmt_state = self._controller.nmt.wait_for_heartbeat(0.1)
except canopen.nmt.NmtError as e:
pass
if nmt_state:
self.connected(True)
return State.INIT
return State.OFFLINE
def init(self):
# TODO somewhere here SDO timeouts may occur.
self._controller.nmt.state = 'PRE-OPERATIONAL'
try:
self._controller.sdo['Producer heartbeat time'].raw = 1000
except canopen.sdo.SdoCommunicationError as e:
logging.info('Failed to configure heartbeat.')
if self._PDO:
try:
self._controller.pdo.read()
except canopen.sdo.SdoCommunicationError as e:
logging.info('Failed to read PDO configuration.')
self._controller.pdo.tx[1].clear()
# TODO replace with Throttle_Command 0x3216, subindex 0 length 2 readonly
self._controller.pdo.tx[1].add_variable(0x2110, 1)
# Asynchronous PDO. If one process variable changes, the data is transfered.
self._controller.pdo.tx[1].trans_type = 254
# Transmit at least every 1000 milliseconds.
self._controller.pdo.tx[1].event_timer = 1000
self._controller.pdo.tx[1].enabled = True
self._controller.pdo.tx[1].add_callback(callback=self.received)
try:
self._controller.pdo.save()
except canopen.sdo.SdoCommunicationError as e:
logging.info('Failed to save PDO configuration.')
else:
if not self._read:
self._read = True
logging.debug('Starting read thread')
self._read_thread = Thread(target=self.read)
self._read_thread.start()
# TODO With the initialisation problem the emulator will not go back into operational mode and we get no data.
self._controller.nmt.state = 'OPERATIONAL'
if self._monitor_thread:
print('Monitor thread not gone')
else:
self._monitor_thread = Thread(target=self.monitor_heartbeat)
self._monitor_thread.start()
return State.ONLINE
def read(self):
count = 0
while self._read:
try:
value = self._controller.sdo[0x3216].raw
self.show_data(value)
except canopen.sdo.SdoError:
if not self._devel:
logging.exception('Reading Throttle_Command failed')
try:
value = self._controller.sdo[0x3207].raw
self.show_rpm(value)
except canopen.sdo.SdoError:
if not self._devel:
logging.exception('Reading RPM failed')
if count >= 10:
try:
value = self._controller.sdo[0x320b].raw
self.show_motor_temperature(value)
except canopen.sdo.SdoError:
if not self._devel:
logging.exception('Reading motor temperature failed')
try:
value = self._controller.sdo[0x322a].raw
self.show_controller_temperature(value)
except canopen.sdo.SdoError:
if not self._devel:
logging.exception('Reading controller temperature failed')
try:
value = self._controller.sdo[0x324d].raw
self.show_voltage(value)
except canopen.sdo.SdoError:
if not self._devel:
logging.exception('Reading voltage failed')
count = 0
time.sleep(0.1)
count += 1
def online(self):
"""
Just monitor the heartbeat and change state accordingly. Make the main thread sleep running through the states.
"""
time.sleep(0.1)
if self._heartbeat:
return State.ONLINE
else:
return State.OFFLINE
def received(self, message):
for var in message:
self.show_data(var.raw)
def show_data(self, value):
logging.debug('Throttle_Command: ' + str(value))
self._received_data = True
if self._display:
self._display.set_measure(value)
self._display.set_torque(self._mapping.map(value))
def show_rpm(self, value):
if value > 32767:
value -= 65536
logging.debug('RPM: ' + str(value))
self._received_data = True
speed = RopeSpeed.calculate_speed(value)
logging.debug('Rope speed: ' + str(speed))
if self._display:
self._display.set_rpm(value)
self._display.set_rope_speed(speed)
def show_motor_temperature(self, value):
value /= 10
logging.debug('Motor temperature ' + str(value))
self._received_data = True
if self._display:
self._display.set_motor_temperature(value)
def show_controller_temperature(self, value):
value /= 10
logging.debug('Controller temperature ' + str(value))
self._received_data = True
if self._display:
self._display.set_controller_temperature(value)
def show_voltage(self, value):
value /= 100.0
logging.debug('Voltage {:3.2f}V'.format(value))
if self._display:
self._display.set_voltage(value)
def monitor_heartbeat(self):
while self._run:
nmt_state = None
self._received_data = False
try:
nmt_state = self._controller.nmt.wait_for_heartbeat(1.2)
except canopen.nmt.NmtError as e:
pass
if nmt_state or self._received_data:
self.connected(True)
else:
self.connected(False)
break
def connected(self, connected):
if self._display:
self._display.connected(connected)
self._heartbeat = connected
ewa = Ewa()
def handler(signum, frame):
ewa.stop()
def main():
signal.signal(signal.SIGINT, handler)
parser = argparse.ArgumentParser(description='EWA')
parser.add_argument('dev', metavar='<CAN device name>', help='CAN device name')
parser.add_argument('-i', default=42, type=int, choices=range(1, 127), required=False, help='canopen Node ID')
parser.add_argument('-d', action="store_true")
args, left = parser.parse_known_args()
sys.argv = sys.argv[:1] + left
logging.basicConfig()
some_logger = logging.getLogger('canopen.network')
some_logger.setLevel(logging.DEBUG)
some_logger.addHandler(logging.StreamHandler())
ewa.start(args)
ewa.stop()
return 0
if __name__ == '__main__':
sys.exit(main())