GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — add-logging ( ee55f7...3862e4 )
by
unknown
01:00
created

Driver.open()   A

Complexity

Conditions 4

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
cc 4
c 6
b 0
f 0
dl 0
loc 7
rs 9.2
1
from abc import abstractmethod
2
from queue import Queue
3
from threading import Lock, Thread, Event
4
5
import math
6
from serial import Serial, SerialException, SerialTimeoutException
7
8
import usb
9
import time
10
from struct import *
11
12
from libAnt.constants import MESSAGE_TX_SYNC, MESSAGE_CHANNEL_BROADCAST_DATA
13
from libAnt.message import Message, SystemResetMessage
14
15
16
class DriverException(Exception):
17
    pass
18
19
class Logger:
20
    def __init__(self, logFile: str):
21
        self._logFile = logFile
22
        self._log = None
23
24
    def __enter__(self):
25
        self.open()
26
        return self
27
28
    def __exit__(self, exc_type, exc_val, exc_tb):
29
        self.close()
30
31
    def open(self):
32
        def validate(logFile: str) -> str:
33
            print("got", logFile)
34
            if '.' in logFile:
35
                name, ext = logFile.split('.', 2)
36
                ext = '.' + ext
37
            else:
38
                name = logFile
39
                ext = ''
40
            num = 0
41
            exists = True
42
            while(exists):
43
                logFile = name + '-' + str(num) + ext
44
                try:
45
                    with open(logFile):
46
                except IOError:
47
                    return logFile
48
                num += 1
49
50
        if self._log is not None:
51
            self.close()
52
        self._logFile = validate(self._logFile)
53
        print(self._logFile)
54
        self._log = open(self._logFile, 'wb')
55
        self.onOpen()
56
57
    def close(self):
58
        if self._log is not None:
59
            self.beforeClose()
60
            self._log.close()
61
            self.afterClose()
62
63
    def log(self, data: bytes):
64
        self._log.write(self.encodeData(data))
65
66
    def onOpen(self):
67
        pass
68
69
    def beforeClose(self):
70
        pass
71
72
    def afterClose(self):
73
        pass
74
75
    def encodeData(self, data):
76
        return data
77
78
class PcapLogger(Logger):
79
    def onOpen(self):
80
        # write pcap global header
81
        magic_number = b'\xD4\xC3\xB2\xA1'
82
        version_major = 2
83
        version_minor = 4
84
        thiszone = b'\x00\x00\x00\x00'
85
        sigfigs = b'\x00\x00\x00\x00'
86
        snaplen = b'\xFF\x00\x00\x00'
87
        network = b'\x01\x00\x00\x00'
88
        pcap_global_header = Struct('<4shh4s4s4s4s')
89
        self._log.write(
90
            pcap_global_header.pack(magic_number, version_major, version_minor, thiszone, sigfigs,
91
                                    snaplen, network))
92
93
    def encodeData(self, data):
94
        timestamp = time.time()
95
        frac, whole = math.modf(timestamp)
96
97
        ts_sec = int(whole).to_bytes(4, byteorder='little')
98
        ts_usec = int(frac * 1000 * 1000).to_bytes(4, byteorder='little')
99
        incl_len = len(data)
100
        orig_len = incl_len
101
102
        pcap_packet_header = Struct('<4s4sll').pack(ts_sec, ts_usec, incl_len, orig_len)
103
        return pcap_packet_header + data
104
105
class Driver:
106
    """
107
    The driver provides an interface to read and write raw data to and from an ANT+ capable hardware device
108
    """
109
110
    def __init__(self, logger: Logger = None):
111
        self._lock = Lock()
112
        self._logger = logger
113
        self._openTime = None
114
115
    def __enter__(self):
116
        self.open()
117
        return self
118
119
    def __exit__(self, exc_type, exc_val, exc_tb):
120
        self.close()
121
122
    def isOpen(self) -> bool:
123
        with self._lock:
124
            return self._isOpen()
125
126
    def open(self) -> None:
