protobin 0.0.47__tar.gz → 0.0.49__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: protobin
3
- Version: 0.0.47
3
+ Version: 0.0.49
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
@@ -327,7 +327,10 @@ class DateTimeField(FieldBase):
327
327
  S, binary = self.get_nible(binary)
328
328
  if y == 0 and m == 0 and d == 0 and H == 0 and M == 0 and S == 0:
329
329
  return None
330
- return datetime.datetime(2000 + y, m, d, H, M, S)
330
+ try:
331
+ return datetime.datetime(2000 + y, m, d, H, M, S)
332
+ except:
333
+ return None
331
334
 
332
335
 
333
336
  class FlagsField(FieldBase):
@@ -502,7 +505,12 @@ class TimestampField(FieldBase):
502
505
  def __init__(self, k, js):
503
506
  super().__init__(k, js)
504
507
  self.bytes = js.get('bytes', 6)
505
- self.decimals = js.get('decimals', 6)
508
+ if self.bytes == 6:
509
+ self.decimals = 3
510
+ elif self.bytes == 8:
511
+ self.decimals = 6
512
+ else:
513
+ raise ValueError(f'Error in field TimestampField<{self.key}>, bytes must be 6 or 8 but "{self.bytes}" is received')
506
514
 
507
515
  def __repr__(self):
508
516
  return f'TimestampField<key: {self.key}>'
@@ -123,6 +123,8 @@ class Protocol:
123
123
  return binary
124
124
 
125
125
  def get_header(self, binary):
126
+ if binary[0] == 0:
127
+ binary = self.check_crc(binary)
126
128
  n = binary.find(b'=')
127
129
  if n == -1:
128
130
  # posiciones sin =, calcular la longitud de la trama con sus primero bytes
@@ -168,6 +170,7 @@ class Protocol:
168
170
  length = int.from_bytes(binary[:self.length], 'big', signed=False)
169
171
  clean_binary = binary[self.length:length + self.length]
170
172
  crc = binary[length + self.length: length + self.length + self.crc_size]
173
+ print('length', binary[:self.length], 'binary', clean_binary, 'crc', crc)
171
174
  crc_value = int.from_bytes(crc, self.crc_byteorder, signed=False)
172
175
  if not crc:
173
176
  raise CRCError(f'There is no CRC')
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "protobin"
7
- version = "0.0.47"
7
+ version = "0.0.49"
8
8
  authors = [
9
9
  { name="Daniel Robles", email="drm.electronic@gmail.com" },
10
10
  ]
