burst-link-protocol 1.0.5__cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl → 1.1.5__cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl

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.
burst_interface_c.pyi CHANGED
@@ -6,3 +6,21 @@ class BurstInterfaceC:
6
6
  def decode(self, data: bytes, fail_on_crc_error: bool = False) -> list: ...
7
7
 
8
8
  def encode(self, packets: list) -> bytes: ...
9
+
10
+ @property
11
+ def bytes_handled(self) -> int: ...
12
+
13
+ @property
14
+ def bytes_processed(self) -> int: ...
15
+
16
+ @property
17
+ def packets_processed(self) -> int: ...
18
+
19
+ @property
20
+ def crc_errors(self) -> int: ...
21
+
22
+ @property
23
+ def overflow_errors(self) -> int: ...
24
+
25
+ @property
26
+ def decode_errors(self) -> int: ...
@@ -1,4 +1,5 @@
1
- from burst_interface_c import BurstInterfaceC
1
+ from burst_interface_c import BurstInterfaceC
2
2
  from .main import BurstInterfacePy
3
+ from .serial_burst_interface import SerialBurstInterface, BurstSerialStatistics
3
4
 
4
- __all__ = ["BurstInterfaceC", "BurstInterfacePy"]
5
+ __all__ = ["BurstInterfaceC", "BurstInterfacePy", "SerialBurstInterface", "BurstSerialStatistics"]
@@ -1,7 +1,8 @@
1
1
  from cobs import cobs
2
- from crc import Calculator,Crc16
2
+ from crc import Calculator, Crc16
3
+
4
+ crc = Calculator(Crc16.IBM_3740) # type: ignore
3
5
 
4
- crc = Calculator(Crc16.IBM_3740)
5
6
 
6
7
  class BurstInterfacePy:
7
8
  buffer = b""
@@ -10,9 +11,8 @@ class BurstInterfacePy:
10
11
  pass
11
12
 
12
13
  @staticmethod
13
- def crc16( data: bytes) -> bytes:
14
+ def crc16(data: bytes) -> bytes:
14
15
  return crc.checksum(data).to_bytes(2, "big")
15
-
16
16
 
17
17
  @staticmethod
18
18
  def encode_packet(packet: bytes) -> bytes:
@@ -38,4 +38,3 @@ class BurstInterfacePy:
38
38
  # Add last packet to buffer
39
39
  self.buffer = separated_packets.pop()
40
40
  return [self.decode_packet(packet) for packet in separated_packets]
