-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdevice.py
More file actions
383 lines (328 loc) · 12.6 KB
/
Copy pathdevice.py
File metadata and controls
383 lines (328 loc) · 12.6 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
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# pylint: disable=unused-argument
"""
File: device.py
Version: 1.03
Author: jpindar@jpindar.com
"""
from time import sleep
from time import time
import string
import globe
import serialdevice_pyserial
import socketdevice
import logging
__author__ = 'jpindar@jpindar.com'
logger = logging.getLogger(__name__)
class DeviceError(Exception):
"""Raise for my specific kind of exception"""
def __init___(self, name, response):
super().__init__()
self.name = name
self.response = response
class DeviceLoggedOutError(DeviceError):
"""Raise for my specific kind of exception"""
def __init___(self, name, response):
super().__init__()
class DeviceTimeoutError(DeviceError):
"""Raise for my specific kind of exception"""
def __init___(self, name, response):
super().__init__()
class DeviceResponseError(DeviceError):
"""Raise for my specific kind of exception"""
def __init___(self, name, response):
super().__init__()
# class ValidationError(Exception):
# def __init__(self, message, errors):
#
# # Call the base class constructor with the parameters it needs
# super(ValidationError, self).__init__(message)
# or super().__init__(message)
#
# # Now for your custom code...
# self.errors = errors
def is_correct_id(s):
if s is None:
return False
s2 = s.upper()
return bool(s2[:7] == "Fluke")
def is_password_prompt(s):
if s is None:
return False
if s[-9:] == 'password:':
return True
return False
class Device:
"""
represents a device regardless of connection type
"""
# pylint: disable=too-many-public-methods, too-many-instance-attributes
PASSWORD_LENGTH = 16
def __init__(self, connection, output, kind):
"""
constructor for a Device
connection is a list containing either a serial port number
or an ip address and a port
output is anything with an .append(string) method, typically a textbox
kind is an enum representing the type of connection:
serial, network or mock
So device is either a serialdevice_pyserial.SerialDevice()
or a socketdevice.SocketDevice()
or a mock
mock means open a serial port to whatever's there,
without expecting it to respond correctly
all methods like self.port.open_port(connection)
should close the port (if it exists and is open) before opening it
"""
assert isinstance(connection, list)
self.kind = kind
self.connection = connection
self.comPort = None
self.port_num = None
self.port = None
self.output = output
self.revision = 0.0
def connect(self):
success = None
if self.kind == globe.DUTKind.serial or self.kind == globe.DUTKind.mock:
self.port = serialdevice_pyserial.SerialDevice()
try:
success = self.port.open_port(self.connection)
except Exception as e:
logger.error(e.__class__)
logger.error("can't open COM port" + str(globe.serial_port_num))
return False
elif self.kind == globe.DUTKind.network:
self.port = socketdevice.SocketDevice() # just a dumb constructor
try:
success = self.port.open_port(self.connection) # this is what actually does something
except OSError as e:
# Typical error is:
# [WinError 10060] A connection attempt failed because the connected party did not properly
# respond after a period of time,
# or established connection failed because connected host has failed to respond
# This error was already logged, only re-raised to move this UI stuff to this higher level
self.output.append(e.__doc__) # "Timeout expired"
self.output.append(e.strerror) # "a connection attempt failed because.....
return False
except Exception as e:
logger.error(e.__class__) # Value Error? TypeError? happens due to a malformed URL, or if it is None
logger.error("can't create socketdevice or open network socket")
# output.append("Can't open the network connection " + connection[0] + ':' + str(connection[1]) + "\n")
return False
# at this point, success means we've opened a com port. Doesn't mean there's anything there.
if not success:
logger.info("connect failed.\n")
return False
if self.kind == globe.DUTKind.mock:
# logger.info("mock constructor is done.\n")
return True
sleep(0.5) # because trying to write to the socket immediately after opening it doesn't always work
if self.kind == globe.DUTKind.serial:
self.initialize_me()
return True
def login(self):
globe.password = globe.password[:Device.PASSWORD_LENGTH]
s = None
logger.info("attempting to log in\r")
attempts = 0
while attempts < 4:
attempts += 1
try:
self.port.write('ID?\r')
s = self.port.read()
except OSError as e:
logger.error(e.__class__)
break
except Exception as e:
logger.error(e.__class__)
break
if is_correct_id(s): # we are logged in or don't need to log in
break # break out of the while loop
if is_password_prompt(s):
success, s = self.send_password()
if not success:
break
if is_correct_id(s):
logger.info("ID is correct, reading some data from device")
self.initialize_me()
logger.info("Connected\n")
return True
else:
logger.info(" Failed to Connect\n")
return False
def send_password(self):
logger.info("sending password")
r = None
try:
self.port.write(globe.password + '\r')
r = self.port.read() # should respond with "OK"
except OSError as e:
logger.error(e.__class__)
return False, r
except Exception as e:
logger.error(e.__class__)
return False, r
if r is None:
logger.info("response is None")
r = ""
return False, r
if r[:2] != "OK": # if characters 0 thru 1 of s does = OK, go around the while loop again, getting the ID
# self.output.append(s)
logger.info("response is not 'OK'")
r = ""
return False, r
else:
logger.info("response is 'OK'")
r = ""
return True, r
def initialize_me(self):
self.revision = self.get_revision()
self.output.append('\n')
def close(self):
self.port.close_port()
# self.destroy()
def set_output(self, output):
self.output = output
def get_all(self):
old_output = self.output # save output destination
self.output = globe.dev_null # suppress output
s = "firmware revision: " + self.get_revision()
old_output.append(s)
s = "feature mode: " + str(self.get_feature_mode()) + "\n"
old_output.append(s)
self.output = old_output # restore output destination
return
def get_id(self):
msg = 'ID?\r'
r = None
logger.info('get_id: sending ' + msg)
self.output.append(msg + '\n') # ??
try:
# self.port.flushInput()
self.port.write(msg)
r = self.port.read()
except OSError as e:
if e == TimeoutError:
self.output.append("\nTimeout Error")
else:
self.output.append("\nCommunication Error\n")
except Exception as e:
logger.error(e.__class__)
return
r = r.strip(' \r\n')
logger.info('get_id: got <' + str(r) + '>')
self.output.append(r)
if r is None:
raise DeviceResponseError("None", "Bad response: None")
if 'password' in r:
raise DeviceLoggedOutError("LoggedOut", r)
return r
def get_revision(self):
# returns something like "0.012.00" or "2.02"
msg = 'REVISION?\r'
r = None
logger.info('sending ' + msg)
self.output.append(msg)
try:
self.port.write(msg)
r = self.port.read()
except OSError as e:
if e == TimeoutError:
self.output.append("\nTimeout Error")
else:
self.output.append("\nCommunication Error\n")
except Exception as e: # more specific exceptions should be already caught
logger.error(e.__class__)
logger.error("can't communicate")
raise e
logger.info('got <' + str(r) + '>')
self.output.append(r)
if r is None:
raise DeviceResponseError("None", "Bad response: None")
if 'password' in r:
raise DeviceLoggedOutError("LoggedOut", r)
r2 = r.strip(" revisionREVISION\r\n")
# now r2 should be something like "2.01"
return r2
def get_any_boolean(self, msg):
logger.info('sending ' + msg)
self.output.append(msg + '\n')
r = None
try:
self.port.write(msg)
r = self.port.read()
except OSError as e:
if e == TimeoutError:
self.output.append("\nTimeout Error")
else:
self.output.append("\nCommunication Error\n")
except Exception as e: # more specific exceptions should be already caught
logger.error(e.__class__)
# logger.error("can't log in")
raise e
logger.info('got <' + str(r) + '>')
self.output.append(r)
if r is None:
raise DeviceResponseError("None", "Bad response: None")
if 'password' in r:
raise DeviceLoggedOutError("LoggedOut", r)
try:
r2 = int(r)
except ValueError:
raise DeviceResponseError("ValueError", "Bad response: '" + r + "'")
return r2
def set_any_boolean(self, msg, data):
if data in [True, '1', 1]:
msg += ' 1\r'
else:
msg += ' 0\r'
logger.info('sending ' + msg)
self.output.append(msg + '\n')
r = None
try:
self.port.write(msg)
r = self.port.read()
except OSError as e:
if e == TimeoutError:
self.output.append("\nTimeout Error")
else:
self.output.append("\nCommunication Error\n")
except Exception as e: # more specific exceptions should be already caught
logger.error(e.__class__)
# logger.error("can't log in")
raise e
logger.info('got <' + str(r) + '>')
self.output.append(r)
if r is None:
raise DeviceResponseError("None", "Bad response: None")
if 'password' in r:
raise DeviceLoggedOutError("LoggedOut", r)
return r
def get_any_string(self, msg):
logger.info('sending ' + msg)
self.output.append(msg)
r = None
try:
self.port.write(msg)
r = self.port.read()
except OSError as e:
if e == TimeoutError:
self.output.append("\nTimeout Error")
else:
self.output.append("\nCommunication Error\n")
except Exception as e: # more specific exceptions should be already caught
logger.error(e.__class__)
# logger.error("can't log in")
raise e
r = r.rstrip("\r\n")
logger.info('got <' + str(r) + '>')
return r
def get_feature_mode(self):
r = self.get_any_boolean("FEATURE?\r")
if r is None:
return True # not sure what desired behavior is #TODO
return r
def set_feature_mode(self, b):
return self.set_any_boolean("FEATURE", b)
def set_baud(self, b):
return self.set_any_boolean("BAUD", b)