@@ -0,0 +1,222 @@
1
+ {
2
+ "length": 2,
3
+ "fake_prefix": true,
4
+ "crc": {"poly": "0x18005", "init": "0x0000", "reverse": true, "byte_order": "big", "size": 2},
5
+ "formats": {
6
+ "login": {
7
+ "header": "T",
8
+ "client": {
9
+ "serial": {"type": "string"}
10
+ },
11
+ "server": {
12
+ "id": {"bytes": 2, "type": "id"},
13
+ "bus_number": {"bytes": 2, "type": "id"},
14
+ "route": {"bytes": 1, "type": "id"},
15
+ "direction": {"bytes": 1, "type": "bool"},
16
+ "geofence": {"bytes": 2, "type": "id"},
17
+ "state": {"bytes": 1, "type": "char"},
18
+ "trip": {"bytes": 4, "type": "id"},
19
+ "driver": {"bytes": 4, "type": "id"},
20
+ "order": {"bytes": 1, "type": "unsigned"},
21
+ "logged": {"type": "bool"},
22
+ "data": {"type": "datetime"},
23
+ "schedule": {"type": "time"}
24
+ }
25
+ },
26
+ "report_alt": {
27
+ "header": "p",
28
+ "client": {
29
+ "positions": {
30
+ "type": "array", "array": {
31
+ "time": {"type": "datetime"},
32
+ "lng": {"bytes": 4, "type": "float", "decimals": 6},
33
+ "lat": {"bytes": 4, "type": "float", "decimals": 6},
34
+ "speed": {"bytes": 1, "type": "unsigned"},
35
+ "mark": {"bytes": 2, "type": "id"},
36
+ "busstop": {"bytes": 2, "type": "id"}
37
+ }
38
+ },
39
+ "trip": {"bytes": 4, "type": "id"},
40
+ "route": {"bytes": 1, "type": "id"},
41
+ "direction": {"type": "bool"},
42
+ "state": {"type": "char"},
43
+ "boletos": {
44
+ "type": "array", "array": {
45
+ "id": { "bytes": 2, "type": "unsigned"},
46
+ "cantidad": {"bytes": 2, "type": "unsigned"}
47
+ }
48
+ },
49
+ "sales": {"bytes": 3, "type": "unsigned"},
50
+ "events": {"type": "array", "array": {
51
+ "id": {"bytes": 2, "type": "unsigned"},
52
+ "value": {"bytes": 1, "type": "unsigned"}}
53
+ }
54
+ },
55
+ "server": {
56
+ "positions": {"bytes": 1, "type": "unsigned"}
57
+ }
58
+ },
59
+ "report": {
60
+ "header": "PV",
61
+ "client": {
62
+ "positions": {"type": "array", "array": {
63
+ "time": {"type": "datetime"},
64
+ "lng": {"bytes": 4,"type": "float", "decimals": 6},
65
+ "lat": {"bytes": 4, "type": "float", "decimals": 6},
66
+ "speed": {"bytes": 1, "type": "unsigned"},
67
+ "mark": {"bytes": 2, "type": "id"},
68
+ "busstop": {"bytes": 2, "type": "id"}
69
+ }
70
+ },
71
+ "trip": {"bytes": 4, "type": "id"},
72
+ "route": {"bytes": 1, "type": "id"},
73
+ "direction": {"type": "bool"},
74
+ "state": {"type": "char"},
75
+ "sales": {"bytes": 3, "type": "unsigned"},
76
+ "events": {"type": "array", "array": {
77
+ "id": {"bytes": 2, "type": "unsigned"},
78
+ "value": {"bytes": 1, "type": "unsigned"}
79
+ }}
80
+ },
81
+ "server": {
82
+ "positions": {"bytes": 1, "type": "unsigned"}
83
+ }
84
+ },
85
+
86
+ "salida": {
87
+ "header": "S",
88
+ "client": {
89
+ "salida": {"bytes": 4, "type": "unsigned"}
90
+ },
91
+ "server": {
92
+ "id": {"bytes": 4, "type": "id"},
93
+ "ruta": {"bytes": 1, "type": "id"},
94
+ "lado": {"bytes": 1, "type": "bool"},
95
+ "inicio" : {"bytes": 6, "type": "datetime"},
96
+ "estado" : { "bytes": 1, "type": "char"},
97
+ "frecuencia" : { "bytes": 1, "type": "unsigned"},
98
+ "conductor" : { "bytes": 4, "type": "id"},
99
+ "geocerca" : { "bytes": 2, "type": "id"},
100
+ "marcados": {"bytes": 0, "type": "bits"},
101
+ "controles": {"type": "array", "array": {
102
+ "geocerca": {"bytes": 2, "type": "unsigned"},
103
+ "tiempo": {"bytes": 2, "type": "signed"},
104
+ "volada": {"bytes": 2, "type": "signed"}
105
+ }
106
+ }
107
+ }
108
+ },
109
+ "comando": {
110
+ "header": "$",
111
+ "client": {
112
+ "comando": {
113
+ "type": "char"
114
+ },
115
+ "id": {
116
+ "type": "string"
117
+ },
118
+ "value": {
119
+ "type": "string"
120
+ }
121
+ },
122
+ "server": {
123
+ "comando": {"type": "char"},
124
+ "id": {"type": "string"},
125
+ "value": {"type": "string"}
126
+ }
127
+ },
128
+ "timestamp": {
129
+ "header": "#T",
130
+ "server": {
131
+ "faltantes": {"bytes": 4, "type": "unsigned"},
132
+ "tabla": {"bytes": 1, "type": "char"}
133
+ },
134
+ "client": {
135
+ "timestamp":{"bytes": 6, "decimals": 3, "type": "timestamp"}
136
+ }
137
+ },
138
+ "driver_login": {
139
+ "header": "LI",
140
+ "client": {
141
+ "key": {"type": "string"},
142
+ "dni": {"type": "string"},
143
+ "direction": {"bytes": 1, "type": "bool"},
144
+ "route": {"bytes": 1, "type": "unsigned"},
145
+ "vehicle": {"bytes": 2, "type": "unsigned"}
146
+ },
147
+ "server": {
148
+ "error": {"type": "bool"},
149
+ "title": {"type": "string"},
150
+ "message": {"type": "string"},
151
+ "sessions": {"type": "array", "array": {
152
+ "id": {"bytes": 4, "type": "id"},
153
+ "driver_id": {"bytes": 2, "type": "id"},
154
+ "driver_code": {"type": "string"},
155
+ "activo,castigado,conductor,vencido": {"type": "flags"},
156
+ "route": {"bytes": 1, "type": "unsigned"},
157
+ "direction": {"type": "bool"},
158
+ "start_time": {"bytes": 6, "type": "datetime"}
159
+ }}
160
+
161
+ }
162
+ },
163
+ "driver_logout": {
164
+ "header": "LO",
165
+ "client": {
166
+ "session": {"bytes": 4, "type": "unsigned"},
167
+ "vehicle": {"bytes": 2, "type": "unsigned"},
168
+ "end_time": {"bytes": 6, "type": "datetime"},
169
+ "production": {"bytes": 4, "type": "unsigned"},
170
+ "first": {"bytes": 4, "type": "unsigned"},
171
+ "last": {"bytes": 4, "type": "unsigned"},
172
+ "resume": {"type":"array", "array": {
173
+ "fare": {"bytes":2, "type": "unsigned"},
174
+ "start": {"bytes": 2, "type": "unsigned"},
175
+ "end": {"bytes": 2, "type": "unsigned"},
176
+ "deleted": {"type": "array", "array": {
177
+ "number": {"bytes":4, "type": "unsigned"}
178
+ }},
179
+ "cash": {"bytes":2, "type": "unsigned"},
180
+ "turuta": {"bytes":2, "type": "unsigned"},
181
+ "niubiz": {"bytes":2, "type": "unsigned"},
182
+ "tutarjeta": {"bytes":2, "type": "unsigned"}
183
+ }}
184
+ },
185
+ "server": {
186
+ "error": {"type": "bool"},
187
+ "title": {"type": "string"},
188
+ "message": {"type": "string"},
189
+ "id": {"bytes": 4, "type": "unsigned"}
190
+ }
191
+ },
192
+
193
+ "tickets": {
194
+ "header": "V",
195
+ "client": {
196
+ "tickets": {"type": "array", "array": {
197
+ "fare": {"bytes": 2, "type": "id"},
198
+ "driver": {"bytes": 2, "type": "id"},
199
+ "correlative": {"bytes": 4, "type": "unsigned"},
200
+ "day": {"bytes": 3, "type": "date"},
201
+ "busstop_end": {"bytes": 2, "type": "id"},
202
+ "time": {"bytes": 6, "type": "datetime"},
203
+ "busstop_start": {"bytes": 4, "type": "id"},
204
+ "inspector": {"bytes": 2, "type": "id"},
205
+ "payment_method": {"bytes": 1, "type": "unsigned"},
206
+ "number": {"bytes": 4, "type": "unsigned"},
207
+ "price": {"bytes": 2, "type": "unsigned"},
208
+ "route": {"bytes": 1, "type": "id"},
209
+ "session": {"bytes": 4, "type": "id"},
210
+ "vehicle": {"bytes": 2, "type": "id"},
211
+ "card": {"type": "string"}
212
+ }}
213
+ },
214
+ "server": {
215
+ "tickets": {"type": "array", "array": {
216
+ "correlative": {"bytes": 4, "type": "unsigned"},
217
+ "id": {"type": "string"}
218
+ }}
219
+ }
220
+ }
221
+ }
222
+ }
@@ -140,7 +140,11 @@ class BasicTest(unittest.TestCase):
140
140
  },