41
-
@@ -0,0 +1,231 @@
1
+ from burst_interface_c import BurstInterfaceC
2
+ import serial
3
+ import time
4
+ import threading
5
+ import asyncio
6
+ import janus
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ def to_si(value: float, suffix: str) -> str:
11
+ """
12
+ Convert a value to a string with SI suffix.
13
+ """
14
+ if value == 0:
15
+ return "0"
16
+ elif value < 1e-3:
17
+ return f"{value:.2f} {suffix}"
18
+ elif value < 1e3:
19
+ return f"{value:.2f} {suffix}"
20
+ elif value < 1e6:
21
+ return f"{value / 1e3:.2f} k{suffix}"
22
+ elif value < 1e9:
23
+ return f"{value / 1e6:.2f} M{suffix}"
24
+ else:
25
+ return f"{value / 1e9:.2f} G{suffix}"
26
+
27
+
28
+ class BurstSerialStatistics(BaseModel):
29
+ last_update_timestamp: float = Field(default_factory=time.time)
30
+
31
+ bytes_handled: int = 0
32
+ bytes_processed: int = 0
33
+ packets_processed: int = 0
34
+ crc_errors: int = 0
35
+ overflow_errors: int = 0
36
+ decode_errors: int = 0
37
+
38
+ handled_bytes_per_second: float = 0.0
39
+ processed_bytes_per_second: float = 0.0
40
+ processed_packets_per_second: float = 0.0
41
+
42
+ def update(
43
+ self,
44
+ bytes_handled,
45
+ bytes_processed,
46
+ packets_processed,
47
+ crc_errors,
48
+ overflow_errors,
49
+ decode_errors,
50
+ ):
51
+ now = time.time()
52
+ if now - self.last_update_timestamp > 1:
53
+ delta_time = now - self.last_update_timestamp
54
+ self.last_update_timestamp = now
55
+
56
+ self.handled_bytes_per_second = (bytes_handled - self.bytes_handled) / delta_time
57
+ self.processed_bytes_per_second = (bytes_processed - self.bytes_processed) / delta_time
58
+ self.processed_packets_per_second = (packets_processed - self.packets_processed) / delta_time
59
+
60
+ self.bytes_handled = bytes_handled
61
+ self.bytes_processed = bytes_processed
62
+ self.packets_processed = packets_processed
63
+ self.crc_errors = crc_errors
64
+ self.overflow_errors = overflow_errors
65
+ self.decode_errors = decode_errors
66
+
67
+ return self
68
+
69
+ def __str__(self):
70
+ return (
71
+ f"Byte Raw: {to_si(self.bytes_handled, 'B')} ({to_si(self.handled_bytes_per_second * 8, 'bps')}), "
72
+ f"Bytes processed: {to_si(self.bytes_processed, 'B')} ({to_si(self.processed_bytes_per_second * 8, 'bps')}), "
73
+ f"Packets processed: {self.packets_processed} ({to_si(self.processed_packets_per_second, 'packets/s')}), "
74
+ f"Errors (CRC: {self.crc_errors}, Overflow: {self.overflow_errors}, Decode: {self.decode_errors})"
75
+ )
76
+
77
+ def to_dict(self):
78
+ return self.model_dump(exclude={"last_update_timestamp"})
79
+
80
+
81
+ class SerialBurstInterface:
82
+ debug_timings = False
83
+ debug_io = False
84
+
85
+ kill = False
86
+ block_size = 1000
87
+ RATE_CHECK_INTERVAL = 1
88
+
89
+ interface: BurstInterfaceC
90
+ last_rate_timestamp: float = 0
91
+
92
+ statitsics: BurstSerialStatistics
93
+
94
+ @classmethod
95
+ def from_serial(cls, port: str, bitrate: int):
96
+ serial_handle: serial.Serial = serial.Serial(port, bitrate, timeout=0.5)
97
+ serial_handle.set_buffer_size(rx_size=100 * 1024, tx_size=100 * 1024) # type: ignore
98
+ return cls(serial_handle)
99
+
100
+ def __init__(self, serial_handle: serial.Serial):
101
+ self.handle = serial_handle
102
+ self.handle.reset_input_buffer()
103
+ self.handle.reset_output_buffer()
104
+
105
+ self.current_stats = BurstSerialStatistics()
106
+ self.statitsics = BurstSerialStatistics()
107
+
108
+ self.receive_task_handle = threading.Thread(target=self.receive_task, daemon=True)
109
+ self.transmit_task_handle = threading.Thread(target=self.transmit_task, daemon=True)
110
+
111
+ self.interface = BurstInterfaceC()
112
+ self.transmit_packet_queue = janus.Queue()
113
+ self.receive_packet_queue = janus.Queue()
114
+
115
+ self.receive_task_handle.start()
116
+ self.transmit_task_handle.start()
117
+
118
+ @property
119
+ def statistics(self):
120
+ return self.current_stats.update(
121
+ self.interface.bytes_handled,
122
+ self.interface.bytes_processed,
123
+ self.interface.packets_processed,
124
+ self.interface.crc_errors,
125
+ self.interface.overflow_errors,
126
+ self.interface.decode_errors,
127
+ )
128
+
129
+ def close(self):
130
+ self.kill = True
131
+ self.handle.close()
132
+ self.transmit_packet_queue.close()
133
+ self.receive_packet_queue.close()
134
+
135
+ def receive_task(self):
136
+ try:
137
+ while True:
138
+ # Read incoming data
139
+ data = self.handle.read(self.block_size)
140
+
141
+ if self.kill:
142
+ break
143
+
144
+ if data:
145
+ if self.debug_io:
146
+ print(f"Received burst frame: {' '.join([f'{x:02X}' for x in data])}, length: {len(data)}")
147
+ try:
148
+ decoded_packets = self.interface.decode(data, fail_on_crc_error=True)
149
+ except Exception as e:
150
+ print(f"Error decoding: {e}")
151
+ continue
152
+
153
+ for packet in decoded_packets:
154
+ # put all packets in the receive queue
155
+ if self.debug_io:
156
+ print(f"Received: {packet}")
157
+
158
+ self.receive_packet_queue.sync_q.put(packet)
159
+
160
+ time.sleep(0.001)
161
+
162
+ except Exception as e:
163
+ print(f"Error in read task: {e}")
164
+ self.close()
165
+
166
+ def transmit_task(self):
167
+ try:
168
+ while True:
169
+ packet = self.transmit_packet_queue.sync_q.get()
170
+ if self.debug_io:
171
+ print(f"Transmitting packet: {' '.join([f'{x:02X}' for x in packet])}")
172
+
173
+ data = self.interface.encode([packet])
174
+
175
+ if self.debug_io:
176
+ from cobs import cobs
177
+
178
+ daat = cobs.decode(data[:-1])
179
+ # print in space separated hex
180
+ print(f"Transmitting burst frame: {' '.join([f'{x:02X}' for x in daat])}")
181
+
182
+ # print raw frame
183
+ print(f"Transmitting 'raw' burst frame: {' '.join([f'{x:02X}' for x in data])}")
184
+ self.handle.write(data)
185
+
186
+ except Exception as e:
187
+ print(f"Error in transmit task: {e}")
188
+ self.close()
189
+
190
+ async def send(self, data: bytes):
191
+ await self.transmit_packet_queue.async_q.put(data)
192
+
193
+ async def flush_receive_queue(self):
194
+ while not self.receive_packet_queue.async_q.empty():
195
+ self.receive_packet_queue.async_q.get_nowait()
196
+
197
+ async def send_with_response(self, data: bytes):
198
+ # Flush all other packets
199
+ await self.flush_receive_queue()
200
+
201
+ start_time = time.time()
202
+ await self.transmit_packet_queue.async_q.put(data)
203
+ response = await self.receive_packet_queue.async_q.get()
204
+ end_time = time.time()
205
+
206
+ if self.debug_timings:
207
+ print(f"Time taken: {(end_time - start_time) * 1000} ms for {len(data)} bytes")
208
+
209
+ return response
210
+
211
+ async def receive(self):
212
+ return await self.receive_packet_queue.async_q.get()
213
+
214
+ def receive_all(self):
215
+ packets = []
216
+ while not self.receive_packet_queue.sync_q.empty():
217
+ packets.append(self.receive_packet_queue.sync_q.get())
218
+ return packets
219
+
220
+
221
+ async def main():
222
+ interface = SerialBurstInterface.from_serial("COM4", 115200)
223
+
224
+ for i in range(1000):
225
+ response = await interface.send_with_response(10 * f"Hello World {i}!".encode())
226
+ print(f"Received: {response}")
227
+ await asyncio.sleep(0.01)
228
+
229
+
230
+ if __name__ == "__main__":
231
+ asyncio.run(main())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: burst-link-protocol
3
- Version: 1.0.5
3
+ Version: 1.1.5
4
4
  Summary: Binary Utility for Reliable Stream Transfer (BURST) is a library for encoding and decoding binary data streams into and from a byte stream.
