wasatch 2.3.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. wasatch/AbstractUSBDevice.py +29 -0
  2. wasatch/AndorDevice.py +910 -0
  3. wasatch/AreaScanImage.py +19 -0
  4. wasatch/AutoRaman.py +457 -0
  5. wasatch/AutoRamanRequest.py +96 -0
  6. wasatch/BLEDevice.py +1032 -0
  7. wasatch/BalanceAcquisition.py +136 -0
  8. wasatch/CSVLoader.py +188 -0
  9. wasatch/CommandSettings.py +95 -0
  10. wasatch/ControlObject.py +17 -0
  11. wasatch/DetectorROI.py +36 -0
  12. wasatch/DetectorRegions.py +158 -0
  13. wasatch/DeviceFinderBLE.py +107 -0
  14. wasatch/DeviceFinderTCP.py +20 -0
  15. wasatch/DeviceFinderUSB.py +236 -0
  16. wasatch/DeviceID.py +344 -0
  17. wasatch/EEPROM.py +1450 -0
  18. wasatch/EEPROM_multical.sav +48 -0
  19. wasatch/FPGAOptions.py +125 -0
  20. wasatch/FeatureIdentificationDevice.py +3961 -0
  21. wasatch/FirmwareRequirements.py +76 -0
  22. wasatch/HardwareInfo.py +36 -0
  23. wasatch/IDSCamera.py +916 -0
  24. wasatch/IDSDevice.py +329 -0
  25. wasatch/IMX385.py +179 -0
  26. wasatch/InterfaceDevice.py +40 -0
  27. wasatch/MockUSBDevice.py +353 -0
  28. wasatch/OceanDevice.py +215 -0
  29. wasatch/PollStatus.py +15 -0
  30. wasatch/ProcessedReading.py +352 -0
  31. wasatch/ROI.py +33 -0
  32. wasatch/Reading.py +80 -0
  33. wasatch/RealUSBDevice.py +64 -0
  34. wasatch/SPIDevice.py +628 -0
  35. wasatch/SpectrometerRequest.py +15 -0
  36. wasatch/SpectrometerResponse.py +38 -0
  37. wasatch/SpectrometerSettings.py +446 -0
  38. wasatch/SpectrometerState.py +255 -0
  39. wasatch/StatusMessage.py +14 -0
  40. wasatch/TCPDevice.py +409 -0
  41. wasatch/TakeOneRequest.py +43 -0
  42. wasatch/WasatchBus.py +87 -0
  43. wasatch/WasatchDevice.py +1060 -0
  44. wasatch/WasatchDeviceWrapper.py +452 -0
  45. wasatch/WrapperWorker.py +304 -0
  46. wasatch/__init__.py +4 -0
  47. wasatch/applog.py +177 -0
  48. wasatch/simulation_protocol.py +227 -0
  49. wasatch/utils.py +731 -0
  50. wasatch-2.3.2.dist-info/LICENSE +21 -0
  51. wasatch-2.3.2.dist-info/METADATA +347 -0
  52. wasatch-2.3.2.dist-info/RECORD +53 -0
  53. wasatch-2.3.2.dist-info/WHEEL +4 -0
