protobin 0.0.24__tar.gz → 0.0.26__tar.gz
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.
- {protobin-0.0.24 → protobin-0.0.26}/PKG-INFO +1 -1
- {protobin-0.0.24 → protobin-0.0.26}/protobin/fields.py +17 -7
- {protobin-0.0.24 → protobin-0.0.26}/protobin/protocol.py +28 -15
- {protobin-0.0.24 → protobin-0.0.26}/pyproject.toml +1 -1
- {protobin-0.0.24 → protobin-0.0.26}/tests/codec8.json +3 -3
- {protobin-0.0.24 → protobin-0.0.26}/tests/tests.py +33 -26
- {protobin-0.0.24 → protobin-0.0.26}/.gitignore +0 -0
- {protobin-0.0.24 → protobin-0.0.26}/LICENSE.txt +0 -0
- {protobin-0.0.24 → protobin-0.0.26}/README.md +0 -0
- {protobin-0.0.24 → protobin-0.0.26}/protobin/__init__.py +0 -0
- {protobin-0.0.24 → protobin-0.0.26}/protobin/errors.py +0 -0
- {protobin-0.0.24 → protobin-0.0.26}/protobin/interfaces.py +0 -0
- {protobin-0.0.24 → protobin-0.0.26}/tests/demo.json +0 -0
- {protobin-0.0.24 → protobin-0.0.26}/tests/demo.yaml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: protobin
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.26
|
|
4
4
|
Summary: Python library for encode and decode data in protobin format
|
|
5
5
|
Project-URL: Homepage, https://github.com/drmelectronic/protobin-python
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/drmelectronic/protobin-python/issues
|
|
@@ -6,6 +6,7 @@ from typing import Union, List
|
|
|
6
6
|
from protobin.errors import ParserError, FormatError
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
# removed for python 3.8
|
|
9
10
|
# class FieldEnum(enum.StrEnum):
|
|
10
11
|
# ARRAY = 'array'
|
|
11
12
|
# BITS = 'bits'
|
|
@@ -24,9 +25,10 @@ from protobin.errors import ParserError, FormatError
|
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class FieldBase:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
# removed for python 3.8
|
|
29
|
+
# bytes: int | None
|
|
30
|
+
# keys: List[str] | None
|
|
31
|
+
# key: str
|
|
30
32
|
# type: FieldEnum
|
|
31
33
|
|
|
32
34
|
def __init__(self, k, js):
|
|
@@ -231,7 +233,9 @@ class DateField(FieldBase):
|
|
|
231
233
|
def __repr__(self):
|
|
232
234
|
return f'DateField<key: {self.key}>'
|
|
233
235
|
|
|
234
|
-
|
|
236
|
+
# removed for python 3.8
|
|
237
|
+
# def to_binary(self, val: datetime.datetime | str):
|
|
238
|
+
def to_binary(self, val):
|
|
235
239
|
if val is None:
|
|
236
240
|
return bytes([0] * 3)
|
|
237
241
|
elif isinstance(val, str):
|
|
@@ -260,7 +264,9 @@ class DateTimeField(FieldBase):
|
|
|
260
264
|
def __repr__(self):
|
|
261
265
|
return f'DateTimeField<key: {self.key}>'
|
|
262
266
|
|
|
263
|
-
|
|
267
|
+
# removed for python 3.8
|
|
268
|
+
# def to_binary(self, val: datetime.datetime | str):
|
|
269
|
+
def to_binary(self, val):
|
|
264
270
|
if val is None:
|
|
265
271
|
return bytes([0] * 6)
|
|
266
272
|
elif not isinstance(val, datetime.datetime):
|
|
@@ -420,7 +426,9 @@ class TimeField(FieldBase):
|
|
|
420
426
|
def __repr__(self):
|
|
421
427
|
return f'TimeField<key: {self.key}, bytes: {self.bytes}>'
|
|
422
428
|
|
|
423
|
-
|
|
429
|
+
# removed for python 3.8
|
|
430
|
+
# def to_binary(self, val: datetime.datetime | datetime.time | str):
|
|
431
|
+
def to_binary(self, val):
|
|
424
432
|
if val is None:
|
|
425
433
|
return bytes([0] * 2)
|
|
426
434
|
elif isinstance(val, str):
|
|
@@ -455,7 +463,9 @@ class TimestampField(FieldBase):
|
|
|
455
463
|
def __repr__(self):
|
|
456
464
|
return f'TimestampField<key: {self.key}>'
|
|
457
465
|
|
|
458
|
-
|
|
466
|
+
# removed for python 3.8
|
|
467
|
+
# def to_binary(self, val: datetime.datetime | str):
|
|
468
|
+
def to_binary(self, val):
|
|
459
469
|
if val is None:
|
|
460
470
|
return bytes([0] * 8)
|
|
461
471
|
timestamp = val.timestamp() * (10 ** self.decimals)
|
|
@@ -42,7 +42,9 @@ class Format:
|
|
|
42
42
|
if self.header:
|
|
43
43
|
binary += self.header.encode('utf') + b'='
|
|
44
44
|
elif self.codec:
|
|
45
|
-
|
|
45
|
+
# added for python 3.8
|
|
46
|
+
# binary += self.codec.to_bytes()
|
|
47
|
+
binary += self.codec.to_bytes(1, 'big')
|
|
46
48
|
for f in self.output_fields:
|
|
47
49
|
binary += f.encode(data)
|
|
48
50
|
return binary
|
|
@@ -99,7 +101,9 @@ class Protocol:
|
|
|
99
101
|
return self.formats[self.headers[h.decode('utf')]]
|
|
100
102
|
|
|
101
103
|
def get_codec(self, h):
|
|
102
|
-
|
|
104
|
+
# added for python 3.8
|
|
105
|
+
# return self.formats[self.codecs[int.from_bytes(h)]]
|
|
106
|
+
return self.formats[self.codecs[int.from_bytes(h, 'big')]]
|
|
103
107
|
|
|
104
108
|
def encode(self, data, format_key):
|
|
105
109
|
if format_key not in self.formats:
|
|
@@ -110,21 +114,30 @@ class Protocol:
|
|
|
110
114
|
binary = len(binary).to_bytes(self.length, 'big', signed=False) + binary + self.get_crc(binary)
|
|
111
115
|
return binary
|
|
112
116
|
|
|
113
|
-
def
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
def get_header(self, binary):
|
|
118
|
+
n = binary.find(b'=')
|
|
119
|
+
if n == -1:
|
|
120
|
+
binary = self.check_crc(binary)
|
|
121
|
+
h = binary[:1]
|
|
122
|
+
binary = binary[1:]
|
|
123
|
+
format = self.get_codec(h)
|
|
124
|
+
else:
|
|
125
|
+
if n > 4:
|
|
117
126
|
binary = self.check_crc(binary)
|
|
118
|
-
|
|
119
|
-
binary = binary[1:]
|
|
120
|
-
format = self.get_codec(h)
|
|
121
|
-
else:
|
|
127
|
+
n = binary.find(b'=')
|
|
122
128
|
if n > 4:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
h = binary[:1]
|
|
130
|
+
binary = binary[1:]
|
|
131
|
+
format = self.get_codec(h)
|
|
132
|
+
return format, binary
|
|
133
|
+
h = binary[0:n]
|
|
134
|
+
binary = binary[n + 1:]
|
|
135
|
+
format = self.get_format(h)
|
|
136
|
+
return format, binary
|
|
137
|
+
|
|
138
|
+
def decode(self, binary, codec=None):
|
|
139
|
+
if codec is None:
|
|
140
|
+
format, binary = self.get_header(binary)
|
|
128
141
|
else:
|
|
129
142
|
format = self.formats[codec]
|
|
130
143
|
if format.crc and self.crc16:
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"events8b": {"type": "array", "array": {
|
|
46
46
|
"id": {"type": "id", "bytes": 1},
|
|
47
47
|
"value": {"type": "unsigned", "bytes": 8}
|
|
48
|
-
}}
|
|
49
|
-
|
|
50
|
-
}
|
|
48
|
+
}}
|
|
49
|
+
}},
|
|
50
|
+
"#reports": {"type": "unsigned", "bytes": 1}
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"status3": {
|
|
@@ -563,7 +563,6 @@ report:
|
|
|
563
563
|
header, recv = server.decode(binary)
|
|
564
564
|
self.assertEqual(recv, data)
|
|
565
565
|
|
|
566
|
-
|
|
567
566
|
def test_teltonika_position_hex(self):
|
|
568
567
|
hexdata = '000000000000003608010000016B40D8EA30010000000000000000000000000000000105021503010101425E0F01F10000601A014E0000000000000000010000C7CF'
|
|
569
568
|
binary = bytes.fromhex(hexdata)
|
|
@@ -571,31 +570,32 @@ report:
|
|
|
571
570
|
header, recv = client.decode(binary)
|
|
572
571
|
self.assertEqual(recv, {
|
|
573
572
|
'positions': [{
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
573
|
+
'time': datetime.datetime(2019, 6, 10, 5, 4, 46),
|
|
574
|
+
'priority': 1,
|
|
575
|
+
'lng': 0.0,
|
|
576
|
+
'lat': 0.0,
|
|
577
|
+
'alt': 0,
|
|
578
|
+
'angle': 0,
|
|
579
|
+
'satellites': 0,
|
|
580
|
+
'speed': 0,
|
|
581
|
+
'event_io': 1,
|
|
582
|
+
'#events': 5,
|
|
583
|
+
'events1b': [
|
|
584
|
+
{'id': 21, 'value': 3},
|
|
585
|
+
{'id': 1, 'value': 1}
|
|
586
|
+
],
|
|
587
|
+
'events2b': [
|
|
588
|
+
{'id': 66, 'value': 24079}
|
|
589
|
+
],
|
|
590
|
+
'events4b': [
|
|
591
|
+
{'id': 241, 'value': 24602}
|
|
592
|
+
],
|
|
593
|
+
'events8b': [
|
|
594
|
+
{'id': 78, 'value': 0}
|
|
595
|
+
]}
|
|
596
596
|
],
|
|
597
|
-
'#reports': 1
|
|
598
|
-
}
|
|
597
|
+
'#reports': 1
|
|
598
|
+
})
|
|
599
599
|
|
|
600
600
|
def test_login_keyerror(self):
|
|
601
601
|
client = Protocol(file='demo.json', server=False)
|
|
@@ -612,7 +612,7 @@ report:
|
|
|
612
612
|
self.assertEqual(data, recv)
|
|
613
613
|
|
|
614
614
|
def test_crc(self):
|
|
615
|
-
data = {'positions': [{'time': datetime.datetime(2024, 10, 19, 9, 37, 57, 555000), 'priority': 1, 'lng': -77.0155334, 'lat': -12.0613651, 'alt': 0, 'angle': 0, 'satellites': 3, 'speed': 105, 'event_io': 0, '#events': 0, 'events1b': [], 'events2b': [], 'events4b': [], 'events8b': [], '#reports': 1}
|
|
615
|
+
data = {'positions': [{'time': datetime.datetime(2024, 10, 19, 9, 37, 57, 555000), 'priority': 1, 'lng': -77.0155334, 'lat': -12.0613651, 'alt': 0, 'angle': 0, 'satellites': 3, 'speed': 105, 'event_io': 0, '#events': 0, 'events1b': [], 'events2b': [], 'events4b': [], 'events8b': []}], '#reports': 1}
|
|
616
616
|
client = Protocol(file='codec8.json')
|
|
617
617
|
binary = client.encode(data, 'report')
|
|
618
618
|
self.assertEqual(b'\x00\x00\x00\x00\x00\x00\x00!\x08\x01\x00\x00\x01\x92\xa56\xaf\xb3\x01\xd2\x18\\\xba\xf8\xcf\x94\xed\x00\x00\x00\x00\x03\x00i\x00\x00\x00\x00\x00\x00\x01\x00\x00\xa3\xe4', binary)
|
|
@@ -635,3 +635,10 @@ report:
|
|
|
635
635
|
data = {'positions': 1}
|
|
636
636
|
binary = protocol.encode(data, 'report_ack')
|
|
637
637
|
self.assertEqual(binary, b'\x00\x00\x00\x01')
|
|
638
|
+
|
|
639
|
+
def test_decode_codec8(self):
|
|
640
|
+
# binary = b'\xfe\x0f350424068858891\x00\x00\x01\x06CBF541\x04\x00\x02R\x00\x00\x00\x00\x01\x00\x00\x1e\n\x04\x00)\x11$\x100\x00R\xff\x00\x00\x00\x02'
|
|
641
|
+
binary = b'\x00\x00\x00\x00\x00\x00\x01\x11\x08\t\x00\x00\x01\x93x\xd74\xf0\x00\xd2\x15\x19\xe6\xf8\xc6\xe8\x9e\x00Z\x01b\x0e\x00\r\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93x\xd7_\xe8\x00\xd2\x15\x1b#\xf8\xc6\xfau\x00W\x00\x06\x0f\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93x\xd7\x86\xf8\x00\xd2\x15\x19\xf7\xf8\xc7\x0c~\x00S\x01e\x10\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93x\xd7\xd5\x18\x00\xd2\x15\x19\xf7\xf8\xc7\x14=\x00U\x00\x02\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93x\xd8#8\x00\xd2\x15\x19\xf7\xf8\xc7\x14=\x00U\x00\x02\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93x\xd8qX\x00\xd2\x15\x19\xf7\xf8\xc7\x14=\x00U\x00\x02\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93x\xd8\xb3\xc0\x00\xd2\x15\x1bU\xf8\xc7%\xe2\x00R\x01g\x10\x00\x19\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93x\xd8\xcf\x18\x00\xd2\x15\x1a\xe0\xf8\xc79\x8b\x00T\x01g\x0f\x00\x17\x00\x00\x00\x00\x00\x00\x00\x00\x01\x93x\xd8\xeeX\x00\xd2\x15\x1al\xf8\xc7K\x1f\x00Y\x00\x01\x0e\x00\x17\x00\x00\x00\x00\x00\x00\t\x00\x00\x13x'
|
|
642
|
+
client = Protocol(file='codec8.json')
|
|
643
|
+
header, recv = client.decode(binary)
|
|
644
|
+
print(header, recv)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|