5
5
  Author-Email: Floris vernieuwe <floris@vernieuwe.eu>
6
6
  License-File: LICENSE
@@ -14,6 +14,8 @@ Requires-Dist: pytest-cov<7.0.0,>=6.0.0
14
14
  Requires-Dist: pytest-benchmark<6.0.0,>=5.1.0
15
15
  Requires-Dist: scikit-build-core>=0.10.7
16
16
  Requires-Dist: nanobind>=2.5.0
17
+ Requires-Dist: pyserial>=3.5
18
+ Requires-Dist: janus>=2.0.0
17
19
  Provides-Extra: dev
18
20
  Requires-Dist: scikit-build-core[pyproject]<0.11.0,>=0.10.7; extra == "dev"
19
21
  Requires-Dist: nanobind<3.0.0,>=2.5.0; extra == "dev"
@@ -61,6 +63,7 @@ uv pip install --reinstall --no-build-isolation -ve .
61
63
  Auto rebuild on run
62
64
  ```sh
63
65
  uv pip install --reinstall --no-build-isolation -Ceditable.rebuild=true -ve .
66
+ # or
64
67
  pip install --no-build-isolation -Ceditable.rebuild=true -ve .
65
68
  ```
66
69
 
@@ -0,0 +1,9 @@
1
+ burst_interface_c.cpython-311-i386-linux-gnu.so,sha256=1bBseQ90rLsLtqx0TIJ0rS0-DjTNkGYB-w0nXkjPhVw,2401840
2
+ burst_interface_c.pyi,sha256=hOKp_AFoFwNYlrbkPxQC1Z_-WKf4LQcsoPQbR3roG3s,528
3
+ burst_link_protocol/__init__.py,sha256=_AN_MobLsSZT-n7a1Tp07diWIDvna6KX3xqDkYLXF6o,261
4
+ burst_link_protocol/main.py,sha256=oG3tiGyAjAWs3g3tvgbTuNoPwV1uHRUOfWTk7wZBQhs,1181
5
+ burst_link_protocol/serial_burst_interface.py,sha256=mtdcp-VOTnGjvfRhG4IY4k5RK6EUq_fCViKsyFEaxdw,7629
6
+ burst_link_protocol-1.1.5.dist-info/METADATA,sha256=RBftwGhSNW8G89OfxFcnFGQ_EJY8r0z3Rp8VMuwH20Y,2599
7
+ burst_link_protocol-1.1.5.dist-info/WHEEL,sha256=s5c7Bx6mFIqyJbKt63fQgI-I64486E68P0CAeFKeuXE,152
8
+ burst_link_protocol-1.1.5.dist-info/RECORD,,
9
+ burst_link_protocol-1.1.5.dist-info/licenses/LICENSE,sha256=x290DRUhub7ZynoErVJsMQSTxiYhsTQdYjtDFzZTOzA,16725
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: scikit-build-core 0.11.1
2
+ Generator: scikit-build-core 0.11.4
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp311-cp311-manylinux_2_17_i686
5
5
  Tag: cp311-cp311-manylinux2014_i686
