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
Pull Request — master (#3)
by
unknown
01:12
created

Logger.__exit__()   A

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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