protobin 0.0.7__tar.gz → 0.0.8__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.3
2
2
  Name: protobin
3
- Version: 0.0.7
3
+ Version: 0.0.8
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
@@ -33,6 +33,7 @@ Classifier: License :: OSI Approved :: MIT License
33
33
  Classifier: Operating System :: OS Independent
34
34
  Classifier: Programming Language :: Python :: 3
35
35
  Requires-Python: >=3.9
36
+ Requires-Dist: pyyaml~=6.0
36
37
  Description-Content-Type: text/markdown
37
38
 
38
39
  # protobin
@@ -13,6 +13,7 @@ class FieldEnum(enum.StrEnum):
13
13
  CHAR = 'char'
14
14
  DATE = 'date'
15
15
  DATETIME = 'datetime'
16
+ ID = 'id'
16
17
  FLAGS = 'flags'
17
18
  FLOAT = 'float'
18
19
  SIGNED = 'signed'
@@ -77,12 +78,6 @@ class FieldBase:
77
78
  return binary[:self.bytes], binary[self.bytes:]
78
79
  return binary[1:binary[0] + 1], binary[binary[0] + 1:]
79
80
 
80
- def to_dict(self):
81
- return {
82
- 'key': self.key,
83
- 'bytes': self.bytes,
84
- 'type': self.type
85
- }
86
81
 
87
82
 
88
83
  class FieldArray(FieldBase):
@@ -98,11 +93,6 @@ class FieldArray(FieldBase):
98
93
  val, binary = self.from_binary(binary)
99
94
  return val, binary
100
95
 
101
- def to_dict(self):
102
- d = super().to_dict()
103
- d['array'] = self.array
104
- return d
105
-
106
96
  def to_binary(self, val):
107
97
  binary = b''
108
98
  length = len(val)
@@ -304,10 +294,23 @@ class FieldFloat(FieldBase):
304
294
  def to_binary(self, val):
305
295
  return int(round(val * (10 ** self.decimals))).to_bytes(self.bytes, 'big', signed=True)
306
296
 
307
- def to_dict(self):
308
- d = super().to_dict()
309
- d['decimals'] = self.decimals
310
- return d
297
+
298
+ class FieldId(FieldBase):
299
+
300
+ def to_binary(self, val):
301
+ if val is None:
302
+ val = 0
303
+ max_value = 256 ** self.bytes - 1
304
+ val = min(val, max_value)
305
+ if val < 0:
306
+ raise ValueError('Negative values not allowed')
307
+ return int(val).to_bytes(self.bytes, 'big', signed=False)
308
+
309
+ def from_binary(self, binary):
310
+ val = int.from_bytes(binary[:self.bytes], 'big', signed=False)
311
+ if val == 0:
312
+ val = None
313
+ return val
311
314
 
312
315
 
313
316
  class FieldSigned(FieldBase):
@@ -380,11 +383,11 @@ class FieldUnsigned(FieldBase):
380
383
 
381
384
  def to_binary(self, val):
382
385
  if val is None:
383
- val = 0
386
+ raise ValueError(f'Error en el campo "{self.key}" no se permite None')
384
387
  max_value = 256 ** self.bytes - 1
385
388
  val = min(val, max_value)
386
389
  if val < 0:
387
- raise ValueError('Negative values not allowed')
390
+ raise ValueError(f'Error en el campo "{self.key}" se espera enteros positivos y se recibió {val}')
388
391
  return int(val).to_bytes(self.bytes, 'big', signed=False)
389
392
 
390
393
  def from_binary(self, binary):