@@ -1,8 +0,0 @@
1
- burst_interface_c.cpython-311-i386-linux-gnu.so,sha256=6dZ8DynzK_iudxq_dex3vPBJvC_BNyKcLxDdS8OTyxk,2502508
2
- burst_interface_c.pyi,sha256=x_agiw2iVgDi2qJOOwEpWXVsV14K6GtJgcMqOBqjASQ,193
3
- burst_link_protocol/main.py,sha256=Pu67bwI7Szk1Y_AdrFVRTBCvl0o45uhFJTd8b0KQl_E,1170
4
- burst_link_protocol/__init__.py,sha256=cyEhIzfFlNOtubh_JjZg6bGSGn_sPq8daTi4cYBKnxc,132
5
- burst_link_protocol-1.0.5.dist-info/WHEEL,sha256=px6RMsu5cdbww4sFnrih1lk7C47hT1iFEDRtsKnen10,152
6
- burst_link_protocol-1.0.5.dist-info/RECORD,,
7
- burst_link_protocol-1.0.5.dist-info/METADATA,sha256=9_-fIKdU8nGot4HR9imtOpZaccCfm953TF6bD1SKABE,2536
8
- burst_link_protocol-1.0.5.dist-info/licenses/LICENSE,sha256=x290DRUhub7ZynoErVJsMQSTxiYhsTQdYjtDFzZTOzA,16725