@@ -0,0 +1,14 @@
1
+ class StatusMessage:
2
+ """
3
+ An outbound analog to ControlObject. Not re-using the same class so that
4
+ it can be extended in different ways, perhaps subclassed etc.
5
+
6
+ There is no enumeration of supported status messages, but ENLIGHTEN's
7
+ Controller.process_status_message() would be a good source.
8
+ """
9
+ def __init__(self, setting, value):
10
+ self.setting = setting # probably a string
11
+ self.value = value # don't presume type
12
+
13
+ def __repr__(self):
14
+ return f"StatusMessage<setting {self.setting}, value {self.value}>"
wasatch/TCPDevice.py ADDED
@@ -0,0 +1,409 @@
1
+ import logging
2
+ import struct
3
+ import socket
4
+
5
+ from .SpectrometerResponse import SpectrometerResponse, ErrorLevel
6
+ from .SpectrometerSettings import SpectrometerSettings
7
+ from .InterfaceDevice import InterfaceDevice
8
+ from .DeviceID import DeviceID
9
+ from .Reading import Reading
10
+
11
+ log = logging.getLogger(__name__)
12
+
13
+ class TCPDevice(InterfaceDevice):
14
+
15
+ SUCCESS = 0x00 # byte response from setter and command (ACQUIRE, DISCONNECT) opcodes
16
+
17
+ ############################################################################
18
+ # lifecycle
19
+ ############################################################################
20
+
21
+ def __init__(self, device_id, message_queue=None, alert_queue=None):
22
+ super().__init__()
23
+
24
+ self.device_id = device_id
25
+ if device_id.type != "TCP":
26
+ raise RuntimeError("TCPDevice can only be constructed with TCP DeviceID")
27
+
28
+ self.addr = device_id.address
29
+ self.port = device_id.port
30
+
31
+ self.process_f = self.init_process_funcs()
32
+
33
+ self.reset()
34
+
35
+ def reset(self):
36
+ self.sock = None
37
+ self.mode = "ascii"
38
+
39
+ def connect(self):
40
+ """
41
+ This method is called by WrapperWorker.run. At the end of the method,
42
+ an InterfaceDevice should have a non-null .settings with a populated
43
+ SpectrometerSettings.
44
+ """
45
+ log.debug(f"connect: trying to connect to {self.device_id}")
46
+ try:
47
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
48
+ self.sock.settimeout(3)
49
+ self.sock.connect((self.addr, self.port))
50
+ except:
51
+ log.error(f"connect: failed to connect to {self.addr} port {self.port}", exc_info=1)
52
+ return SpectrometerResponse(error_lvl=ErrorLevel.high, error_msg=f"Failed to connect to {self.device_id}")
53
+
54
+ log.debug(f"connect: connected to {self.device_id}")
55
+
56
+ self.set_binary_mode()
57
+
58
+ self.protocol_version = self.get_protocol_version()
59
+ log.debug(f"Protocol version: {self.protocol_version}")
60
+
61
+ # use this to hold settings that already have a standard place
62
+ self.settings = SpectrometerSettings()
63
+
64
+ self.settings.state.microcontroller_firmware_version = self.get_firmware_version()
65
+
66
+ # this is not currently a SpectrometerSettings attribute
67
+ self.product_id = self.get_product_id()
68
+ log.debug(f"Product ID: {self.product_id}")
69
+
70
+ self.settings.eeprom.model = self.get_model_name()
71
+ log.debug(f"Model Name: {self.settings.eeprom.model}")
72
+
73
+ self.settings.eeprom.serial_number = self.get_serial_number()
74
+ log.debug(f"Serial Number: {self.settings.eeprom.serial_number}")
75
+
76
+ self.settings.eeprom.active_pixels_horizontal = self.get_line_length()
77
+ self.settings.eeprom.active_pixels_vertical = self.get_line_count()
78
+ log.debug(f"Detector Size: {self.settings.pixels()} (H) x {self.settings.eeprom.active_pixels_vertical} (V)")
79
+
80
+ self.settings.eeprom.excitation_nm_float = self.get_excitation()
81
+ log.debug(f"Excitation: {self.settings.excitation()}")
82
+
83
+ # read wavecal
84
+ self.settings.eeprom.wavecal_coeffs = []
85
+ for i in range(5):
86
+ self.settings.eeprom.wavecal_coeffs.append(self.get_wavecal_coeff(i))
87
+ self.settings.update_wavecal()
88
+
89
+ return SpectrometerResponse(True)
90
+
91
+ def disconnect(self):
92
+ if self.sock:
93
+ self.send_cmd(0xff, 0xaa14, label="DISCONNECT")
94
+ self.reset()
95
+
96
+ ############################################################################
97
+ # ascii protocol
98
+ ############################################################################
99
+
100
+ def set_binary_mode(self):
101
+ log.debug("awaiting ready")
102
+ data = self.read_data(3)
103
+ log.debug("<< " + "".join([chr(c) for c in data]))
104
+ for a, b in zip(data, bytes("OK\n", encoding='ascii')):
105
+ if a != b:
106
+ raise(RuntimeError("failed handshaking"))
107
+
108
+ log.debug("setting binary mode")
109
+ self.send_string("BIN\n")
110
+ if 0 == self.read_data(1)[0]:
111
+ self.mode = "binary"
112
+ else:
113
+ raise(RuntimeError("failed to set BIN mode"))
114
+
115
+ ############################################################################
116
+ # binary protocol
117
+ ############################################################################
118
+
119
+ def get_protocol_version(self):
120
+ data = self.get_cmd(0xff, 0xaa13, length=4, label="GET_PROTOCOL_VERSION")
121
+ return ".".join([str(v) for v in data])
122
+
123
+ def get_firmware_version(self):
124
+ data = self.get_cmd(0xc0, length=4, label="GET_FIRMWARE_VERSION")
125
+ return ".".join([str(v) for v in data])
126
+
127
+ def get_product_id(self):
128
+ data = self.get_cmd(0xff, 0xaa01, length=4, label="GET_PRODUCT_ID")
129
+ vid = (data[0] << 8) | data[1]
130
+ pid = (data[2] << 8) | data[3]
131
+ return f"0x{vid:04x}:0x{pid:04x}"
132
+
133
+ def get_model_name(self):
134
+ return self.get_cmd(0xff, 0xaa0b, str_len=32, label="GET_MODEL_NAME")
135
+
136
+ def get_serial_number(self):
137
+ return self.get_cmd(0xff, 0xaa09, str_len=16, label="GET_SERIAL_NUMBER")
138
+
139
+ def get_line_length(self):
140
+ return self.get_cmd(0x03, lsb_len=2, label="GET_LINE_LENGTH")
141
+
142
+ def get_line_count(self):
143
+ return self.get_cmd(0xff, 0xaa10, lsb_len=2, label="GET_LINE_COUNT")
144
+
145
+ def get_excitation(self):
146
+ data = self.get_cmd(0xff, 0xaa12, length=4, label="GET_EXCITATION")
147
+ return self.to_float32(data, label="GET_EXCITATION")
148
+
149
+ def get_wavecal_coeff(self, exponent):
150
+ label = f"GET_WAVECAL_COEFF({exponent})"
151
+ data = self.get_cmd(0xff, 0xaa0d, exponent, length=4, label=label)
152
+ return self.to_float32(data, label=label)
153
+
154
+ def set_integration_time_ms(self, ms):
155
+ lsw = ms & 0xffff
156
+ msb = (ms >> 16) & 0xff
157
+ self.send_cmd(0xb2, lsw, msb, label="SET_INTEGRATION_TIME_MS")
158
+
159
+ def get_integration_time_ms(self):
160
+ return self.get_cmd(0xbf, lsb_len=3, label="GET_INTEGRATION_TIME_MS")
161
+
162
+ def set_detector_gain(self, db):
163
+ word = self.float_to_uint16(db, label="SET_DETECTOR_GAIN")
164
+ self.send_cmd(0xb7, word, label="SET_DETECTOR_GAIN")
165
+
166
+ def get_detector_gain(self):
167
+ data = self.get_cmd(0xc5, length=2, label="GET_DETECTOR_GAIN")
168
+ msb = data[0]
169
+ lsb = data[1]
170
+ return msb + lsb / 256.0
171
+
172
+ def set_start_line(self, line):
173
+ self.send_cmd(0xff, 0x21, line, label="SET_START_LINE")
174
+
175
+ def get_start_line(self):
176
+ return self.get_cmd(0xff, 0x22, lsb_len=2, label="GET_START_LINE")
177
+
178
+ def set_stop_line(self, line):
179
+ self.send_cmd(0xff, 0x23, line, label="SET_STOP_LINE")
180
+
181
+ def get_stop_line(self):
182
+ return self.get_cmd(0xff, 0x24, lsb_len=2, label="GET_STOP_LINE")
183
+
184
+ def get_spectrum(self):
185
+ self.send_cmd(0xad, label="ACQUIRE")
186
+ data = self.read_data(self.settings.pixels() * 2, label="GET_SPECTRUM", quiet=True)
187
+
188
+ spectrum = []
189
+ for i in range(self.settings.pixels()):
190
+ intensity = data[i*2] | (data[i*2 + 1] << 8) # little-endian
191
+ spectrum.append(intensity)
192
+
193
+ return spectrum
194
+
195
+ # not implemented:
196
+ #
197
+ # - SET_AREA_SCAN_MODE
198
+ # - SET_IMX_SENSOR_MODE
199
+ # - GET_IMX_SENSOR_MODE
200
+ # - SET_START_PIXEL
201
+ # - GET_START_PIXEL
202
+ # - SET_STOP_PIXEL
203
+ # - GET_STOP_PIXEL
204
+ # - SET_SERIAL_NUMBER
205
+ # - SET_MODEL_NAME
206
+ # - SET_WAVELENGTH_COEFF
207
+ # - SET_BAD_PIXEL
208
+ # - GET_BAD_PIXEL
209
+
210
+ ############################################################################
211
+ # operations
212
+ ############################################################################
213
+
214
+ def acquire_data(self):
215
+ reading = Reading(self.device_id)
216
+
217
+ spectrum = self.get_spectrum()
218
+
219
+ reading.spectrum = spectrum
220
+
221
+ return SpectrometerResponse(data=reading)
222
+
223
+ ############################################################################
224
+ # utility
225
+ ############################################################################
226
+
227
+ def to_hex(self, data):
228
+ return "[ " + " ".join([f"0x{v:02x}" for v in data]) + " ]"
229
+
230
+ def get_cmd(self, bRequest, wValue=0, wIndex=0, lsb_len=None, msb_len=None, str_len=None, length=None, label=None):
231
+ """
232
+ bRequest is required, and one length parameter must be provided. The
233
+ length parameter used determines the return data format.
234
+ """
235
+ # send outgoing request
236
+ packet = MessagePacket(bRequest, wValue, wIndex)
237
+ serialized = packet.serialize()
238
+ log.debug(f">> {packet} ({label})")
239
+ log.debug(f">> {self.to_hex(serialized)} ({label})")
240
+ self.sock.sendall(serialized)
241
+
242
+ # determine response length
243
+ if lsb_len: expected_len = lsb_len
244
+ elif msb_len: expected_len = msb_len
245
+ elif str_len: expected_len = str_len
246
+ elif length: expected_len = length
247
+ else:
248
+ raise(RuntimeError(f"get_cmd called without length parameter [{label}]"))
249
+
250
+ data = self.read_data(expected_len, label=label)
251
+
252
+ # demarshall response
253
+ value = 0
254
+ if lsb_len:
255
+ for i, byte in enumerate(data):
256
+ value |= (byte << (i * 8)) # little-endian
257
+ elif msb_len:
258
+ for byte in data:
259
+ value = (value << 8) | byte # big-endian
260
+ elif str_len:
261
+ value = ""
262
+ for c in data:
263
+ if c == 0: # null-terminated
264
+ break
265
+ value += chr(c)
266
+ else:
267
+ value = data # raw data
268
+
269
+ # log.debug(f"get_cmd: received {value} ({label})")
270
+ return value
271
+
272
+ def send_cmd(self, bRequest, wValue=0, wIndex=0, payload=None, readback_len=None, label=None):
273
+ packet = MessagePacket(bRequest, wValue, wIndex, payload)
274
+ data = packet.serialize()
275
+ log.debug(f">> {packet} ({label})")
276
+ log.debug(f">> {self.to_hex(data)} ({label})")
277
+ self.sock.sendall(data)
278
+
279
+ if readback_len is None:
280
+ # by default, read back one byte
281
+ response = self.read_data(1)[0]
282
+ if response != self.SUCCESS:
283
+ raise(RuntimeError(f"send_cmd received {response} from {packet} [{label}]"))
284
+ log.debug(f"<< SUCCESS ({label})")
285
+ else:
286
+ response = self.read_data(readback_len, label=label)
287
+ log.debug(f"<< {self.to_hex(response)} ({label})")
288
+ return response
289
+
290
+ def read_data(self, length, label=None, quiet=False):
291
+ if length == 0:
292
+ return
293
+
294
+ response = []
295
+ while True:
296
+ data = self.sock.recv(1)
297
+ if len(data):
298
+ response.append(data[0])
299
+ if length == len(response):
300
+ retval = bytes(response)
301
+ if quiet:
302
+ log.debug(f"<< ({length} bytes) ({label})")
303
+ else:
304
+ log.debug(f"<< {self.to_hex(response)} ({length} bytes) ({label})")
305
+ return retval
306
+
307
+ def send_string(self, msg, length=None, label=None):
308
+ if length is None:
309
+ length = len(msg)
310
+ data = []
311
+ for i in range(length):
312
+ if i < len(msg):
313
+ data.append(ord(msg[i]))
314
+ else:
315
+ data.append(0)
316
+ log.debug(f">> {data} ({msg.strip()}) ({label})")
317
+ self.sock.sendall(bytes(data))
318
+
319
+ def to_float32(self, data, label=None):
320
+ value = struct.unpack('f', data)[0]
321
+ return value
322
+
323
+ def float_to_uint16(self, f, label=None):
324
+ msb = int(round(f, 5)) & 0xff
325
+ lsb = int((f - msb) * 256) & 0xff
326
+ value = (msb << 8) | lsb
327
+ return value
328
+
329
+ def init_process_funcs(self):
330
+ process_f = {}
331
+
332
+ process_f["connect"] = self.connect
333
+ process_f["disconnect"] = self.disconnect
334
+ process_f["close"] = self.disconnect
335
+
336
+ process_f["acquire_data"] = self.acquire_data
337
+
338
+ process_f["integration_time_ms"] = lambda x: self.set_integration_time_ms(x)
339
+ process_f["detector_gain"] = lambda x: self.set_gain(x)
340
+ process_f["laser_enable"] = lambda x: self.set_laser_enable(x)
341
+ process_f["vertical_binning"] = lambda x: self.set_vertical_roi(x)
342
+
343
+ return process_f
344
+
345
+ class MessagePacket:
346
+ """
347
+ Essentially a 6-byte simplified USB Setup Packet, with bmRequestType removed
348
+ and wLength reduced to 1 byte.
349
+
350
+ This format was deliberately chosen to minimize porting complexity from
351
+ the existing ENG-0001 USB FID API.
352
+
353
+ Offset Length Datatype Field Description
354
+ ------ ------ -------- -------- ---------------------
355
+ 0 1 uint8 bRequest The command/opcode
356
+ 1 2 uint16 wValue Parameter #1 (or 2nd-tier opcode if bRequest was 0xff)
357
+ 3 2 uint16 wIndex Parameter #2
358
+ 5 1 uint8 bLength Payload length
359
+ """
360
+
361
+ def __init__(self, bRequest=None, wValue=0, wIndex=0, payload=None, serialized=None):
362
+ if serialized:
363
+ self.deserialize(serialized)
364
+ else:
365
+ self.bRequest = bRequest
366
+ self.wValue = wValue
367
+ self.wIndex = wIndex
368
+ self.payload = None
369
+
370
+ if payload is None:
371
+ self.bLength = 0
372
+ elif isinstance(payload, bytes):
373
+ self.bLength = len(payload)
374
+ self.payload = payload
375
+ else:
376
+ raise(RuntimeError(f"payload must be 'bytes' (currently {type(payload)})"))
377
+
378
+ def __repr__(self):
379
+ return f"MessagePacket(bRequest 0x{self.bRequest:02x}, wValue 0x{self.wValue:04x}, wIndex 0x{self.wIndex:04x}, bLength {self.bLength})"
380
+
381
+ def serialize(self):
382
+ data = []
383
+ data.append(self.bRequest)
384
+ data.append((self.wValue >> 8) & 0xff)
385
+ data.append((self.wValue ) & 0xff)
386
+ data.append((self.wIndex >> 8) & 0xff)
387
+ data.append((self.wIndex ) & 0xff)
388
+ data.append(self.bLength)
389
+
390
+ if self.payload is not None:
391
+ for v in self.payload:
392
+ data.append(v)
393
+
394
+ return bytes(data)
395
+
396
+ def deserialize(self, serialized):
397
+ if len(serialized) < 6:
398
+ raise(RuntimeError(f"len({serialized}) < 6"))
399
+
400
+ self.bRequest = serialized[0]
401
+ self.wValue = (serialized[1] << 8) | serialized[2]
402
+ self.wIndex = (serialized[3] << 8) | serialized[4]
403
+ self.bLength = serialized[5]
404
+
405
+ payload_len = len(serialized) - 6
406
+ if payload_len != self.bLength:
407
+ raise(RuntimeError(f"payload_len {payload_len} != bLength {self.bLength}"))
408
+
409
+ self.payload = serialized[6:]
@@ -0,0 +1,43 @@
1
+ import os
2
+ import time
3
+ import random
4
+ import threading
5
+
6
+ class TakeOneRequest:
7
+
8
+ def __init__(self, take_dark = False,
9
+ enable_laser_before = False,
10
+ disable_laser_after = False,
11
+ laser_warmup_ms = 0,
12
+ scans_to_average = 1,
13
+ auto_raman_request = None,
14
+ template = None,
15
+ readings_target = None,
16
+ readings_current = None):
17
+
18
+ if template:
19
+ self.take_dark = template.take_dark
20
+ self.enable_laser_before = template.enable_laser_before
21
+ self.disable_laser_after = template.disable_laser_after
22
+ self.laser_warmup_ms = template.laser_warmup_ms
23
+ self.scans_to_average = template.scans_to_average
24
+ self.auto_raman_request = template.auto_raman_request
25
+ self.readings_target = template.readings_target
26
+ self.readings_current = template.readings_current
27
+ else:
28
+ self.take_dark = take_dark
29
+ self.enable_laser_before = enable_laser_before
30
+ self.disable_laser_after = disable_laser_after
31
+ self.laser_warmup_ms = laser_warmup_ms
32
+ self.scans_to_average = scans_to_average
33
+ self.auto_raman_request = auto_raman_request
34
+ self.readings_target = readings_target
35
+ self.readings_current = readings_current
36
+
37
+ self.request_id = f"{os.getpid()}-{threading.get_native_id()}-{time.time_ns()}-{random.randrange(65536)}"
38
+
39
+ def __repr__(self):
40
+ return f"TakeOneRequest <id {self.request_id}, avg {self.scans_to_average}, take_dark {self.take_dark}, enable_laser_before {self.enable_laser_before}, disable_laser_after {self.disable_laser_after}, laser_warmup_ms {self.laser_warmup_ms}, readings {self.readings_current}/{self.readings_target}, auto_raman_request {self.auto_raman_request}>"
41
+
42
+ def __eq__(self, rhs):
43
+ return self.request_id == rhs.request_id
wasatch/WasatchBus.py ADDED
@@ -0,0 +1,87 @@
1
+ import logging
2
+
3
+ from .DeviceFinderUSB import DeviceFinderUSB
4
+ from .DeviceFinderTCP import DeviceFinderTCP
5
+
6
+ from usb import USBError
7
+
8
+ log = logging.getLogger(__name__)
9
+
10
+ ##
11
+ # The different bus classes don't use inheritance and don't follow a common ABC
12
+ # or interface, but each should have an update() method, and each should have a
13
+ # 'device_ids' array.
14
+ #
15
+ # @param use_sim not used, left to avoid breaking old code
16
+ # @param monitor_dir not used, left to avoid breaking old code
17
+ class WasatchBus:
18
+ def __init__(self, use_sim=False, monitor_dir=None):
19
+ self.device_ids = []
20
+
21
+ self.usb_bus = USBBus()
22
+ self.tcp_bus = TCPBus()
23
+
24
+ self.update()
25
+
26
+ ## called by enlighten.Controller.tick_bus_listener()
27
+ def update(self, poll=False):
28
+ device_ids = []
29
+ if self.usb_bus:
30
+ # MZ: if we call .extend here...when are devices ever purged from the stateful list?
31
+ # self.device_ids.extend(self.usb_bus.update(poll))
32
+ usb_device_ids = self.usb_bus.update(poll)
33
+ device_ids.extend(usb_device_ids)
34
+
35
+ if self.tcp_bus:
36
+ tcp_device_ids = self.tcp_bus.update(poll)
37
+ device_ids.extend(tcp_device_ids)
38
+
39
+ # purge any duplicates (can happen while USB device is enumerating)
40
+ self.device_ids = list(set(device_ids))
41
+
42
+ def is_empty(self):
43
+ return 0 == len(self.device_ids)
44
+
45
+ def dump(self):
46
+ """ called by Controller.update_connections """
47
+ log.debug("WasatchBus:")
48
+ for device_id in self.device_ids:
49
+ log.debug(f" {device_id}")
50
+
51
+ class USBBus:
52
+ finder = DeviceFinderUSB() # static attribute
53
+
54
+ def __init__(self):
55
+ self.backend_error_raised = False
56
+ # self.update()
57
+
58
+ def update(self, poll=False):
59
+ """ Return a list of DeviceIDs on the USB bus """
60
+ device_ids = []
61
+ try:
62
+ device_ids = self.finder.find_usb_devices(poll=True)
63
+ except USBError:
64
+ # MZ: this seems to happen when I run from Git Bash shell
65
+ # (resolved on MacOS with 'brew install libusb')
66
+ if not self.backend_error_raised:
67
+ log.warn("USBBus: No libusb backend", exc_info=1)
68
+ self.backend_error_raised = True
69
+ except Exception:
70
+ log.critical("USBBus: LIBUSB error", exc_info=1)
71
+
72
+ return device_ids
73
+
74
+ class TCPBus:
75
+
76
+ # these are static so callers can casually instantiate and release WasatchBus
77
+ # objects whenever they want, but these singletons will remain quietly
78
+ # persistent
79
+ finder = DeviceFinderTCP() # static
80
+ addresses = []
81
+
82
+ def __init__(self):
83
+ pass
84
+
85
+ def update(self, poll=False):
86
+ device_ids = self.finder.find_tcp_devices()
87
+ return device_ids