rocket-welder-sdk 1.1.43__py3-none-any.whl → 1.1.45__py3-none-any.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.
- rocket_welder_sdk/__init__.py +44 -22
- rocket_welder_sdk/binary_frame_reader.py +222 -0
- rocket_welder_sdk/binary_frame_writer.py +213 -0
- rocket_welder_sdk/confidence.py +206 -0
- rocket_welder_sdk/delta_frame.py +150 -0
- rocket_welder_sdk/graphics/__init__.py +42 -0
- rocket_welder_sdk/graphics/layer_canvas.py +157 -0
- rocket_welder_sdk/graphics/protocol.py +72 -0
- rocket_welder_sdk/graphics/rgb_color.py +109 -0
- rocket_welder_sdk/graphics/stage.py +494 -0
- rocket_welder_sdk/graphics/vector_graphics_encoder.py +575 -0
- rocket_welder_sdk/high_level/__init__.py +8 -1
- rocket_welder_sdk/high_level/client.py +114 -3
- rocket_welder_sdk/high_level/connection_strings.py +88 -15
- rocket_welder_sdk/high_level/frame_sink_factory.py +2 -15
- rocket_welder_sdk/high_level/transport_protocol.py +4 -130
- rocket_welder_sdk/keypoints_protocol.py +520 -55
- rocket_welder_sdk/rocket_welder_client.py +210 -89
- rocket_welder_sdk/segmentation_result.py +387 -2
- rocket_welder_sdk/session_id.py +7 -182
- rocket_welder_sdk/transport/__init__.py +10 -3
- rocket_welder_sdk/transport/frame_sink.py +3 -3
- rocket_welder_sdk/transport/frame_source.py +2 -2
- rocket_welder_sdk/transport/websocket_transport.py +316 -0
- rocket_welder_sdk/varint.py +213 -0
- {rocket_welder_sdk-1.1.43.dist-info → rocket_welder_sdk-1.1.45.dist-info}/METADATA +1 -4
- rocket_welder_sdk-1.1.45.dist-info/RECORD +51 -0
- {rocket_welder_sdk-1.1.43.dist-info → rocket_welder_sdk-1.1.45.dist-info}/WHEEL +1 -1
- rocket_welder_sdk/transport/nng_transport.py +0 -197
- rocket_welder_sdk-1.1.43.dist-info/RECORD +0 -40
- {rocket_welder_sdk-1.1.43.dist-info → rocket_welder_sdk-1.1.45.dist-info}/top_level.txt +0 -0
|
@@ -10,7 +10,7 @@ class IFrameSource(ABC):
|
|
|
10
10
|
|
|
11
11
|
Transport-agnostic interface that handles the question: "where do frames come from?"
|
|
12
12
|
This abstraction decouples protocol logic (KeyPoints, SegmentationResults) from
|
|
13
|
-
transport mechanisms (File, TCP, WebSocket,
|
|
13
|
+
transport mechanisms (File, TCP, WebSocket, Unix Socket). Each frame is read atomically.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
@abstractmethod
|
|
@@ -40,7 +40,7 @@ class IFrameSource(ABC):
|
|
|
40
40
|
Check if more frames are available.
|
|
41
41
|
|
|
42
42
|
For streaming transports (file), this checks for EOF.
|
|
43
|
-
For message-based transports
|
|
43
|
+
For message-based transports, this may always return True until disconnection.
|
|
44
44
|
|
|
45
45
|
Returns:
|
|
46
46
|
True if more frames are available, False otherwise
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WebSocket transport for reading/writing frames.
|
|
3
|
+
Matches C# WebSocketFrameSink and WebSocketFrameSource from RocketWelder.SDK.Transport.
|
|
4
|
+
|
|
5
|
+
Uses the websockets library for async WebSocket support.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
from typing import Any, Optional, Protocol
|
|
12
|
+
|
|
13
|
+
from .frame_sink import IFrameSink
|
|
14
|
+
from .frame_source import IFrameSource
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class WebSocketProtocol(Protocol):
|
|
18
|
+
"""Protocol for WebSocket-like objects (for type checking)."""
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def closed(self) -> bool: ...
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def state(self) -> Any: ...
|
|
25
|
+
|
|
26
|
+
async def send(self, message: bytes) -> None: ...
|
|
27
|
+
|
|
28
|
+
async def recv(self) -> bytes: ...
|
|
29
|
+
|
|
30
|
+
async def close(self) -> None: ...
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class WebSocketFrameSink(IFrameSink):
|
|
34
|
+
"""
|
|
35
|
+
Frame sink that writes to a WebSocket connection.
|
|
36
|
+
|
|
37
|
+
Each frame is sent as a single binary WebSocket message.
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
leave_open: If True, doesn't close WebSocket on disposal
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
__slots__ = ("_closed", "_leave_open", "_websocket")
|
|
44
|
+
|
|
45
|
+
def __init__(self, websocket: WebSocketProtocol, leave_open: bool = False) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Create a WebSocket frame sink.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
websocket: WebSocket connection to write to
|
|
51
|
+
leave_open: If True, doesn't close WebSocket on disposal
|
|
52
|
+
"""
|
|
53
|
+
if websocket is None:
|
|
54
|
+
raise ValueError("websocket cannot be None")
|
|
55
|
+
self._websocket = websocket
|
|
56
|
+
self._leave_open = leave_open
|
|
57
|
+
self._closed = False
|
|
58
|
+
|
|
59
|
+
def write_frame(self, frame_data: bytes) -> None:
|
|
60
|
+
"""
|
|
61
|
+
Write a complete frame to the WebSocket synchronously.
|
|
62
|
+
|
|
63
|
+
Note: WebSocket is inherently async, so this runs the async
|
|
64
|
+
version in a new event loop. Prefer write_frame_async for better performance.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
frame_data: Complete frame data to write
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
RuntimeError: If sink is closed or WebSocket is not open
|
|
71
|
+
"""
|
|
72
|
+
if self._closed:
|
|
73
|
+
raise RuntimeError("WebSocketFrameSink is closed")
|
|
74
|
+
|
|
75
|
+
# Use asyncio.run for simplicity (creates new event loop)
|
|
76
|
+
try:
|
|
77
|
+
loop = asyncio.get_running_loop()
|
|
78
|
+
# If we're already in an async context, use run_coroutine_threadsafe
|
|
79
|
+
future = asyncio.run_coroutine_threadsafe(self.write_frame_async(frame_data), loop)
|
|
80
|
+
future.result()
|
|
81
|
+
except RuntimeError:
|
|
82
|
+
# No running event loop, create a new one
|
|
83
|
+
asyncio.run(self.write_frame_async(frame_data))
|
|
84
|
+
|
|
85
|
+
async def write_frame_async(self, frame_data: bytes) -> None:
|
|
86
|
+
"""
|
|
87
|
+
Write a complete frame to the WebSocket asynchronously.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
frame_data: Complete frame data to write
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
RuntimeError: If sink is closed or WebSocket is not open
|
|
94
|
+
"""
|
|
95
|
+
if self._closed:
|
|
96
|
+
raise RuntimeError("WebSocketFrameSink is closed")
|
|
97
|
+
|
|
98
|
+
if self._websocket.closed:
|
|
99
|
+
raise RuntimeError(f"WebSocket is not open: {self._websocket.state.name}")
|
|
100
|
+
|
|
101
|
+
# Send as single binary message
|
|
102
|
+
await self._websocket.send(frame_data)
|
|
103
|
+
|
|
104
|
+
def flush(self) -> None:
|
|
105
|
+
"""
|
|
106
|
+
Flush any buffered data.
|
|
107
|
+
|
|
108
|
+
WebSocket sends immediately, so this is a no-op.
|
|
109
|
+
"""
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
async def flush_async(self) -> None:
|
|
113
|
+
"""
|
|
114
|
+
Flush any buffered data asynchronously.
|
|
115
|
+
|
|
116
|
+
WebSocket sends immediately, so this is a no-op.
|
|
117
|
+
"""
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
def close(self) -> None:
|
|
121
|
+
"""Close the sink and release resources."""
|
|
122
|
+
if self._closed:
|
|
123
|
+
return
|
|
124
|
+
self._closed = True
|
|
125
|
+
|
|
126
|
+
if not self._leave_open and not self._websocket.closed:
|
|
127
|
+
try:
|
|
128
|
+
loop = asyncio.get_running_loop()
|
|
129
|
+
future = asyncio.run_coroutine_threadsafe(self.close_async(), loop)
|
|
130
|
+
future.result()
|
|
131
|
+
except RuntimeError:
|
|
132
|
+
asyncio.run(self._close_websocket())
|
|
133
|
+
|
|
134
|
+
async def close_async(self) -> None:
|
|
135
|
+
"""Close the sink and release resources asynchronously."""
|
|
136
|
+
if self._closed:
|
|
137
|
+
return
|
|
138
|
+
self._closed = True
|
|
139
|
+
|
|
140
|
+
await self._close_websocket()
|
|
141
|
+
|
|
142
|
+
async def _close_websocket(self) -> None:
|
|
143
|
+
"""Internal async close helper."""
|
|
144
|
+
if not self._leave_open and not self._websocket.closed:
|
|
145
|
+
try: # noqa: SIM105
|
|
146
|
+
await self._websocket.close()
|
|
147
|
+
except Exception:
|
|
148
|
+
# Best effort close
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class WebSocketFrameSource(IFrameSource):
|
|
153
|
+
"""
|
|
154
|
+
Frame source that reads from a WebSocket connection.
|
|
155
|
+
|
|
156
|
+
Each WebSocket binary message is treated as a complete frame.
|
|
157
|
+
|
|
158
|
+
Attributes:
|
|
159
|
+
leave_open: If True, doesn't close WebSocket on disposal
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
__slots__ = ("_closed", "_leave_open", "_websocket")
|
|
163
|
+
|
|
164
|
+
def __init__(self, websocket: WebSocketProtocol, leave_open: bool = False) -> None:
|
|
165
|
+
"""
|
|
166
|
+
Create a WebSocket frame source.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
websocket: WebSocket connection to read from
|
|
170
|
+
leave_open: If True, doesn't close WebSocket on disposal
|
|
171
|
+
"""
|
|
172
|
+
if websocket is None:
|
|
173
|
+
raise ValueError("websocket cannot be None")
|
|
174
|
+
self._websocket = websocket
|
|
175
|
+
self._leave_open = leave_open
|
|
176
|
+
self._closed = False
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def has_more_frames(self) -> bool:
|
|
180
|
+
"""
|
|
181
|
+
Check if more frames are available.
|
|
182
|
+
|
|
183
|
+
Returns True if the WebSocket is open or in CloseSent state.
|
|
184
|
+
"""
|
|
185
|
+
return not self._websocket.closed
|
|
186
|
+
|
|
187
|
+
def read_frame(self) -> Optional[bytes]:
|
|
188
|
+
"""
|
|
189
|
+
Read a complete frame from the WebSocket synchronously.
|
|
190
|
+
|
|
191
|
+
Note: WebSocket is inherently async, so this runs the async
|
|
192
|
+
version. Prefer read_frame_async for better performance.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Complete frame data, or None if connection closed
|
|
196
|
+
|
|
197
|
+
Raises:
|
|
198
|
+
RuntimeError: If source is closed
|
|
199
|
+
"""
|
|
200
|
+
if self._closed:
|
|
201
|
+
raise RuntimeError("WebSocketFrameSource is closed")
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
loop = asyncio.get_running_loop()
|
|
205
|
+
future = asyncio.run_coroutine_threadsafe(self.read_frame_async(), loop)
|
|
206
|
+
return future.result()
|
|
207
|
+
except RuntimeError:
|
|
208
|
+
return asyncio.run(self.read_frame_async())
|
|
209
|
+
|
|
210
|
+
async def read_frame_async(self) -> Optional[bytes]:
|
|
211
|
+
"""
|
|
212
|
+
Read a complete frame from the WebSocket asynchronously.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Complete frame data, or None if connection closed
|
|
216
|
+
|
|
217
|
+
Raises:
|
|
218
|
+
RuntimeError: If source is closed
|
|
219
|
+
ValueError: If received non-binary message
|
|
220
|
+
"""
|
|
221
|
+
if self._closed:
|
|
222
|
+
raise RuntimeError("WebSocketFrameSource is closed")
|
|
223
|
+
|
|
224
|
+
if not self.has_more_frames:
|
|
225
|
+
return None
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
# websockets library handles message framing automatically
|
|
229
|
+
message = await self._websocket.recv()
|
|
230
|
+
|
|
231
|
+
# Ensure we got binary data
|
|
232
|
+
if isinstance(message, str):
|
|
233
|
+
raise ValueError("Expected binary message, got text")
|
|
234
|
+
|
|
235
|
+
return message
|
|
236
|
+
|
|
237
|
+
except Exception:
|
|
238
|
+
# Connection closed or error
|
|
239
|
+
return None
|
|
240
|
+
|
|
241
|
+
def close(self) -> None:
|
|
242
|
+
"""Close the source and release resources."""
|
|
243
|
+
if self._closed:
|
|
244
|
+
return
|
|
245
|
+
self._closed = True
|
|
246
|
+
|
|
247
|
+
if not self._leave_open and not self._websocket.closed:
|
|
248
|
+
try:
|
|
249
|
+
loop = asyncio.get_running_loop()
|
|
250
|
+
future = asyncio.run_coroutine_threadsafe(self.close_async(), loop)
|
|
251
|
+
future.result()
|
|
252
|
+
except RuntimeError:
|
|
253
|
+
asyncio.run(self._close_websocket())
|
|
254
|
+
|
|
255
|
+
async def close_async(self) -> None:
|
|
256
|
+
"""Close the source and release resources asynchronously."""
|
|
257
|
+
if self._closed:
|
|
258
|
+
return
|
|
259
|
+
self._closed = True
|
|
260
|
+
|
|
261
|
+
await self._close_websocket()
|
|
262
|
+
|
|
263
|
+
async def _close_websocket(self) -> None:
|
|
264
|
+
"""Internal async close helper."""
|
|
265
|
+
if not self._leave_open and not self._websocket.closed:
|
|
266
|
+
try: # noqa: SIM105
|
|
267
|
+
await self._websocket.close()
|
|
268
|
+
except Exception:
|
|
269
|
+
# Best effort close
|
|
270
|
+
pass
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
async def connect_websocket_sink(url: str, leave_open: bool = False) -> WebSocketFrameSink:
|
|
274
|
+
"""
|
|
275
|
+
Connect to a WebSocket server and return a frame sink.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
url: WebSocket URL (ws:// or wss://)
|
|
279
|
+
leave_open: If True, doesn't close WebSocket on disposal
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Connected WebSocketFrameSink
|
|
283
|
+
"""
|
|
284
|
+
try:
|
|
285
|
+
import websockets
|
|
286
|
+
except ImportError as e:
|
|
287
|
+
raise ImportError(
|
|
288
|
+
"websockets package is required for WebSocket transport. "
|
|
289
|
+
"Install with: pip install websockets"
|
|
290
|
+
) from e
|
|
291
|
+
|
|
292
|
+
websocket = await websockets.connect(url)
|
|
293
|
+
return WebSocketFrameSink(websocket, leave_open)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
async def connect_websocket_source(url: str, leave_open: bool = False) -> WebSocketFrameSource:
|
|
297
|
+
"""
|
|
298
|
+
Connect to a WebSocket server and return a frame source.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
url: WebSocket URL (ws:// or wss://)
|
|
302
|
+
leave_open: If True, doesn't close WebSocket on disposal
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Connected WebSocketFrameSource
|
|
306
|
+
"""
|
|
307
|
+
try:
|
|
308
|
+
import websockets
|
|
309
|
+
except ImportError as e:
|
|
310
|
+
raise ImportError(
|
|
311
|
+
"websockets package is required for WebSocket transport. "
|
|
312
|
+
"Install with: pip install websockets"
|
|
313
|
+
) from e
|
|
314
|
+
|
|
315
|
+
websocket = await websockets.connect(url)
|
|
316
|
+
return WebSocketFrameSource(websocket, leave_open)
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core varint and ZigZag encoding/decoding algorithms.
|
|
3
|
+
Matches C# Varint static class from RocketWelder.SDK.Protocols.
|
|
4
|
+
|
|
5
|
+
Single source of truth for all varint operations in the SDK.
|
|
6
|
+
Compatible with Protocol Buffers varint encoding.
|
|
7
|
+
|
|
8
|
+
Varint encoding uses 7 bits per byte with MSB as continuation flag.
|
|
9
|
+
ZigZag encoding maps signed integers to unsigned for efficient varint encoding
|
|
10
|
+
of values near zero (both positive and negative).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Tuple
|
|
16
|
+
|
|
17
|
+
# Maximum bytes needed for a uint32 varint
|
|
18
|
+
MAX_BYTES_UINT32 = 5
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def zigzag_encode(value: int) -> int:
|
|
22
|
+
"""
|
|
23
|
+
ZigZag encode a signed integer to unsigned.
|
|
24
|
+
|
|
25
|
+
Maps negative numbers to odd positives: 0->0, -1->1, 1->2, -2->3, 2->4, etc.
|
|
26
|
+
This allows efficient varint encoding of signed values near zero.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
value: Signed integer to encode
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Unsigned integer (ZigZag encoded)
|
|
33
|
+
"""
|
|
34
|
+
return (value << 1) ^ (value >> 31)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def zigzag_decode(value: int) -> int:
|
|
38
|
+
"""
|
|
39
|
+
ZigZag decode an unsigned integer to signed.
|
|
40
|
+
|
|
41
|
+
Reverses the ZigZag encoding: 0->0, 1->-1, 2->1, 3->-2, 4->2, etc.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
value: Unsigned integer (ZigZag encoded)
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Signed integer (decoded)
|
|
48
|
+
"""
|
|
49
|
+
return (value >> 1) ^ -(value & 1)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_byte_count(value: int) -> int:
|
|
53
|
+
"""
|
|
54
|
+
Calculate the number of bytes needed to encode a value as varint.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
value: Unsigned integer value
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Number of bytes needed (1-5)
|
|
61
|
+
"""
|
|
62
|
+
if value < 0x80:
|
|
63
|
+
return 1
|
|
64
|
+
if value < 0x4000:
|
|
65
|
+
return 2
|
|
66
|
+
if value < 0x200000:
|
|
67
|
+
return 3
|
|
68
|
+
if value < 0x10000000:
|
|
69
|
+
return 4
|
|
70
|
+
return 5
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def write_varint(buffer: bytearray, offset: int, value: int) -> int:
|
|
74
|
+
"""
|
|
75
|
+
Write a varint to a buffer at the given offset.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
buffer: Destination buffer (must have at least 5 bytes from offset)
|
|
79
|
+
offset: Starting offset in buffer
|
|
80
|
+
value: Unsigned value to encode
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Number of bytes written (1-5)
|
|
84
|
+
"""
|
|
85
|
+
i = 0
|
|
86
|
+
while value >= 0x80:
|
|
87
|
+
buffer[offset + i] = (value & 0x7F) | 0x80
|
|
88
|
+
value >>= 7
|
|
89
|
+
i += 1
|
|
90
|
+
buffer[offset + i] = value
|
|
91
|
+
return i + 1
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def write_zigzag(buffer: bytearray, offset: int, value: int) -> int:
|
|
95
|
+
"""
|
|
96
|
+
Write a ZigZag-encoded signed integer as varint.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
buffer: Destination buffer (must have at least 5 bytes from offset)
|
|
100
|
+
offset: Starting offset in buffer
|
|
101
|
+
value: Signed value to encode
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Number of bytes written (1-5)
|
|
105
|
+
"""
|
|
106
|
+
return write_varint(buffer, offset, zigzag_encode(value))
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def read_varint(data: bytes, offset: int) -> Tuple[int, int]:
|
|
110
|
+
"""
|
|
111
|
+
Read a varint from bytes at the given offset.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
data: Source buffer
|
|
115
|
+
offset: Starting offset in buffer
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Tuple of (decoded_value, bytes_read)
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
EOFError: If buffer ends before varint completes
|
|
122
|
+
ValueError: If varint is malformed (too long)
|
|
123
|
+
"""
|
|
124
|
+
result = 0
|
|
125
|
+
shift = 0
|
|
126
|
+
i = 0
|
|
127
|
+
|
|
128
|
+
while True:
|
|
129
|
+
if offset + i >= len(data):
|
|
130
|
+
raise EOFError("Unexpected end of varint")
|
|
131
|
+
|
|
132
|
+
b = data[offset + i]
|
|
133
|
+
result |= (b & 0x7F) << shift
|
|
134
|
+
i += 1
|
|
135
|
+
|
|
136
|
+
if (b & 0x80) == 0:
|
|
137
|
+
break
|
|
138
|
+
|
|
139
|
+
shift += 7
|
|
140
|
+
if shift >= 35:
|
|
141
|
+
raise ValueError("Varint too long (corrupted data)")
|
|
142
|
+
|
|
143
|
+
return result, i
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def read_zigzag(data: bytes, offset: int) -> Tuple[int, int]:
|
|
147
|
+
"""
|
|
148
|
+
Read a ZigZag-encoded signed integer.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
data: Source buffer
|
|
152
|
+
offset: Starting offset in buffer
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Tuple of (decoded_signed_value, bytes_read)
|
|
156
|
+
"""
|
|
157
|
+
encoded, bytes_read = read_varint(data, offset)
|
|
158
|
+
return zigzag_decode(encoded), bytes_read
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def try_read_varint(data: bytes, offset: int) -> Tuple[bool, int, int]:
|
|
162
|
+
"""
|
|
163
|
+
Try to read a varint, returning False if not enough data.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
data: Source buffer
|
|
167
|
+
offset: Starting offset in buffer
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Tuple of (success, value, bytes_read)
|
|
171
|
+
If success is False, value and bytes_read are 0
|
|
172
|
+
"""
|
|
173
|
+
result = 0
|
|
174
|
+
shift = 0
|
|
175
|
+
|
|
176
|
+
for i in range(min(5, len(data) - offset)):
|
|
177
|
+
b = data[offset + i]
|
|
178
|
+
result |= (b & 0x7F) << shift
|
|
179
|
+
|
|
180
|
+
if (b & 0x80) == 0:
|
|
181
|
+
return True, result, i + 1
|
|
182
|
+
|
|
183
|
+
shift += 7
|
|
184
|
+
|
|
185
|
+
return False, 0, 0
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def encode_varint(value: int) -> bytes:
|
|
189
|
+
"""
|
|
190
|
+
Convenience function to encode a value to varint bytes.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
value: Unsigned value to encode
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Bytes containing the varint encoding
|
|
197
|
+
"""
|
|
198
|
+
buffer = bytearray(MAX_BYTES_UINT32)
|
|
199
|
+
length = write_varint(buffer, 0, value)
|
|
200
|
+
return bytes(buffer[:length])
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def encode_zigzag(value: int) -> bytes:
|
|
204
|
+
"""
|
|
205
|
+
Convenience function to encode a signed value to ZigZag varint bytes.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
value: Signed value to encode
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Bytes containing the ZigZag varint encoding
|
|
212
|
+
"""
|
|
213
|
+
return encode_varint(zigzag_encode(value))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rocket-welder-sdk
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.45
|
|
4
4
|
Summary: High-performance video streaming SDK for RocketWelder services using ZeroBuffer IPC
|
|
5
5
|
Home-page: https://github.com/modelingevolution/rocket-welder-sdk
|
|
6
6
|
Author: ModelingEvolution
|
|
@@ -32,8 +32,6 @@ Requires-Dist: zerobuffer-ipc>=1.1.17
|
|
|
32
32
|
Requires-Dist: pydantic>=2.5.0
|
|
33
33
|
Requires-Dist: py-micro-plumberd>=0.1.8
|
|
34
34
|
Requires-Dist: typing-extensions>=4.0.0
|
|
35
|
-
Provides-Extra: nng
|
|
36
|
-
Requires-Dist: pynng>=0.7.2; extra == "nng"
|
|
37
35
|
Provides-Extra: dev
|
|
38
36
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
39
37
|
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
@@ -42,7 +40,6 @@ Requires-Dist: black>=22.0; extra == "dev"
|
|
|
42
40
|
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
43
41
|
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
44
42
|
Requires-Dist: types-setuptools; extra == "dev"
|
|
45
|
-
Requires-Dist: pynng>=0.7.2; extra == "dev"
|
|
46
43
|
Dynamic: author
|
|
47
44
|
Dynamic: home-page
|
|
48
45
|
Dynamic: requires-python
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
rocket_welder_sdk/__init__.py,sha256=5hcsNb0zu8dtRHD9sgCAi4YbWJe-1g6cl9jSuLQZk_U,3889
|
|
2
|
+
rocket_welder_sdk/binary_frame_reader.py,sha256=5IyYO7yv07gLpQ9Toyw1zLkTrnqHLjxIeMVRn8B40-E,6270
|
|
3
|
+
rocket_welder_sdk/binary_frame_writer.py,sha256=2-VEJNHw0fL30TXsCBHyhHltvgx7KUahmbamwdSU8sw,6658
|
|
4
|
+
rocket_welder_sdk/bytes_size.py,sha256=Myl29-wyWCIYdbMmgaxXebT8Dz8_Fwcr3fnfaNW81P0,7463
|
|
5
|
+
rocket_welder_sdk/confidence.py,sha256=AlgVM7gNDff3UxcQHhA5sJlTb4VeOMFAraF3W59AeSI,6498
|
|
6
|
+
rocket_welder_sdk/connection_string.py,sha256=NIC6OiOXF-DeBFCWzgMFOWsenrSS45hY81j_HLMSpgo,9974
|
|
7
|
+
rocket_welder_sdk/controllers.py,sha256=uuVyKsNHtpaIzQt8pXCAu7SywoFa7cfww8VSa4LNi_E,33130
|
|
8
|
+
rocket_welder_sdk/delta_frame.py,sha256=51RFWI2odKVak-d3qhv7S8DiXbk4M5f-bbuTT1unXfk,4518
|
|
9
|
+
rocket_welder_sdk/frame_metadata.py,sha256=TMLIY47cIdIlxqk9xj7I3M8FZFmZ3GcVoLZht7prjQM,3929
|
|
10
|
+
rocket_welder_sdk/gst_metadata.py,sha256=6Ov-DekGsKNuAo3UIc-g9fh4_SAPdHYQTnnPqCK3Ks0,16180
|
|
11
|
+
rocket_welder_sdk/keypoints_protocol.py,sha256=kN8ok6Ptpgl4txoQE9DpKhMoI7zr73vTw9CTvCICtgE,36921
|
|
12
|
+
rocket_welder_sdk/opencv_controller.py,sha256=MDM6_yFBB9BaMa5jnZRqw7xZZB-WuLr7EPrrfHQ2DK4,9905
|
|
13
|
+
rocket_welder_sdk/periodic_timer.py,sha256=hnObybmrnf3J47QrNKJhYAytLKwria016123NvPRfQ0,9369
|
|
14
|
+
rocket_welder_sdk/py.typed,sha256=0cXFZXmes4Y-vnl4lO3HtyyyWaFNw85B7tJdFeCtHDc,67
|
|
15
|
+
rocket_welder_sdk/rocket_welder_client.py,sha256=iSyMiIx_5GgL5VI3W4G6o7JlG3A9G8JDlsTwLhQApVU,28609
|
|
16
|
+
rocket_welder_sdk/segmentation_result.py,sha256=b3xpv6AZyWzm924cd1RfXwCuEwdkPL5TUA0gzT3HpBA,29153
|
|
17
|
+
rocket_welder_sdk/session_id.py,sha256=YgnDwmdOcdJTXIObjkrzIkxU2gbMzNko1h1ms4lNIfU,1928
|
|
18
|
+
rocket_welder_sdk/varint.py,sha256=SmffemQCXToRzs3lb7hWQWDY7NmKv4XG_Wb0oeMrb_I,5058
|
|
19
|
+
rocket_welder_sdk/external_controls/__init__.py,sha256=ldOLGhLLS5BQL8m4VKFYV0SvsNNlV2tghlc7rkqadU8,699
|
|
20
|
+
rocket_welder_sdk/external_controls/contracts.py,sha256=3DU6pdpteN50gF2fsS7C2279dGjDa0tZLrLntkBa2LM,2607
|
|
21
|
+
rocket_welder_sdk/external_controls/contracts_old.py,sha256=XWriuXJZu5caTSS0bcTIOZcKnj-IRCm96voA4gqLBfU,2980
|
|
22
|
+
rocket_welder_sdk/graphics/__init__.py,sha256=4sKaqeu391WfVX1c_TDxAbucy7OJF8Aw8CG77NeggFA,1230
|
|
23
|
+
rocket_welder_sdk/graphics/layer_canvas.py,sha256=FcgrrCaI-2XlVOs4qYTGvpJQdVw_NP2V7D4a1LgLtS4,4580
|
|
24
|
+
rocket_welder_sdk/graphics/protocol.py,sha256=57GOx9rlKGojm7jnXdM2t3bpa4Ykii1o3wQtQ5IO-Hs,1357
|
|
25
|
+
rocket_welder_sdk/graphics/rgb_color.py,sha256=do6y362CTROiLMqbQwdvJF07L8wMWRv3n7-PVWP-Flk,3497
|
|
26
|
+
rocket_welder_sdk/graphics/stage.py,sha256=2hgkKGCxLxcZ64cHBKgxxFHSZOlLue236x5f43cmYk0,15538
|
|
27
|
+
rocket_welder_sdk/graphics/vector_graphics_encoder.py,sha256=lD3cIOAVL_JvKZayIQ_TaQ5fa8S6d9Q45C91PubHMbk,16483
|
|
28
|
+
rocket_welder_sdk/high_level/__init__.py,sha256=OKbI3l0PFo1Cb_v0kbv16Wr05urz8heVGps0dJcTcG4,1507
|
|
29
|
+
rocket_welder_sdk/high_level/client.py,sha256=rVnnrn68PEvc68p5acuOBZfEjobWWed8XcssTRzxNr8,11502
|
|
30
|
+
rocket_welder_sdk/high_level/connection_strings.py,sha256=1Hp72V5qDngHGXyjBRFMckz365BMpGVjb3PMSGvCtow,12531
|
|
31
|
+
rocket_welder_sdk/high_level/data_context.py,sha256=SXJvDpDBFi8Lm4XqSRSHK7YUUHuugXGo4ZRCb6_z5l0,4833
|
|
32
|
+
rocket_welder_sdk/high_level/frame_sink_factory.py,sha256=tLhMV_qnY-ZUtffIzhycM9znNjYguRumba4vphpNx7E,3493
|
|
33
|
+
rocket_welder_sdk/high_level/schema.py,sha256=2Vv0rDwahtGswWB_ceaCdc7JDtmbkx4wE2jQePzeTpU,5367
|
|
34
|
+
rocket_welder_sdk/high_level/transport_protocol.py,sha256=pdl7l1Tu9b7IYb95q6kzDY7-g1fbhSnhdV_gGhLeYzk,2943
|
|
35
|
+
rocket_welder_sdk/transport/__init__.py,sha256=J1iMvmIwC89PO8VZwd-qxKoAoB6BOxEY82aB9wLdB5c,950
|
|
36
|
+
rocket_welder_sdk/transport/frame_sink.py,sha256=--uL0kqxoz10Qya3eKpH6vjbMuSZHP0C9q6uwmbEo38,3332
|
|
37
|
+
rocket_welder_sdk/transport/frame_source.py,sha256=pgWFmqSLoIjGx8vW6Uv5uYCpcC49_iBSVU0oJZHDuPw,2171
|
|
38
|
+
rocket_welder_sdk/transport/stream_transport.py,sha256=FhxFlZT-CTo6aPq6VclM1A_ecqfeHcwR4Ty1vmlt3W0,5886
|
|
39
|
+
rocket_welder_sdk/transport/tcp_transport.py,sha256=Tui6nKgu50C1KV_UDANQKgVK3M52XN7zrkZTcWIpGmY,4745
|
|
40
|
+
rocket_welder_sdk/transport/unix_socket_transport.py,sha256=t44Q2Fj6fMv53Ll561hN35-ZbHPdAQt0t9tUAXoL0rY,12245
|
|
41
|
+
rocket_welder_sdk/transport/websocket_transport.py,sha256=ti-Vr2I90JvgJgRfjqo2PfTYk5B86SYZhNofuxpwdBo,9591
|
|
42
|
+
rocket_welder_sdk/ui/__init__.py,sha256=5-fCkv3vG0VVVZkselrifkMmx8d51EJp3nNT9mt4JK4,886
|
|
43
|
+
rocket_welder_sdk/ui/controls.py,sha256=cRBxdUDWMOYlCxtw1PV11fTlte6qLxSfCGpr9bQiLcE,11162
|
|
44
|
+
rocket_welder_sdk/ui/icons.py,sha256=DcDklZkPmiEzlOD4IR7VTJOtGPCuuh_OM_WN7ScghWE,8934592
|
|
45
|
+
rocket_welder_sdk/ui/ui_events_projection.py,sha256=siiNhjLEBOPfTKw1ZhOPGkwIN5rLDH7V9VCZTNrhEtQ,7836
|
|
46
|
+
rocket_welder_sdk/ui/ui_service.py,sha256=uRdpyJGoCQmtOli_HKSrxLwhZYG-XRuHIYdkmFz1zNk,12026
|
|
47
|
+
rocket_welder_sdk/ui/value_types.py,sha256=f7OA_9zgXEDPoITc8v8SfAR23I4XeFhE3E2_GcAbR6k,1616
|
|
48
|
+
rocket_welder_sdk-1.1.45.dist-info/METADATA,sha256=Kw6yzgaMHhKdQ5iskkBWSXP54zGr1vMpgz0mv_PQsvY,24739
|
|
49
|
+
rocket_welder_sdk-1.1.45.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
50
|
+
rocket_welder_sdk-1.1.45.dist-info/top_level.txt,sha256=2iZvBjnwVCUW-uDE23-eJld5PZ9-mlPI69QiXM5IrTA,18
|
|
51
|
+
rocket_welder_sdk-1.1.45.dist-info/RECORD,,
|