@@ -400,6 +403,7 @@ FIELD_MAP = {
400
403
  'datetime': FieldDateTime,
401
404
  'flags': FieldFlags,
402
405
  'float': FieldFloat,
406
+ 'id': FieldId,
403
407
  'signed': FieldSigned,
404
408
  'string': FieldString,
405
409
  'time': FieldTime,
File without changes
@@ -1,29 +1,38 @@
1
1
  import json
2
-
3
2
  import yaml
4
-
5
3
  from protobin.fields import FieldBase, FIELD_MAP
6
4
 
7
5
 
8
6
  class Format:
9
7
 
10
- fields: [FieldBase]
8
+ input_fields: [FieldBase]
9
+ output_fields: [FieldBase]
11
10
 
12
- def __init__(self, format: [object]):
13
- self.fields = []
11
+ def __init__(self, name: str, format, server: bool):
12
+ self.name = name
13
+ self.input_fields = []
14
+ self.output_fields = []
14
15
  self.header = format['header']
15
- for k, f in format['fields'].items():
16
- self.fields.append(FIELD_MAP[f['type']](k, f))
16
+ if server is None:
17
+ input_mode = 'fields'
18
+ output_mode = 'fields'
19
+ else:
20
+ input_mode = 'device' if server else 'server'
21
+ output_mode = 'server' if server else 'device'
22
+ for k, f in format[input_mode].items():
23
+ self.input_fields.append(FIELD_MAP[f['type']](k, f))
24
+ for k, f in format[output_mode].items():
25
+ self.output_fields.append(FIELD_MAP[f['type']](k, f))
17
26
 
18
27
  def encode(self, data):
19
28
  binary = self.header.encode('utf') + b'='
20
- for f in self.fields:
29
+ for f in self.output_fields:
21
30
  binary += f.encode(data)
22
31
  return binary
23
32
 
24
33
  def decode(self, binary):
25
34
  data = {}
26
- for f in self.fields:
35
+ for f in self.input_fields:
27
36
  val, binary = f.decode(binary)
28
37
  if isinstance(f.key, list):
29
38
  for k in f.key:
@@ -32,13 +41,11 @@ class Format:
32
41
  data[f.key] = val
33
42
  return data
34
43
 
35
- def to_dict(self):
36
- return [f.to_dict() for f in self.fields]
37
-
38
44
 
39
45
  class Protocol:
40
46
 
41
- def __init__(self, file=None, js=None):
47
+ def __init__(self, server: bool = None, file=None, js=None):
48
+ self.server = server
42
49
  self.headers = {}
43
50
  if file:
44
51
  with open(file, 'r') as f:
@@ -54,7 +61,7 @@ class Protocol:
54
61
  def load_format(self, js):
55
62
  self.formats = {}
56
63
  for k in js.keys():
57
- self.formats[k] = Format(js[k])
64
+ self.formats[k] = Format(name=k, format=js[k], server=self.server)
58
65
  self.headers[js[k]['header']] = k
59
66
 
60
67
  def get_format(self, h):
@@ -66,4 +73,4 @@ class Protocol:
66
73
  def decode(self, binary):
67
74
  h, binary = binary.split(b'=')
68
75
  format = self.get_format(h)
69
- return h.decode('utf'), format.decode(binary)
76
+ return format.name, format.decode(binary)
@@ -4,10 +4,13 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "protobin"
7
- version = "0.0.7"
7
+ version = "0.0.8"
8
8
  authors = [
9
9
  { name="Daniel Robles", email="drm.electronic@gmail.com" },
10
10
  ]
11
+ dependencies = [
12
+ 'PyYAML ~= 6.0'
13
+ ]
11
14
  description = "Python library for encode and decode data in protobin format"
12
15
  readme = "README.md"
13
16
  license = { file="LICENSE.txt" }
@@ -1,20 +1,30 @@
1
1
  {
2
2
  "login": {
3
3
  "header": "T",
4
- "fields": {
4
+ "device": {
5
5
  "serial": {"bytes": 0, "type": "string"}
6
- }
6
+ },
7
+ "server": {
8
+ "id": {"bytes": 2, "type": "id"},
9
+ "bus_number": {"bytes": 2, "type": "id"},
10
+ "route": {"bytes": 1, "type": "id"},
11
+ "direction": {"bytes": 1, "type": "bool"},
12
+ "geofence": {"bytes": 2, "type": "id"},
13
+ "state": {"bytes": 1, "type": "char"},
14
+ "trip": {"bytes": 4, "type": "id"},
15
+ "driver": {"bytes": 4, "type": "id"}
16
+ }
7
17
  },
8
18
  "report": {
9
19
  "header": "P",
10
- "fields": {
20
+ "device": {
11
21
  "positions": {"bytes": 19, "type": "array", "array": {
12
22
  "time": {"bytes": 6, "type": "datetime"},
13
23
  "lng": {"bytes": 4, "type": "float", "decimals": 6},
14
24
  "lat": {"bytes": 4, "type": "float", "decimals": 6},
15
25
  "speed": {"bytes": 1, "type": "unsigned"},
16
- "mark": {"bytes": 2, "type": "unsigned"},
17
- "busstop": {"bytes": 2, "type": "unsigned"}}
26
+ "mark": {"bytes": 2, "type": "id"},
27
+ "busstop": {"bytes": 2, "type": "id"}}
18
28
  },
19
29
  "trip": {"bytes": 4, "type": "unsigned"},
20
30
  "route": {"bytes": 1, "type": "unsigned"},
@@ -25,6 +35,9 @@
25
35
  "id": {"bytes": 2, "type": "unsigned"},
26
36
  "value": {"bytes": 1, "type": "unsigned"}}
27
37
  }
38
+ },
39
+ "server": {
40
+ "positions": {"bytes": 1, "type": "unsigned"}
28
41
  }
29
42
  }