127
        with self._lock:
128
            if not self._isOpen():
129
                self._openTime = time.time()
130
                if self._logger is not None:
131
                    self._logger.open()
132
                self._open()
133
134
    def close(self) -> None:
135
        with self._lock:
136
            if self._isOpen:
137
                self._close()
138
                if self._logger is not None:
139
                    self._logger.close()
140
141
    def reOpen(self) -> None:
142
        with self._lock:
143
            if self._isOpen():
144
                self._close()
145
            self._open()
146
147
    def read(self, timeout=None) -> Message:
148
        # Splits the string into a list of tokens every n characters
149
        def splitN(str1, n):
150
            return [str1[start:start + n] for start in range(0, len(str1), n)]
151
152
        if not self.isOpen():
153
            raise DriverException("Device is closed")
154
155
        with self._lock:
156
            while True:
157
                sync = self._read(1, timeout=timeout)[0]
158
                if sync is not MESSAGE_TX_SYNC:
159
                    continue
160
                length = self._read(1, timeout=timeout)[0]
161
                type = self._read(1, timeout=timeout)[0]
162
                data = self._read(length, timeout=timeout)
163
                chk = self._read(1, timeout=timeout)[0]
164
                msg = Message(type, data)
165
166
                if self._logger:
167
                    logMsg = bytearray([sync, length, type])
168
                    logMsg.extend(data)
169
                    logMsg.append(chk)
170
171
                    self._logger.log(bytes(logMsg))
172
173
                if msg.checksum() == chk:
174
                    return msg
175
176
    def write(self, msg: Message) -> None:
177
        if not self.isOpen():
178
            raise DriverException("Device is closed")
179
180
        with self._lock:
181
            self._write(msg.encode())
182
183
    @abstractmethod
184
    def _isOpen(self) -> bool:
185
        pass
186
187
    @abstractmethod
188
    def _open(self) -> None:
189
        pass
190
191
    @abstractmethod
192
    def _close(self) -> None:
193
        pass
194
195
    @abstractmethod
196
    def _read(self, count: int, timeout=None) -> bytes:
197
        pass
198
199
    @abstractmethod
200
    def _write(self, data: bytes) -> None:
201
        pass
202
203
204
class SerialDriver(Driver):
205
    """
206
    An implementation of a serial ANT+ device driver
207
    """
208
209
    def __init__(self, device: str, baudRate: int = 115200, logger: Logger = None):
210
        super().__init__(logger=logger)
211
        self._device = device
212
        self._baudRate = baudRate
213
        self._serial = None
214
215
    def __str__(self):
216
        if self.isOpen():
217
            return self._device + " @ " + str(self._baudRate)
218
        return None
219
220
    def _isOpen(self) -> bool:
221
        return self._serial is None
222
223
    def _open(self) -> None:
224
        try:
225
            self._serial = Serial(self._device, self._baudRate)
226
        except SerialException as e:
227
            raise DriverException(str(e))
228
229
        if not self._serial.isOpen():
230
            raise DriverException("Could not open specified device")
231
232
    def _close(self) -> None:
233
        self._serial.close()
234
        self._serial = None
235
236
    def _read(self, count: int, timeout=None) -> bytes:
237
        return self._serial.read(count, timeout=timeout)
238
239
    def _write(self, data: bytes) -> None:
240
        try:
241
            self._serial.write(data)
242
            self._serial.flush()
243
        except SerialTimeoutException as e:
244
            raise DriverException(str(e))
245
246
247
class USBDriver(Driver):
248
    """
249
    An implementation of a USB ANT+ device driver
250
    """
251
252
    def __init__(self, vid, pid, logger: Logger = None):
253
        super().__init__(logger=logger)
254
        self._idVendor = vid
255
        self._idProduct = pid
256
        self._dev = None
257
        self._epOut = None
258
        self._epIn = None
259
        self._interfaceNumber = None
260
        self._packetSize = 0x20
261
        self._queue = None
262
        self._loop = None
263
        self._driver_open = False
264
265
    def __str__(self):
266
        if self.isOpen():
267
            return str(self._dev)
268
        return "Closed"
269
270
    class USBLoop(Thread):
271
        def __init__(self, ep, packetSize: int, queue: Queue):
272
            super().__init__()
273
            self._stopper = Event()
274
            self._ep = ep
275
            self._packetSize = packetSize
276
            self._queue = queue
277
278
        def stop(self) -> None:
279
            self._stopper.set()
280
281
        def run(self) -> None:
282
            while not self._stopper.is_set():
283
                try:
284
                    data = self._ep.read(self._packetSize, timeout=1000)
285
                    for d in data:
286
                        self._queue.put(d)
287
                except usb.core.USBError as e:
288
                    if e.errno not in (60, 110) and e.backend_error_code != -116:  # Timout errors
289
                        self._stopper.set()
290
            # We Put in an invalid byte so threads will realize the device is stopped
291
            self._queue.put(None)
292
293
    def _isOpen(self) -> bool:
294
        return self._driver_open
295
296
    def _open(self) -> None:
297
        print('USB OPEN START')
298
        try:
299
            # find the first USB device that matches the filter
300
            self._dev = usb.core.find(idVendor=self._idVendor, idProduct=self._idProduct)
301
302
            if self._dev is None:
303
                raise DriverException("Could not open specified device")
304
305
            # Detach kernel driver
306
            try:
307
                if self._dev.is_kernel_driver_active(0):
308
                    try:
309
                        self._dev.detach_kernel_driver(0)
310
                    except usb.USBError as e:
311
                        raise DriverException("Could not detach kernel driver")
312
            except NotImplementedError:
313
                pass  # for non unix systems
314
315
            # set the active configuration. With no arguments, the first
316
            # configuration will be the active one
317
            self._dev.set_configuration()
318
319
            # get an endpoint instance
320
            cfg = self._dev.get_active_configuration()
321
            self._interfaceNumber = cfg[(0, 0)].bInterfaceNumber
322
            interface = usb.util.find_descriptor(cfg, bInterfaceNumber=self._interfaceNumber,
323
                                                 bAlternateSetting=usb.control.get_interface(self._dev,
324
                                                                                             self._interfaceNumber))
325
            usb.util.claim_interface(self._dev, self._interfaceNumber)
326
327
            self._epOut = usb.util.find_descriptor(interface, custom_match=lambda e: usb.util.endpoint_direction(
328
                e.bEndpointAddress) == usb.ENDPOINT_OUT)
329
330
            self._epIn = usb.util.find_descriptor(interface, custom_match=lambda e: usb.util.endpoint_direction(
331
                e.bEndpointAddress) == usb.ENDPOINT_IN)
332
333
            if self._epOut is None or self._epIn is None:
334
                raise DriverException("Could not initialize USB endpoint")
335
336
            self._queue = Queue()
337
            self._loop = self.USBLoop(self._epIn, self._packetSize, self._queue)
338
            self._loop.start()
339
            self._driver_open = True
340
            print('USB OPEN SUCCESS')
341
        except IOError as e:
342
            self._close()
343
            raise DriverException(str(e))
344
345
    def _close(self) -> None:
346
        print('USB CLOSE START')
347
        if self._loop is not None:
348
            if self._loop.is_alive():
349
                self._loop.stop()
350
                self._loop.join()
351
        self._loop = None
352
        try:
353
            self._dev.reset()
354
            usb.util.dispose_resources(self._dev)
355
        except:
356
            pass
357
        self._dev = self._epOut = self._epIn = None
358
        self._driver_open = False
359
        print('USB CLOSE END')
360
361
    def _read(self, count: int, timeout=None) -> bytes:
362
        data = bytearray()
363
        for i in range(0, count):
364
            b = self._queue.get(timeout=timeout)
365
            if b is None:
366
                self._close()
367
                raise DriverException("Device is closed!")
368
            data.append(b)
369
        return bytes(data)
370
371
    def _write(self, data: bytes) -> None:
372
        return self._epOut.write(data)
373
374
375
class DummyDriver(Driver):
376
    def __init__(self, logger: Logger = None):
377
        super().__init__(logger=logger)
378
        self._isopen = False
379
        self._data = Queue()
380
        msg1 = Message(MESSAGE_CHANNEL_BROADCAST_DATA, b'\x00\x01\x02\x03\x04\x05\x06\x07').encode()
381
        for b in msg1:
382
            self._data.put(b)
383
        msg2 = Message(MESSAGE_CHANNEL_BROADCAST_DATA, b'\x00\xF1\xF2\xF3\xF4\xF5\xF6\xF7').encode()
384
        for b in msg2:
385
            self._data.put(b)
386
        msg3 = Message(MESSAGE_CHANNEL_BROADCAST_DATA, b'\x00\xF9\xFA\xFB\xFC\xFD\xFE\xFF').encode()
387
        for b in msg3:
388
            self._data.put(b)
389
390
    def _isOpen(self) -> bool:
391
        return self._isopen
392
393
    def _close(self) -> None:
394
        self._isopen = False
395
396
    def _read(self, count: int, timeout=None) -> bytes:
397
        data = bytearray()
398
        for i in range(0, count):
399
            data.append(self._data.get(timeout=timeout))
400
        return bytes(data)
401
402
    def _open(self) -> None:
403
        self._isopen = True
404
405
    def _write(self, data: bytes) -> None:
406
        pass
407
408
409
class PcapDriver(Driver):
410
    def __init__(self, pcap, logger: Logger = None):
411
        super().__init__(logger=logger)
412
        self._isopen = False
413
        self._pcap = pcap
414
        self._buffer = Queue()
415
416
        self._loop = None
417
418
    class PcapLoop(Thread):
419
        def __init__(self, pcap, buffer: Queue):
420
            super().__init__()
421
            self._stopper = Event()
422
            self._pcap = pcap
423
            self._buffer = buffer
424
425
        def stop(self) -> None:
426
            self._stopper.set()
427
428
        def run(self) -> None:
429
            self._pcapfile = open(self._pcap, 'rb')
430
            # move file pointer to first packet header
431
            global_header_length = 24
432
            self._pcapfile.seek(global_header_length, 0)
433
434
            first_ts = 0
435
            start_time = time.time()
436
            while not self._stopper.is_set():
437
                try:
438
                    ts_sec, = unpack('i', self._pcapfile.read(4))
439
                except error:
440
                    break
441
                ts_usec = unpack('i', self._pcapfile.read(4))[0] / 1000000
442
443
                if first_ts is 0:
444
                    first_ts = ts_sec + ts_usec
445
446
                ts = ts_sec + ts_usec
447
                send_time = ts - first_ts
448
                elapsed_time = time.time() - start_time
449
                if send_time > (elapsed_time):
450
                    sleep_time = send_time - elapsed_time
451
                    time.sleep(sleep_time)
452
453
                packet_length = unpack('i', self._pcapfile.read(4))[0]
454
                self._pcapfile.seek(4, 1)
455
                for i in range(packet_length):
456
                    self._buffer.put(self._pcapfile.read(1))
457
458
            self._pcapfile.close()
459
460
    def _isOpen(self) -> bool:
461
        return self._isopen
462
463
    def _open(self) -> None:
464
        self._isopen = True
465
        self._loop = self.PcapLoop(self._pcap, self._buffer)
466
        self._loop.start()
467
468
    def _close(self) -> None:
469
        self._isopen = False
470
        if self._loop is not None:
471
            if self._loop.is_alive():
472
                self._loop.stop()
473
                self._loop.join()
474
        self._loop = None
475
476
    def _read(self, count: int, timeout=None) -> bytes:
477
        result = bytearray()
478
479
        while len(result) < count:
480
            result += self._buffer.get(block=True, timeout=timeout)
481
482
        return bytes(result)
483
484
    def _write(self, data: bytes) -> None:
485
        pass
486