141
141
  'timestamp6': {
142
142
  "header": "T6",
143
- "fields": {'test': {'bytes': 6, "decimals": 3, 'type': 'timestamp'}}
143
+ "fields": {'test': {'bytes': 6, 'type': 'timestamp'}}
144
+ },
145
+ 'timestamp_default': {
146
+ "header": "TD",
147
+ "fields": {'test': {'type': 'timestamp'}}
144
148
  },
145
149
  'unsigned': {
146
150
  "header": "u",
@@ -185,6 +189,10 @@ class BasicTest(unittest.TestCase):
185
189
  binary = self.protocol.encode(data, 'bits')
186
190
  h, recv = self.protocol.decode(binary)
187
191
  self.assertEqual(data, recv)
192
+ data = {'test': [True, True, True, True, True, True, True, True, True, True, True, True, True, True, False, False, False, False]}
193
+ binary = self.protocol.encode(data, 'bits')
194
+ h, recv = self.protocol.decode(binary)
195
+ self.assertEqual(data, recv)
188
196
 
189
197
  def test_binary(self):
190
198
  data = {'test': b'S=RA\x05'}
@@ -451,6 +459,7 @@ class BasicTest(unittest.TestCase):
451
459
  def test_timestamp(self):
452
460
  data = {'test': datetime.datetime(2024, 9, 13, 15, 23, 51, 265981)}
453
461
  binary = self.protocol.encode(data, 'timestamp')
462
+ print(binary)
454
463
  h, recv = self.protocol.decode(binary)
455
464
  self.assertEqual(data, recv)
456
465
 
@@ -467,6 +476,14 @@ class BasicTest(unittest.TestCase):
467
476
  h, recv = self.protocol.decode(binary)
468
477
  self.assertEqual(data, recv)
469
478
 
479
+ def test_timestamp_default(self):
480
+ data = {'test': datetime.datetime.now()}
481
+ print(data)
482
+ binary = self.protocol.encode(data, 'timestamp_default')
483
+ print(binary)
484
+ h, recv = self.protocol.decode(binary)
485
+ print(recv)
486
+ self.assertEqual(data['test'].replace(microsecond=(data['test'].microsecond // 1000) * 1000), recv['test'])
470
487
 
471
488
  class AdvancedTest(unittest.TestCase):
472
489
 
@@ -495,6 +512,19 @@ class AdvancedTest(unittest.TestCase):
495
512
  h, recv = protocol.decode(binary)
496
513
  self.assertEqual(data, recv)
497
514
 
515
+ def test_decode_login_client(self):
516
+ client = Protocol(file='demo.json', server=False)
517
+ server = Protocol(file='demo.json', server=True)
518
+ binary = b'LI=\x03123\x0810707788\x00\x03\x00\x01'
519
+ name, recv = server.decode(binary)
520
+ print(name, recv)
521
+ binary = b'V=\x01\x00\x00\x01@\x1868b5aeac6fe42cc2f509f589'
522
+ name, recv = client.decode(binary)
523
+ print(name, recv)
524
+ binary = b'LI=\x00\x10Acceso permitido\x1eSe ha creado una nueva sesi\xc3\xb3n\x01\x00\x00\x02\x94\x00\x12\x04ID18\n\x03\x00\x01\t%\t)\x01'
525
+ name, recv = client.decode(binary)
526
+ print(name, recv)
527
+
498
528
  def test_new_yaml(self):
499
529
  ym = yaml.full_load("""
500
530
  login:
@@ -629,6 +659,17 @@ report:
629
659
  self.assertEqual(header, 'login')
630
660
  print('recv', recv)
631
661
 
662
+ def test_login_binary_crc(self):
663
+ client = Protocol(file='demo.json', server=True)
664
+ binary = b'\x00\x13T=\x1000000000d4a31550\xfcy'
665
+ header, recv = client.decode(binary)
666
+ self.assertEqual(header, 'login')
667
+ print('recv', recv)
668
+ binary = b'T=\x1000000000d4a31550'
669
+ header, recv = client.decode(binary)
670
+ self.assertEqual(header, 'login')
671
+ print('recv', recv)
672
+
632
673
  def test_file_yaml(self):
633
674
  client = Protocol(file='demo.yaml', server=False)
634
675
  server = Protocol(file='demo.yaml', server=True)
@@ -961,3 +1002,24 @@ report:
961
1002
  binary = client.encode(data, 'flag_array')
962
1003
  h, recv = client.decode(binary)
963
1004
  self.assertEqual(data, recv)
1005
+
1006
+
1007
+
1008
+ def test_report_teltonika_alt(self):
1009
+ server = Protocol(file='demo.json', server=True)
1010
+ binary = b'p=\x03\x14\x07%\x10YP\xfbh.\x18\xffG\xaa\x8c\x18\x00\x16\xee\xfa\x14\x07%\x10YU\xfbh/\x06\xffG\xa9\xc6\x15\x00\x16\xee\xfay\x01\xe7$\xba\x10o}\x00\xf7\xc1m\x10\xc3R\n\xffh\xbb\x00\x04\x06\xf8\x01\x01R\x00\x00\x00\x00\x00'
1011
+ h, recv = server.decode(binary)
1012
+ self.assertEqual(h, 'report_alt')
1013
+ print(recv)
1014
+
1015
+ def test_salida(self):
1016
+ server = Protocol(file='demo.json', server=True)
1017
+ client = Protocol(file='demo.json', server=False)
1018
+ data = {'id': 52723, 'ruta': 1, 'lado': True, 'inicio': datetime.datetime(2025, 7, 22, 16, 40), 'estado': 'R', 'frecuencia': 6, 'conductor': 36, 'geocerca': 14, 'marcados': [True, True, True, True, True, True, True, True, True, True, True, True, True, True, False, False, False, False], 'controles': [{'geocerca': 3, 'tiempo': 0, 'volada': -10}, {'geocerca': 11, 'tiempo': 3, 'volada': -11}, {'geocerca': 28, 'tiempo': 3, 'volada': -4}, {'geocerca': 29, 'tiempo': 6, 'volada': -2}, {'geocerca': 43, 'tiempo': 0, 'volada': -2}, {'geocerca': 30, 'tiempo': 3, 'volada': 2}, {'geocerca': 12, 'tiempo': 3, 'volada': 2}, {'geocerca': 31, 'tiempo': 8, 'volada': 2}, {'geocerca': 32, 'tiempo': 2, 'volada': 3}, {'geocerca': 13, 'tiempo': 2, 'volada': 3}, {'geocerca': 33, 'tiempo': 2, 'volada': 6}, {'geocerca': 34, 'tiempo': 6, 'volada': 4}, {'geocerca': 35, 'tiempo': 4, 'volada': 4}, {'geocerca': 14, 'tiempo': 6, 'volada': 3}, {'geocerca': 36, 'tiempo': 11, 'volada': 0}, {'geocerca': 37, 'tiempo': 11, 'volada': 0}, {'geocerca': 38, 'tiempo': 11, 'volada': 0}, {'geocerca': 4, 'tiempo': 11, 'volada': 0}]}
1019
+ binary = server.encode(data, 'salida')
1020
+ print(binary)
1021
+ self.assertEqual(b'S=\x00\x00\xcd\xf3\x01\x01"\x07%\x16@\x00R\x06\x00\x00\x00$\x00\x0e\x12\xff\xfc\x00\x12\x00\x03\x00\x00\xff\xf6\x00\x0b\x00\x03\xff\xf5\x00\x1c\x00\x03\xff\xfc\x00\x1d\x00\x06\xff\xfe\x00+\x00\x00\xff\xfe\x00\x1e\x00\x03\x00\x02\x00\x0c\x00\x03\x00\x02\x00\x1f\x00\x08\x00\x02\x00 \x00\x02\x00\x03\x00\r\x00\x02\x00\x03\x00!\x00\x02\x00\x06\x00"\x00\x06\x00\x04\x00#\x00\x04\x00\x04\x00\x0e\x00\x06\x00\x03\x00$\x00\x0b\x00\x00\x00%\x00\x0b\x00\x00\x00&\x00\x0b\x00\x00\x00\x04\x00\x0b\x00\x00', binary)
1022
+ h, recv = client.decode(binary)
1023
+ print(recv)
1024
+ self.assertEqual(h, 'salida')
1025
+ self.assertEqual(recv, data)
@@ -1,79 +0,0 @@
1
- {
2
- "formats": {
3
- "login": {
4
- "header": "T",
5
- "client": {
6
- "serial": {"type": "string"}
7
- },
8
- "server": {
9
- "id": {"bytes": 2, "type": "id"},
10
- "bus_number": {"bytes": 2, "type": "id"},
11
- "route": {"bytes": 1, "type": "id"},
12
- "direction": {"bytes": 1, "type": "bool"},
13
- "geofence": {"bytes": 2, "type": "id"},
14
- "state": {"bytes": 1, "type": "char"},
15
- "trip": {"bytes": 4, "type": "id"},
16
- "driver": {"bytes": 4, "type": "id"},
17
- "order": {"bytes": 1, "type": "unsigned"},
18
- "logged": {"type": "bool"},
19
- "data": {"type": "datetime"},
20
- "schedule": {"type": "time"}
21
- }
22
- },
23
- "report": {
24
- "header": "PV",
25
- "client": {
26
- "positions": {"type": "array", "array": {
27
- "time": {"type": "datetime"},
28
- "lng": {"bytes": 4,"type": "float", "decimals": 6},
29
- "lat": {"bytes": 4, "type": "float", "decimals": 6},
30
- "speed": {"bytes": 1, "type": "unsigned"},
31
- "mark": {"bytes": 2, "type": "id"},
32
- "busstop": {"bytes": 2, "type": "id"}
33
- }
34
- },
35
- "trip": {"bytes": 4, "type": "id"},
36
- "route": {"bytes": 1, "type": "id"},
37
- "direction": {"type": "bool"},
38
- "state": {"type": "char"},
39
- "sales": {"bytes": 3, "type": "unsigned"},
40
- "events": {"type": "array", "array": {
41
- "id": {"bytes": 2, "type": "unsigned"},
42
- "value": {"bytes": 1, "type": "unsigned"}
43
- }}
44
- },
45
- "server": {
46
- "positions": {"bytes": 1, "type": "unsigned"}
47
- }
48
- },
49
- "comando": {
50
- "header": "$",
51
- "client": {
52
- "comando": {
53
- "type": "char"
54
- },
55
- "id": {
56
- "type": "string"
57
- },
58
- "value": {
59
- "type": "string"
60
- }
61
- },
62
- "server": {
63
- "comando": {"type": "char"},
64
- "id": {"type": "string"},
65
- "value": {"type": "string"}
66
- }
67
- },
68
- "timestamp": {
69
- "header": "#T",
70
- "server": {
71
- "faltantes": {"bytes": 4, "type": "unsigned"},
72
- "tabla": {"bytes": 1, "type": "char"}
73
- },
74
- "client": {
75
- "timestamp":{"bytes": 6, "decimals": 3, "type": "timestamp"}
76
- }
77
- }
78
- }
79
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes