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
03:11 queued 01:28
created

Logger.open()   C

Complexity

Conditions 7

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 7
dl 0
loc 24
rs 5.5
c 2
b 1
f 0

1 Method

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