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
02:29 queued 01:17
created

PcapLogger   A

Complexity

Total Complexity 2

Size/Duplication

Total Lines 26
Duplicated Lines 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 26
rs 10
wmc 2

2 Methods

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