30
43
  }
@@ -12,7 +12,7 @@ DATA = {
12
12
  'lng': -11.485014,
13
13
  'lat': -77.621845,
14
14
  'speed': 20,
15
- 'mark': 0,
15
+ 'mark': None,
16
16
  'busstop': 16
17
17
  },
18
18
  {
@@ -20,7 +20,7 @@ DATA = {
20
20
  'lng': -11.485414,
21
21
  'lat': -77.622245,
22
22
  'speed': 25,
23
- 'mark': 0,
23
+ 'mark': None,
24
24
  'busstop': 16
25
25
  },
26
26
  {
@@ -28,7 +28,7 @@ DATA = {
28
28
  'lng': -11.485514,
29
29
  'lat': -77.621945,
30
30
  'speed': 18,
31
- 'mark': 0,
31
+ 'mark': None,
32
32
  'busstop': 16
33
33
  },
34
34
  {
@@ -228,6 +228,7 @@ class BasicTest(unittest.TestCase):
228
228
  def test_float(self):
229
229
  data = {'test': -11.659812}
230
230
  binary = self.protocol.encode(data, 'float')
231
+ print(binary)
231
232
  h, recv = self.protocol.decode(binary)
232
233
  self.assertEqual(data, recv)
233
234
 
@@ -263,11 +264,12 @@ class BasicTest(unittest.TestCase):
263
264
  class AdvancedTest(unittest.TestCase):
264
265
 
265
266
  def test_file_json(self):
266
- protocol = Protocol(file='demo.json')
267
- binary = protocol.encode(DATA, 'report')
268
- h, recv = protocol.decode(binary)
267
+ device = Protocol(file='demo.json', server=False)
268
+ binary = device.encode(DATA, 'report')
269
+ server = Protocol(file='demo.json', server=True)
270
+ name, recv = server.decode(binary)
269
271
  self.assertEqual(DATA, recv)
270
- self.assertEqual(h, 'P')
272
+ self.assertEqual(name, 'report')
271
273
 
272
274
  # def test_file_yaml(self):
273
275
  # protocol = Protocol(file='demo.yaml')
@@ -347,12 +349,27 @@ class AdvancedTest(unittest.TestCase):
347
349
  js = json.dumps(ym, indent=2)
348
350
 
349
351
  def test_convert_json_to_yaml(self):
350
- protocol = Protocol(file='demo.json')
352
+ protocol = Protocol(file='demo.json', server=True)
351
353
 
352
354
  def test_login(self):
353
- protocol = Protocol(file='demo.json')
355
+ device = Protocol(file='demo.json', server=False)
356
+ server = Protocol(file='demo.json', server=True)
354
357
  data = {'serial': '15984316545'}
355
- binary = protocol.encode(data, 'login')
356
- header, recv = protocol.decode(binary)
358
+ binary = device.encode(data, 'login')
359
+ header, recv = server.decode(binary)
360
+ self.assertEqual(header, 'login')
361
+ self.assertEqual(data, recv)
362
+ data = {
363
+ 'id': 43,
364
+ 'bus_number': 18,
365
+ 'route': 2,
366
+ 'direction': True,
367
+ 'geofence': None,
368
+ 'state': 'R',
369
+ 'trip': 5681121,
370
+ 'driver': 1432
371
+ }
372
+ binary = server.encode(data, 'login')
373
+ header, recv = device.decode(binary)
374
+ self.assertEqual(header, 'login')
357
375
  self.assertEqual(data, recv)
358
- self.assertEqual(header, 'T')
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes