rocket-welder-sdk 1.1.34.dev9__py3-none-any.whl → 1.1.34.dev11__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.
@@ -338,7 +338,7 @@ class OneWayShmController(IController):
338
338
  Matches C# CreateMat behavior - creates Mat wrapping the data.
339
339
 
340
340
  Frame data layout from GStreamer zerosink:
341
- [FrameMetadata (16 bytes)][Pixel Data (W×H×C bytes)]
341
+ [FrameMetadata (16 bytes)][Pixel Data (WxHxC bytes)]
342
342
 
343
343
  Args:
344
344
  frame: ZeroBuffer frame
@@ -18,7 +18,7 @@ from __future__ import annotations
18
18
 
19
19
  import struct
20
20
  from dataclasses import dataclass
21
- from typing import Optional
21
+ from typing import ClassVar, Dict, Optional
22
22
 
23
23
  # Size of the FrameMetadata structure in bytes
24
24
  FRAME_METADATA_SIZE = 16
@@ -113,7 +113,7 @@ class GstVideoFormat:
113
113
  GRAY16_BE = 26
114
114
  GRAY16_LE = 27
115
115
 
116
- _FORMAT_NAMES: dict[int, str] = {
116
+ _FORMAT_NAMES: ClassVar[Dict[int, str]] = {
117
117
  0: "UNKNOWN",
118
118
  2: "I420",
119
119
  3: "YV12",
@@ -1,26 +1,18 @@
1
1
  """
2
2
  High-level API for RocketWelder SDK.
3
3
 
4
- Provides a simplified, user-friendly API for common video processing workflows
5
- with automatic transport management and schema definitions.
4
+ Mirrors C# RocketWelder.SDK API for consistent developer experience.
6
5
 
7
6
  Example:
8
- from rocket_welder_sdk.high_level import RocketWelderClient, Transport
7
+ from rocket_welder_sdk.high_level import RocketWelderClient
9
8
 
10
- async with RocketWelderClient.from_environment() as client:
11
- # Define keypoints schema
9
+ with RocketWelderClient.from_environment() as client:
12
10
  nose = client.keypoints.define_point("nose")
13
- left_eye = client.keypoints.define_point("left_eye")
14
-
15
- # Define segmentation classes
16
11
  person = client.segmentation.define_class(1, "person")
17
-
18
- async for input_frame, seg_ctx, kp_ctx, output_frame in client.start():
19
- # Process frame...
20
- kp_ctx.add(nose, x=100, y=200, confidence=0.95)
21
- seg_ctx.add(person, instance_id=0, points=contour_points)
12
+ client.start(process_frame)
22
13
  """
23
14
 
15
+ from .client import RocketWelderClient, RocketWelderClientOptions
24
16
  from .connection_strings import (
25
17
  KeyPointsConnectionString,
26
18
  SegmentationConnectionString,
@@ -34,15 +26,11 @@ from .data_context import (
34
26
  from .schema import (
35
27
  IKeyPointsSchema,
36
28
  ISegmentationSchema,
37
- KeyPoint,
29
+ KeyPointDefinition,
38
30
  SegmentClass,
39
31
  )
40
32
  from .transport_protocol import (
41
- MessagingLibrary,
42
- MessagingPattern,
43
- Transport,
44
- TransportBuilder,
45
- TransportLayer,
33
+ TransportKind,
46
34
  TransportProtocol,
47
35
  )
48
36
 
@@ -51,15 +39,13 @@ __all__ = [
51
39
  "IKeyPointsSchema",
52
40
  "ISegmentationDataContext",
53
41
  "ISegmentationSchema",
54
- "KeyPoint",
42
+ "KeyPointDefinition",
55
43
  "KeyPointsConnectionString",
56
- "MessagingLibrary",
57
- "MessagingPattern",
44
+ "RocketWelderClient",
45
+ "RocketWelderClientOptions",
58
46
  "SegmentClass",
59
47
  "SegmentationConnectionString",
60
- "Transport",
61
- "TransportBuilder",
62
- "TransportLayer",
48
+ "TransportKind",
63
49
  "TransportProtocol",
64
50
  "VideoSourceConnectionString",
65
51
  "VideoSourceType",
@@ -0,0 +1,262 @@
1
+ """
2
+ RocketWelderClient - High-level API matching C# RocketWelder.SDK.
3
+
4
+ Usage:
5
+ with RocketWelderClient.from_environment() as client:
6
+ # Define schema
7
+ nose = client.keypoints.define_point("nose")
8
+ person = client.segmentation.define_class(1, "person")
9
+
10
+ # Start processing
11
+ client.start(process_frame)
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ from dataclasses import dataclass, field
18
+ from typing import TYPE_CHECKING, Any, Callable, Optional
19
+
20
+ import numpy as np
21
+ import numpy.typing as npt
22
+ from typing_extensions import TypeAlias
23
+
24
+ from .connection_strings import (
25
+ KeyPointsConnectionString,
26
+ SegmentationConnectionString,
27
+ VideoSourceConnectionString,
28
+ )
29
+ from .data_context import (
30
+ IKeyPointsDataContext,
31
+ ISegmentationDataContext,
32
+ KeyPointsDataContext,
33
+ SegmentationDataContext,
34
+ )
35
+ from .schema import (
36
+ IKeyPointsSchema,
37
+ ISegmentationSchema,
38
+ KeyPointsSchema,
39
+ SegmentationSchema,
40
+ )
41
+ from .transport_protocol import TransportKind
42
+
43
+ if TYPE_CHECKING:
44
+ from rocket_welder_sdk.keypoints_protocol import KeyPointsSink
45
+ from rocket_welder_sdk.transport.frame_sink import IFrameSink
46
+
47
+ # Type alias for OpenCV Mat (numpy array)
48
+ Mat: TypeAlias = npt.NDArray[np.uint8]
49
+
50
+ logger = logging.getLogger(__name__)
51
+
52
+
53
+ @dataclass
54
+ class RocketWelderClientOptions:
55
+ """Configuration options for RocketWelderClient."""
56
+
57
+ video_source: VideoSourceConnectionString = field(
58
+ default_factory=VideoSourceConnectionString.default
59
+ )
60
+ keypoints: KeyPointsConnectionString = field(default_factory=KeyPointsConnectionString.default)
61
+ segmentation: SegmentationConnectionString = field(
62
+ default_factory=SegmentationConnectionString.default
63
+ )
64
+
65
+ @classmethod
66
+ def from_environment(cls) -> RocketWelderClientOptions:
67
+ """Create from environment variables."""
68
+ return cls(
69
+ video_source=VideoSourceConnectionString.from_environment(),
70
+ keypoints=KeyPointsConnectionString.from_environment(),
71
+ segmentation=SegmentationConnectionString.from_environment(),
72
+ )
73
+
74
+
75
+ class RocketWelderClient:
76
+ """
77
+ High-level client for RocketWelder SDK.
78
+
79
+ Mirrors C# RocketWelder.SDK.IRocketWelderClient interface.
80
+ """
81
+
82
+ def __init__(self, options: RocketWelderClientOptions) -> None:
83
+ self._options = options
84
+ self._keypoints_schema = KeyPointsSchema()
85
+ self._segmentation_schema = SegmentationSchema()
86
+ self._keypoints_sink: Optional[KeyPointsSink] = None
87
+ self._keypoints_frame_sink: Optional[IFrameSink] = None
88
+ self._segmentation_frame_sink: Optional[IFrameSink] = None
89
+ self._closed = False
90
+ logger.debug("RocketWelderClient created with options: %s", options)
91
+
92
+ @classmethod
93
+ def from_environment(cls) -> RocketWelderClient:
94
+ """Create client from environment variables."""
95
+ logger.info("Creating RocketWelderClient from environment variables")
96
+ return cls(RocketWelderClientOptions.from_environment())
97
+
98
+ @classmethod
99
+ def create(cls, options: Optional[RocketWelderClientOptions] = None) -> RocketWelderClient:
100
+ """Create client with explicit options."""
101
+ return cls(options or RocketWelderClientOptions())
102
+
103
+ @property
104
+ def keypoints(self) -> IKeyPointsSchema:
105
+ """Schema for defining keypoints."""
106
+ return self._keypoints_schema
107
+
108
+ @property
109
+ def segmentation(self) -> ISegmentationSchema:
110
+ """Schema for defining segmentation classes."""
111
+ return self._segmentation_schema
112
+
113
+ def start(
114
+ self,
115
+ process_frame: Callable[[Mat, ISegmentationDataContext, IKeyPointsDataContext, Mat], None],
116
+ ) -> None:
117
+ """Start with both keypoints and segmentation."""
118
+ self._run_loop(process_frame, use_keypoints=True, use_segmentation=True)
119
+
120
+ def start_keypoints(
121
+ self,
122
+ process_frame: Callable[[Mat, IKeyPointsDataContext, Mat], None],
123
+ ) -> None:
124
+ """Start with keypoints only."""
125
+ self._run_loop(process_frame, use_keypoints=True, use_segmentation=False)
126
+
127
+ def start_segmentation(
128
+ self,
129
+ process_frame: Callable[[Mat, ISegmentationDataContext, Mat], None],
130
+ ) -> None:
131
+ """Start with segmentation only."""
132
+ self._run_loop(process_frame, use_keypoints=False, use_segmentation=True)
133
+
134
+ def _run_loop(
135
+ self,
136
+ process_frame: Callable[..., None],
137
+ use_keypoints: bool,
138
+ use_segmentation: bool,
139
+ ) -> None:
140
+ """Run processing loop."""
141
+ from rocket_welder_sdk.keypoints_protocol import KeyPointsSink
142
+
143
+ logger.info(
144
+ "Starting processing loop (keypoints=%s, segmentation=%s)",
145
+ use_keypoints,
146
+ use_segmentation,
147
+ )
148
+
149
+ # Initialize sinks
150
+ if use_keypoints:
151
+ cs = self._options.keypoints
152
+ logger.info("Initializing keypoints sink: %s -> %s", cs.protocol, cs.address)
153
+ self._keypoints_frame_sink = self._create_frame_sink(cs.protocol, cs.address)
154
+ self._keypoints_sink = KeyPointsSink(
155
+ frame_sink=self._keypoints_frame_sink,
156
+ master_frame_interval=cs.master_frame_interval,
157
+ owns_sink=False, # We manage frame sink lifecycle in close()
158
+ )
159
+ logger.debug(
160
+ "KeyPointsSink created with master_frame_interval=%d", cs.master_frame_interval
161
+ )
162
+
163
+ if use_segmentation:
164
+ seg_cs = self._options.segmentation
165
+ logger.info("Initializing segmentation sink: %s -> %s", seg_cs.protocol, seg_cs.address)
166
+ self._segmentation_frame_sink = self._create_frame_sink(seg_cs.protocol, seg_cs.address)
167
+ logger.debug("Segmentation frame sink created")
168
+
169
+ # TODO: Video capture loop - for now raise NotImplementedError
170
+ raise NotImplementedError(
171
+ "Video capture not implemented. Use process_frame_sync() or low-level API."
172
+ )
173
+
174
+ def process_frame_sync(
175
+ self,
176
+ frame_id: int,
177
+ input_frame: Mat,
178
+ output_frame: Mat,
179
+ width: int,
180
+ height: int,
181
+ ) -> tuple[Optional[IKeyPointsDataContext], Optional[ISegmentationDataContext]]:
182
+ """
183
+ Process a single frame synchronously.
184
+
185
+ Returns (keypoints_context, segmentation_context) for the caller to use.
186
+ Caller must call commit() on contexts when done.
187
+ """
188
+ from rocket_welder_sdk.segmentation_result import SegmentationResultWriter
189
+
190
+ kp_ctx: Optional[IKeyPointsDataContext] = None
191
+ seg_ctx: Optional[ISegmentationDataContext] = None
192
+
193
+ if self._keypoints_sink is not None:
194
+ kp_writer = self._keypoints_sink.create_writer(frame_id)
195
+ kp_ctx = KeyPointsDataContext(frame_id, kp_writer)
196
+
197
+ if self._segmentation_frame_sink is not None:
198
+ seg_writer = SegmentationResultWriter(
199
+ frame_id, width, height, frame_sink=self._segmentation_frame_sink
200
+ )
201
+ seg_ctx = SegmentationDataContext(frame_id, seg_writer)
202
+
203
+ return kp_ctx, seg_ctx
204
+
205
+ def _create_frame_sink(self, protocol: Any, address: str) -> IFrameSink:
206
+ """Create frame sink from protocol."""
207
+ from rocket_welder_sdk.transport import NngFrameSink
208
+ from rocket_welder_sdk.transport.stream_transport import StreamFrameSink
209
+ from rocket_welder_sdk.transport.unix_socket_transport import UnixSocketFrameSink
210
+
211
+ from .transport_protocol import TransportProtocol
212
+
213
+ if not isinstance(protocol, TransportProtocol):
214
+ raise TypeError(f"Expected TransportProtocol, got {type(protocol)}")
215
+
216
+ if protocol.kind == TransportKind.FILE:
217
+ logger.debug("Creating file sink: %s", address)
218
+ file_handle = open(address, "wb")
219
+ try:
220
+ return StreamFrameSink(file_handle)
221
+ except Exception:
222
+ file_handle.close()
223
+ raise
224
+ elif protocol.kind == TransportKind.SOCKET:
225
+ logger.debug("Creating Unix socket sink: %s", address)
226
+ return UnixSocketFrameSink.connect(address)
227
+ elif protocol.kind in (TransportKind.NNG_PUSH_IPC, TransportKind.NNG_PUSH_TCP):
228
+ logger.debug("Creating NNG pusher: %s", address)
229
+ return NngFrameSink.create_pusher(address)
230
+ elif protocol.kind in (TransportKind.NNG_PUB_IPC, TransportKind.NNG_PUB_TCP):
231
+ logger.debug("Creating NNG publisher: %s", address)
232
+ return NngFrameSink.create_publisher(address)
233
+ else:
234
+ raise ValueError(f"Unsupported protocol: {protocol}")
235
+
236
+ def close(self) -> None:
237
+ """Release resources."""
238
+ if self._closed:
239
+ return
240
+
241
+ logger.info("Closing RocketWelderClient")
242
+
243
+ # Close frame sinks (KeyPointsSink has owns_sink=False, so we manage lifecycle)
244
+ self._keypoints_sink = None
245
+ if self._keypoints_frame_sink is not None:
246
+ logger.debug("Closing keypoints frame sink")
247
+ self._keypoints_frame_sink.close()
248
+ self._keypoints_frame_sink = None
249
+
250
+ if self._segmentation_frame_sink is not None:
251
+ logger.debug("Closing segmentation frame sink")
252
+ self._segmentation_frame_sink.close()
253
+ self._segmentation_frame_sink = None
254
+
255
+ self._closed = True
256
+ logger.info("RocketWelderClient closed")
257
+
258
+ def __enter__(self) -> RocketWelderClient:
259
+ return self
260
+
261
+ def __exit__(self, *args: object) -> None:
262
+ self.close()
@@ -6,7 +6,8 @@ Connection string format: protocol://path?param1=value1&param2=value2
6
6
  Examples:
7
7
  nng+push+ipc://tmp/keypoints?masterFrameInterval=300
8
8
  nng+pub+tcp://localhost:5555
9
- file://path/to/output.bin
9
+ file:///path/to/output.bin
10
+ socket:///tmp/my.sock
10
11
  """
11
12
 
12
13
  from __future__ import annotations
@@ -144,19 +145,19 @@ class KeyPointsConnectionString:
144
145
  """
145
146
  Strongly-typed connection string for KeyPoints output.
146
147
 
147
- Supported protocols (composable with + operator):
148
- - Transport.Nng + Transport.Push + Transport.Ipc → nng+push+ipc://tmp/keypoints
149
- - Transport.Nng + Transport.Push + Transport.Tcp → nng+push+tcp://host:port
150
- - file://path/to/file.bin - File output
148
+ Supported protocols:
149
+ - file:///path/to/file.bin - File output (absolute path)
150
+ - socket:///tmp/socket.sock - Unix domain socket
151
+ - nng+push+ipc://tmp/keypoints - NNG Push over IPC
152
+ - nng+push+tcp://host:port - NNG Push over TCP
151
153
 
152
154
  Supported parameters:
153
155
  - masterFrameInterval: Interval between master frames (default: 300)
154
156
  """
155
157
 
156
158
  value: str
157
- protocol: Optional[TransportProtocol] = None
158
- is_file: bool = False
159
- address: str = ""
159
+ protocol: TransportProtocol
160
+ address: str
160
161
  master_frame_interval: int = 300
161
162
  parameters: Dict[str, str] = field(default_factory=dict)
162
163
 
@@ -199,25 +200,26 @@ class KeyPointsConnectionString:
199
200
 
200
201
  # Parse protocol and address
201
202
  scheme_end = endpoint_part.find("://")
202
- if scheme_end > 0:
203
- protocol_str = endpoint_part[:scheme_end]
204
- path_part = endpoint_part[scheme_end + 3 :] # skip "://"
205
-
206
- if protocol_str.lower() == "file":
207
- address = "/" + path_part # Restore absolute path
208
- is_file = True
209
- protocol = None
210
- else:
211
- protocol = TransportProtocol.try_parse(protocol_str)
212
- if protocol is None:
213
- return None
214
- address = protocol.create_nng_address(path_part)
215
- is_file = False
216
- elif s.startswith("/"):
217
- # Assume absolute file path
218
- address = s
219
- is_file = True
220
- protocol = None
203
+ if scheme_end <= 0:
204
+ return None
205
+
206
+ schema_str = endpoint_part[:scheme_end]
207
+ path_part = endpoint_part[scheme_end + 3 :] # skip "://"
208
+
209
+ protocol = TransportProtocol.try_parse(schema_str)
210
+ if protocol is None:
211
+ return None
212
+
213
+ # Build address based on protocol type
214
+ if protocol.is_file:
215
+ # file:///absolute/path -> /absolute/path
216
+ address = path_part if path_part.startswith("/") else "/" + path_part
217
+ elif protocol.is_socket:
218
+ # socket:///tmp/sock -> /tmp/sock
219
+ address = path_part if path_part.startswith("/") else "/" + path_part
220
+ elif protocol.is_nng:
221
+ # NNG protocols need proper address format
222
+ address = protocol.create_nng_address(path_part)
221
223
  else:
222
224
  return None
223
225
 
@@ -230,7 +232,6 @@ class KeyPointsConnectionString:
230
232
  return cls(
231
233
  value=s,
232
234
  protocol=protocol,
233
- is_file=is_file,
234
235
  address=address,
235
236
  master_frame_interval=master_frame_interval,
236
237
  parameters=parameters,
@@ -245,16 +246,16 @@ class SegmentationConnectionString:
245
246
  """
246
247
  Strongly-typed connection string for Segmentation output.
247
248
 
248
- Supported protocols (composable with + operator):
249
- - Transport.Nng + Transport.Push + Transport.Ipc → nng+push+ipc://tmp/segmentation
250
- - Transport.Nng + Transport.Push + Transport.Tcp → nng+push+tcp://host:port
251
- - file://path/to/file.bin - File output
249
+ Supported protocols:
250
+ - file:///path/to/file.bin - File output (absolute path)
251
+ - socket:///tmp/socket.sock - Unix domain socket
252
+ - nng+push+ipc://tmp/segmentation - NNG Push over IPC
253
+ - nng+push+tcp://host:port - NNG Push over TCP
252
254
  """
253
255
 
254
256
  value: str
255
- protocol: Optional[TransportProtocol] = None
256
- is_file: bool = False
257
- address: str = ""
257
+ protocol: TransportProtocol
258
+ address: str
258
259
  parameters: Dict[str, str] = field(default_factory=dict)
259
260
 
260
261
  @classmethod
@@ -296,32 +297,32 @@ class SegmentationConnectionString:
296
297
 
297
298
  # Parse protocol and address
298
299
  scheme_end = endpoint_part.find("://")
299
- if scheme_end > 0:
300
- protocol_str = endpoint_part[:scheme_end]
301
- path_part = endpoint_part[scheme_end + 3 :] # skip "://"
302
-
303
- if protocol_str.lower() == "file":
304
- address = "/" + path_part # Restore absolute path
305
- is_file = True
306
- protocol = None
307
- else:
308
- protocol = TransportProtocol.try_parse(protocol_str)
309
- if protocol is None:
310
- return None
311
- address = protocol.create_nng_address(path_part)
312
- is_file = False
313
- elif s.startswith("/"):
314
- # Assume absolute file path
315
- address = s
316
- is_file = True
317
- protocol = None
300
+ if scheme_end <= 0:
301
+ return None
302
+
303
+ schema_str = endpoint_part[:scheme_end]
304
+ path_part = endpoint_part[scheme_end + 3 :] # skip "://"
305
+
306
+ protocol = TransportProtocol.try_parse(schema_str)
307
+ if protocol is None:
308
+ return None
309
+
310
+ # Build address based on protocol type
311
+ if protocol.is_file:
312
+ # file:///absolute/path -> /absolute/path
313
+ address = path_part if path_part.startswith("/") else "/" + path_part
314
+ elif protocol.is_socket:
315
+ # socket:///tmp/sock -> /tmp/sock
316
+ address = path_part if path_part.startswith("/") else "/" + path_part
317
+ elif protocol.is_nng:
318
+ # NNG protocols need proper address format
319
+ address = protocol.create_nng_address(path_part)
318
320
  else:
319
321
  return None
320
322
 
321
323
  return cls(
322
324
  value=s,
323
325
  protocol=protocol,
324
- is_file=is_file,
325
326
  address=address,
326
327
  parameters=parameters,
327
328
  )
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
17
17
  from rocket_welder_sdk.keypoints_protocol import IKeyPointsWriter
18
18
  from rocket_welder_sdk.segmentation_result import SegmentationResultWriter
19
19
 
20
- from .schema import KeyPoint, SegmentClass
20
+ from .schema import KeyPointDefinition, SegmentClass
21
21
 
22
22
  # Type aliases
23
23
  Point = Tuple[int, int]
@@ -37,12 +37,12 @@ class IKeyPointsDataContext(ABC):
37
37
  pass
38
38
 
39
39
  @abstractmethod
40
- def add(self, point: KeyPoint, x: int, y: int, confidence: float) -> None:
40
+ def add(self, point: KeyPointDefinition, x: int, y: int, confidence: float) -> None:
41
41
  """
42
42
  Add a keypoint detection for this frame.
43
43
 
44
44
  Args:
45
- point: KeyPoint from schema definition
45
+ point: KeyPointDefinition from schema definition
46
46
  x: X coordinate in pixels
47
47
  y: Y coordinate in pixels
48
48
  confidence: Detection confidence (0.0 to 1.0)
@@ -50,17 +50,22 @@ class IKeyPointsDataContext(ABC):
50
50
  pass
51
51
 
52
52
  @abstractmethod
53
- def add_point(self, point: KeyPoint, position: Point, confidence: float) -> None:
53
+ def add_point(self, point: KeyPointDefinition, position: Point, confidence: float) -> None:
54
54
  """
55
55
  Add a keypoint detection using a Point tuple.
56
56
 
57
57
  Args:
58
- point: KeyPoint from schema definition
58
+ point: KeyPointDefinition from schema definition
59
59
  position: (x, y) tuple
60
60
  confidence: Detection confidence (0.0 to 1.0)
61
61
  """
62
62
  pass
63
63
 
64
+ @abstractmethod
65
+ def commit(self) -> None:
66
+ """Commit the context (called automatically when delegate returns)."""
67
+ pass
68
+
64
69
 
65
70
  class ISegmentationDataContext(ABC):
66
71
  """
@@ -92,6 +97,11 @@ class ISegmentationDataContext(ABC):
92
97
  """
93
98
  pass
94
99
 
100
+ @abstractmethod
101
+ def commit(self) -> None:
102
+ """Commit the context (called automatically when delegate returns)."""
103
+ pass
104
+
95
105
 
96
106
  class KeyPointsDataContext(IKeyPointsDataContext):
97
107
  """Implementation of keypoints data context."""
@@ -101,8 +111,6 @@ class KeyPointsDataContext(IKeyPointsDataContext):
101
111
  frame_id: int,
102
112
  writer: IKeyPointsWriter,
103
113
  ) -> None:
104
- from .schema import KeyPoint # noqa: F401
105
-
106
114
  self._frame_id = frame_id
107
115
  self._writer = writer
108
116
 
@@ -110,11 +118,11 @@ class KeyPointsDataContext(IKeyPointsDataContext):
110
118
  def frame_id(self) -> int:
111
119
  return self._frame_id
112
120
 
113
- def add(self, point: KeyPoint, x: int, y: int, confidence: float) -> None:
121
+ def add(self, point: KeyPointDefinition, x: int, y: int, confidence: float) -> None:
114
122
  """Add a keypoint detection for this frame."""
115
123
  self._writer.append(point.id, x, y, confidence)
116
124
 
117
- def add_point(self, point: KeyPoint, position: Point, confidence: float) -> None:
125
+ def add_point(self, point: KeyPointDefinition, position: Point, confidence: float) -> None:
118
126
  """Add a keypoint detection using a Point tuple."""
119
127
  self._writer.append_point(point.id, position, confidence)
120
128
 
@@ -131,8 +139,6 @@ class SegmentationDataContext(ISegmentationDataContext):
131
139
  frame_id: int,
132
140
  writer: SegmentationResultWriter,
133
141
  ) -> None:
134
- from .schema import SegmentClass # noqa: F401
135
-
136
142
  self._frame_id = frame_id
137
143
  self._writer = writer
138
144
 
@@ -10,11 +10,11 @@ from __future__ import annotations
10
10
  import json
11
11
  from abc import ABC, abstractmethod
12
12
  from dataclasses import dataclass
13
- from typing import Dict, List
13
+ from typing import Dict, List, Any
14
14
 
15
15
 
16
16
  @dataclass(frozen=True)
17
- class KeyPoint:
17
+ class KeyPointDefinition:
18
18
  """
19
19
  A keypoint definition with ID and name.
20
20
 
@@ -26,7 +26,7 @@ class KeyPoint:
26
26
  name: str
27
27
 
28
28
  def __str__(self) -> str:
29
- return f"KeyPoint({self.id}, '{self.name}')"
29
+ return f"KeyPointDefinition({self.id}, '{self.name}')"
30
30
 
31
31
 
32
32
  @dataclass(frozen=True)
@@ -54,7 +54,7 @@ class IKeyPointsSchema(ABC):
54
54
  """
55
55
 
56
56
  @abstractmethod
57
- def define_point(self, name: str) -> KeyPoint:
57
+ def define_point(self, name: str) -> KeyPointDefinition:
58
58
  """
59
59
  Define a new keypoint.
60
60
 
@@ -62,13 +62,13 @@ class IKeyPointsSchema(ABC):
62
62
  name: Human-readable name for the keypoint (e.g., "nose", "left_eye")
63
63
 
64
64
  Returns:
65
- KeyPoint handle for use with IKeyPointsDataContext.add()
65
+ KeyPointDefinition handle for use with IKeyPointsDataContext.add()
66
66
  """
67
67
  pass
68
68
 
69
69
  @property
70
70
  @abstractmethod
71
- def defined_points(self) -> List[KeyPoint]:
71
+ def defined_points(self) -> List[KeyPointDefinition]:
72
72
  """Get all defined keypoints."""
73
73
  pass
74
74
 
@@ -116,34 +116,41 @@ class KeyPointsSchema(IKeyPointsSchema):
116
116
  """Implementation of keypoints schema."""
117
117
 
118
118
  def __init__(self) -> None:
119
- self._points: Dict[str, KeyPoint] = {}
119
+ self._points: Dict[str, KeyPointDefinition] = {}
120
120
  self._next_id = 0
121
121
 
122
- def define_point(self, name: str) -> KeyPoint:
122
+ def define_point(self, name: str) -> KeyPointDefinition:
123
123
  """Define a new keypoint."""
124
124
  if name in self._points:
125
125
  raise ValueError(f"Keypoint '{name}' already defined")
126
126
 
127
- point = KeyPoint(id=self._next_id, name=name)
127
+ point = KeyPointDefinition(id=self._next_id, name=name)
128
128
  self._points[name] = point
129
129
  self._next_id += 1
130
130
  return point
131
131
 
132
132
  @property
133
- def defined_points(self) -> List[KeyPoint]:
133
+ def defined_points(self) -> List[KeyPointDefinition]:
134
134
  """Get all defined keypoints."""
135
135
  return list(self._points.values())
136
136
 
137
137
  def get_metadata_json(self) -> str:
138
- """Get JSON metadata for serialization."""
139
- return json.dumps(
140
- {
141
- "version": "1.0",
142
- "compute_module_name": "",
143
- "points": {p.name: p.id for p in self._points.values()},
144
- },
145
- indent=2,
146
- )
138
+ """
139
+ Get JSON metadata for serialization.
140
+
141
+ Format matches C# SDK:
142
+ {
143
+ "version": 1,
144
+ "type": "keypoints",
145
+ "points": [{"id": 0, "name": "nose"}, ...]
146
+ }
147
+ """
148
+ metadata: Dict[str, Any] = {
149
+ "version": 1,
150
+ "type": "keypoints",
151
+ "points": [{"id": p.id, "name": p.name} for p in self._points.values()],
152
+ }
153
+ return json.dumps(metadata, indent=2)
147
154
 
148
155
 
149
156
  class SegmentationSchema(ISegmentationSchema):
@@ -170,11 +177,21 @@ class SegmentationSchema(ISegmentationSchema):
170
177
  return list(self._classes.values())
171
178
 
172
179
  def get_metadata_json(self) -> str:
173
- """Get JSON metadata for serialization."""
174
- return json.dumps(
175
- {
176
- "version": "1.0",
177
- "classes": {str(c.class_id): c.name for c in self._classes.values()},
178
- },
179
- indent=2,
180
- )
180
+ """
181
+ Get JSON metadata for serialization.
182
+
183
+ Format matches C# SDK:
184
+ {
185
+ "version": 1,
186
+ "type": "segmentation",
187
+ "classes": [{"classId": 1, "name": "person"}, ...]
188
+ }
189
+ """
190
+ metadata: Dict[str, Any] = {
191
+ "version": 1,
192
+ "type": "segmentation",
193
+ "classes": [
194
+ {"classId": c.class_id, "name": c.name} for c in self._classes.values()
195
+ ],
196
+ }
197
+ return json.dumps(metadata, indent=2)
@@ -1,103 +1,204 @@
1
1
  """
2
- Transport protocol types with composable + operator.
2
+ Unified transport protocol as a value type.
3
3
 
4
- Allows building transport protocols like:
5
- protocol = Transport.Nng + Transport.Push + Transport.Ipc
6
- # Results in TransportProtocol("nng", "push", "ipc")
4
+ Supports: file://, socket://, nng+push+ipc://, nng+push+tcp://, etc.
5
+
6
+ Examples:
7
+ file:///home/user/output.bin - absolute file path
8
+ socket:///tmp/my.sock - Unix domain socket
9
+ nng+push+ipc://tmp/keypoints - NNG Push over IPC
10
+ nng+push+tcp://host:5555 - NNG Push over TCP
7
11
  """
8
12
 
9
13
  from __future__ import annotations
10
14
 
11
- from dataclasses import dataclass
12
- from typing import Optional
15
+ from enum import Enum, auto
16
+ from typing import ClassVar, Dict, Optional
13
17
 
14
18
 
15
- @dataclass(frozen=True)
16
- class MessagingLibrary:
17
- """Messaging library (nng, zeromq, etc.)."""
19
+ class TransportKind(Enum):
20
+ """Transport kind enumeration."""
18
21
 
19
- name: str
22
+ FILE = auto()
23
+ """File output."""
20
24
 
21
- def __add__(self, pattern: MessagingPattern) -> TransportBuilder:
22
- """Compose with messaging pattern: Nng + Push."""
23
- return TransportBuilder(library=self, pattern=pattern)
25
+ SOCKET = auto()
26
+ """Unix domain socket (direct, no messaging library)."""
24
27
 
25
- def __str__(self) -> str:
26
- return self.name
28
+ NNG_PUSH_IPC = auto()
29
+ """NNG Push over IPC."""
27
30
 
31
+ NNG_PUSH_TCP = auto()
32
+ """NNG Push over TCP."""
28
33
 
29
- @dataclass(frozen=True)
30
- class MessagingPattern:
31
- """Messaging pattern (push/pull, pub/sub, etc.)."""
34
+ NNG_PULL_IPC = auto()
35
+ """NNG Pull over IPC."""
32
36
 
33
- name: str
37
+ NNG_PULL_TCP = auto()
38
+ """NNG Pull over TCP."""
34
39
 
35
- def __str__(self) -> str:
36
- return self.name
40
+ NNG_PUB_IPC = auto()
41
+ """NNG Pub over IPC."""
37
42
 
43
+ NNG_PUB_TCP = auto()
44
+ """NNG Pub over TCP."""
38
45
 
39
- @dataclass(frozen=True)
40
- class TransportLayer:
41
- """Transport layer (ipc, tcp, etc.)."""
46
+ NNG_SUB_IPC = auto()
47
+ """NNG Sub over IPC."""
42
48
 
43
- name: str
44
- uri_prefix: str
49
+ NNG_SUB_TCP = auto()
50
+ """NNG Sub over TCP."""
45
51
 
46
- def __str__(self) -> str:
47
- return self.name
48
52
 
53
+ class TransportProtocol:
54
+ """
55
+ Unified transport protocol specification as a value type.
56
+
57
+ Supports: file://, socket://, nng+push+ipc://, nng+push+tcp://, etc.
58
+ """
59
+
60
+ # Predefined protocols
61
+ File: TransportProtocol
62
+ Socket: TransportProtocol
63
+ NngPushIpc: TransportProtocol
64
+ NngPushTcp: TransportProtocol
65
+ NngPullIpc: TransportProtocol
66
+ NngPullTcp: TransportProtocol
67
+ NngPubIpc: TransportProtocol
68
+ NngPubTcp: TransportProtocol
69
+ NngSubIpc: TransportProtocol
70
+ NngSubTcp: TransportProtocol
71
+
72
+ _SCHEMA_MAP: ClassVar[Dict[str, TransportKind]] = {
73
+ "file": TransportKind.FILE,
74
+ "socket": TransportKind.SOCKET,
75
+ "nng+push+ipc": TransportKind.NNG_PUSH_IPC,
76
+ "nng+push+tcp": TransportKind.NNG_PUSH_TCP,
77
+ "nng+pull+ipc": TransportKind.NNG_PULL_IPC,
78
+ "nng+pull+tcp": TransportKind.NNG_PULL_TCP,
79
+ "nng+pub+ipc": TransportKind.NNG_PUB_IPC,
80
+ "nng+pub+tcp": TransportKind.NNG_PUB_TCP,
81
+ "nng+sub+ipc": TransportKind.NNG_SUB_IPC,
82
+ "nng+sub+tcp": TransportKind.NNG_SUB_TCP,
83
+ }
84
+
85
+ _KIND_TO_SCHEMA: ClassVar[Dict[TransportKind, str]] = {}
86
+
87
+ def __init__(self, kind: TransportKind, schema: str) -> None:
88
+ self._kind = kind
89
+ self._schema = schema
90
+
91
+ @property
92
+ def kind(self) -> TransportKind:
93
+ """The transport kind."""
94
+ return self._kind
49
95
 
50
- @dataclass(frozen=True)
51
- class TransportBuilder:
52
- """Builder for constructing transport protocols."""
96
+ @property
97
+ def schema(self) -> str:
98
+ """The schema string (e.g., 'file', 'socket', 'nng+push+ipc')."""
99
+ return self._schema
53
100
 
54
- library: MessagingLibrary
55
- pattern: MessagingPattern
101
+ # Classification properties
56
102
 
57
- def __add__(self, layer: TransportLayer) -> TransportProtocol:
58
- """Compose with transport layer: (Nng + Push) + Ipc."""
59
- return TransportProtocol(library=self.library, pattern=self.pattern, layer=layer)
103
+ @property
104
+ def is_file(self) -> bool:
105
+ """True if this is a file transport."""
106
+ return self._kind == TransportKind.FILE
60
107
 
61
- def __str__(self) -> str:
62
- return f"{self.library}+{self.pattern}"
108
+ @property
109
+ def is_socket(self) -> bool:
110
+ """True if this is a Unix socket transport."""
111
+ return self._kind == TransportKind.SOCKET
63
112
 
113
+ @property
114
+ def is_nng(self) -> bool:
115
+ """True if this is any NNG-based transport."""
116
+ return self._kind in {
117
+ TransportKind.NNG_PUSH_IPC,
118
+ TransportKind.NNG_PUSH_TCP,
119
+ TransportKind.NNG_PULL_IPC,
120
+ TransportKind.NNG_PULL_TCP,
121
+ TransportKind.NNG_PUB_IPC,
122
+ TransportKind.NNG_PUB_TCP,
123
+ TransportKind.NNG_SUB_IPC,
124
+ TransportKind.NNG_SUB_TCP,
125
+ }
64
126
 
65
- @dataclass(frozen=True)
66
- class TransportProtocol:
67
- """Complete transport protocol specification."""
127
+ @property
128
+ def is_push(self) -> bool:
129
+ """True if this is a Push pattern."""
130
+ return self._kind in {TransportKind.NNG_PUSH_IPC, TransportKind.NNG_PUSH_TCP}
131
+
132
+ @property
133
+ def is_pull(self) -> bool:
134
+ """True if this is a Pull pattern."""
135
+ return self._kind in {TransportKind.NNG_PULL_IPC, TransportKind.NNG_PULL_TCP}
136
+
137
+ @property
138
+ def is_pub(self) -> bool:
139
+ """True if this is a Pub pattern."""
140
+ return self._kind in {TransportKind.NNG_PUB_IPC, TransportKind.NNG_PUB_TCP}
68
141
 
69
- library: MessagingLibrary
70
- pattern: MessagingPattern
71
- layer: TransportLayer
142
+ @property
143
+ def is_sub(self) -> bool:
144
+ """True if this is a Sub pattern."""
145
+ return self._kind in {TransportKind.NNG_SUB_IPC, TransportKind.NNG_SUB_TCP}
146
+
147
+ @property
148
+ def is_ipc(self) -> bool:
149
+ """True if this uses IPC layer."""
150
+ return self._kind in {
151
+ TransportKind.NNG_PUSH_IPC,
152
+ TransportKind.NNG_PULL_IPC,
153
+ TransportKind.NNG_PUB_IPC,
154
+ TransportKind.NNG_SUB_IPC,
155
+ }
72
156
 
73
157
  @property
74
- def protocol_string(self) -> str:
75
- """Protocol string for parsing (e.g., 'nng+push+ipc')."""
76
- return f"{self.library}+{self.pattern}+{self.layer}"
158
+ def is_tcp(self) -> bool:
159
+ """True if this uses TCP layer."""
160
+ return self._kind in {
161
+ TransportKind.NNG_PUSH_TCP,
162
+ TransportKind.NNG_PULL_TCP,
163
+ TransportKind.NNG_PUB_TCP,
164
+ TransportKind.NNG_SUB_TCP,
165
+ }
77
166
 
78
167
  def create_nng_address(self, path_or_host: str) -> str:
79
168
  """
80
169
  Create the NNG address from a path/host.
81
170
 
82
- For IPC: adds leading "/" to make absolute path
83
- For TCP: uses as-is
171
+ For IPC: ipc:///path
172
+ For TCP: tcp://host:port
173
+
174
+ Raises:
175
+ ValueError: If this is not an NNG protocol.
84
176
  """
85
- if self.layer == Transport.Ipc and not path_or_host.startswith("/"):
86
- return f"{self.layer.uri_prefix}/{path_or_host}"
87
- return f"{self.layer.uri_prefix}{path_or_host}"
177
+ if not self.is_nng:
178
+ raise ValueError(f"Cannot create NNG address for {self._kind} transport")
88
179
 
89
- @property
90
- def is_push(self) -> bool:
91
- """Check if this is a push pattern."""
92
- return self.pattern == Transport.Push
180
+ if self.is_ipc:
181
+ # IPC paths need leading "/" for absolute paths
182
+ if not path_or_host.startswith("/"):
183
+ return f"ipc:///{path_or_host}"
184
+ return f"ipc://{path_or_host}"
93
185
 
94
- @property
95
- def is_pub(self) -> bool:
96
- """Check if this is a pub pattern."""
97
- return self.pattern == Transport.Pub
186
+ # TCP
187
+ return f"tcp://{path_or_host}"
98
188
 
99
189
  def __str__(self) -> str:
100
- return self.protocol_string
190
+ return self._schema
191
+
192
+ def __repr__(self) -> str:
193
+ return f"TransportProtocol({self._kind.name}, '{self._schema}')"
194
+
195
+ def __eq__(self, other: object) -> bool:
196
+ if isinstance(other, TransportProtocol):
197
+ return self._kind == other._kind
198
+ return False
199
+
200
+ def __hash__(self) -> int:
201
+ return hash(self._kind)
101
202
 
102
203
  @classmethod
103
204
  def parse(cls, s: str) -> TransportProtocol:
@@ -108,59 +209,30 @@ class TransportProtocol:
108
209
  return result
109
210
 
110
211
  @classmethod
111
- def try_parse(cls, s: str) -> Optional[TransportProtocol]:
212
+ def try_parse(cls, s: Optional[str]) -> Optional[TransportProtocol]:
112
213
  """Try to parse a protocol string."""
113
214
  if not s:
114
215
  return None
115
216
 
116
- parts = s.lower().split("+")
117
- if len(parts) != 3:
118
- return None
119
-
120
- # Parse library
121
- if parts[0] == "nng":
122
- library = Transport.Nng
123
- else:
124
- return None
125
-
126
- # Parse pattern
127
- if parts[1] == "push":
128
- pattern = Transport.Push
129
- elif parts[1] == "pull":
130
- pattern = Transport.Pull
131
- elif parts[1] == "pub":
132
- pattern = Transport.Pub
133
- elif parts[1] == "sub":
134
- pattern = Transport.Sub
135
- else:
217
+ schema = s.lower().strip()
218
+ kind = cls._SCHEMA_MAP.get(schema)
219
+ if kind is None:
136
220
  return None
137
221
 
138
- # Parse layer
139
- if parts[2] == "ipc":
140
- layer = Transport.Ipc
141
- elif parts[2] == "tcp":
142
- layer = Transport.Tcp
143
- else:
144
- return None
145
-
146
- return cls(library=library, pattern=pattern, layer=layer)
147
-
148
-
149
- class Transport:
150
- """Static helpers for building transport protocols using + operator."""
151
-
152
- # Messaging libraries
153
- Nng: MessagingLibrary = MessagingLibrary("nng")
222
+ return cls(kind, schema)
154
223
 
155
- # Messaging patterns
156
- Push: MessagingPattern = MessagingPattern("push")
157
- Pull: MessagingPattern = MessagingPattern("pull")
158
- Pub: MessagingPattern = MessagingPattern("pub")
159
- Sub: MessagingPattern = MessagingPattern("sub")
160
224
 
161
- # Transport layers
162
- Ipc: TransportLayer = TransportLayer("ipc", "ipc://")
163
- Tcp: TransportLayer = TransportLayer("tcp", "tcp://")
225
+ # Initialize predefined protocols
226
+ TransportProtocol.File = TransportProtocol(TransportKind.FILE, "file")
227
+ TransportProtocol.Socket = TransportProtocol(TransportKind.SOCKET, "socket")
228
+ TransportProtocol.NngPushIpc = TransportProtocol(TransportKind.NNG_PUSH_IPC, "nng+push+ipc")
229
+ TransportProtocol.NngPushTcp = TransportProtocol(TransportKind.NNG_PUSH_TCP, "nng+push+tcp")
230
+ TransportProtocol.NngPullIpc = TransportProtocol(TransportKind.NNG_PULL_IPC, "nng+pull+ipc")
231
+ TransportProtocol.NngPullTcp = TransportProtocol(TransportKind.NNG_PULL_TCP, "nng+pull+tcp")
232
+ TransportProtocol.NngPubIpc = TransportProtocol(TransportKind.NNG_PUB_IPC, "nng+pub+ipc")
233
+ TransportProtocol.NngPubTcp = TransportProtocol(TransportKind.NNG_PUB_TCP, "nng+pub+tcp")
234
+ TransportProtocol.NngSubIpc = TransportProtocol(TransportKind.NNG_SUB_IPC, "nng+sub+ipc")
235
+ TransportProtocol.NngSubTcp = TransportProtocol(TransportKind.NNG_SUB_TCP, "nng+sub+tcp")
164
236
 
165
- # File output (not a real transport)
166
- File: str = "file"
237
+ # Initialize reverse lookup map
238
+ TransportProtocol._KIND_TO_SCHEMA = {v: k for k, v in TransportProtocol._SCHEMA_MAP.items()}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rocket-welder-sdk
3
- Version: 1.1.34.dev9
3
+ Version: 1.1.34.dev11
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
@@ -1,8 +1,8 @@
1
1
  rocket_welder_sdk/__init__.py,sha256=pcYOCqqYuK8oJwkFRnWej4uNfEkuYWXP6jcUhPSLgrc,3041
2
2
  rocket_welder_sdk/bytes_size.py,sha256=Myl29-wyWCIYdbMmgaxXebT8Dz8_Fwcr3fnfaNW81P0,7463
3
3
  rocket_welder_sdk/connection_string.py,sha256=NIC6OiOXF-DeBFCWzgMFOWsenrSS45hY81j_HLMSpgo,9974
4
- rocket_welder_sdk/controllers.py,sha256=WY3fgUUrlicA_ibDhvsfsF0ucD7RuoHuL-BWojAhjtQ,32692
5
- rocket_welder_sdk/frame_metadata.py,sha256=8AbCgSQ17QeCHSzdQiSN0E1KxE5rrNpOlTYZ2em8kes,3903
4
+ rocket_welder_sdk/controllers.py,sha256=tPfQ2GZRfLbj-M21uAa9X3cFzrBBvqSbxTUxPiaZ5xc,32690
5
+ rocket_welder_sdk/frame_metadata.py,sha256=TMLIY47cIdIlxqk9xj7I3M8FZFmZ3GcVoLZht7prjQM,3929
6
6
  rocket_welder_sdk/gst_metadata.py,sha256=jEQvZX4BdR6OR3lqp12PV-HEXZhcxfiS010diA2CbMM,14213
7
7
  rocket_welder_sdk/keypoints_protocol.py,sha256=NKiSPrevWG4_RrD6jtFxPjwftlaPWe1CqoFVKRMwp4k,21858
8
8
  rocket_welder_sdk/opencv_controller.py,sha256=MDM6_yFBB9BaMa5jnZRqw7xZZB-WuLr7EPrrfHQ2DK4,9905
@@ -14,11 +14,12 @@ rocket_welder_sdk/session_id.py,sha256=sRhzQw90shqq_DJVtrsSggcGZ775kz7cRfbI-1LMe
14
14
  rocket_welder_sdk/external_controls/__init__.py,sha256=ldOLGhLLS5BQL8m4VKFYV0SvsNNlV2tghlc7rkqadU8,699
15
15
  rocket_welder_sdk/external_controls/contracts.py,sha256=3DU6pdpteN50gF2fsS7C2279dGjDa0tZLrLntkBa2LM,2607
16
16
  rocket_welder_sdk/external_controls/contracts_old.py,sha256=XWriuXJZu5caTSS0bcTIOZcKnj-IRCm96voA4gqLBfU,2980
17
- rocket_welder_sdk/high_level/__init__.py,sha256=5oTCBL2qMGonCytHDckmX1b1U97-7Xb8Bg9Z70nR7fc,1759
18
- rocket_welder_sdk/high_level/connection_strings.py,sha256=4undnkbWZ837vY-o6ybIj1827F2VlUbCJX4m3Filz-s,10469
19
- rocket_welder_sdk/high_level/data_context.py,sha256=Pmwsl9MgBfKA9BqmBJmAVRvnaqPdjnq2rZEA_pzzYsw,4585
20
- rocket_welder_sdk/high_level/schema.py,sha256=UlefNAV2UL9eO_Th2q19a7tf_eoaeLE92fYZ5LNq7-M,4849
21
- rocket_welder_sdk/high_level/transport_protocol.py,sha256=lvLVHikW_MNZhXqaXlyXscT68OklyMbGs8DnDEMCtgE,4515
17
+ rocket_welder_sdk/high_level/__init__.py,sha256=C1Gan74Ce_JQZKJmXhpP3L3O11wvOClesBVaAxRZQaA,1305
18
+ rocket_welder_sdk/high_level/client.py,sha256=kVtxpzP9qZgv-SYG_0glDckHapEod4MFl5NQ75TR_b4,9584
19
+ rocket_welder_sdk/high_level/connection_strings.py,sha256=q1uZJQ7mt1RR-E8MJzIwG6vz3Ddruoc3pTCdTvg_pe4,10434
20
+ rocket_welder_sdk/high_level/data_context.py,sha256=SXJvDpDBFi8Lm4XqSRSHK7YUUHuugXGo4ZRCb6_z5l0,4833
21
+ rocket_welder_sdk/high_level/schema.py,sha256=7F5DfPACHUfOpbJE4YZJRpro9VL8tLa6Kt9ZJuY7R7w,5397
22
+ rocket_welder_sdk/high_level/transport_protocol.py,sha256=EFF0bgNn9hxRMj67FwU6MVu-UiEFINSGhd2VC8agrgc,7393
22
23
  rocket_welder_sdk/transport/__init__.py,sha256=DYmZpohGPU7RhS6EdVT_BwCy5MZzyTQ6Eymm8TpmxJ8,751
23
24
  rocket_welder_sdk/transport/frame_sink.py,sha256=16dUefZF1QJv62Ig0ezPR6nEho_7A3WJu4M9_PPMqJM,2164
24
25
  rocket_welder_sdk/transport/frame_source.py,sha256=G1rBAQS1AgOOdtASB0_CYon8g20hUGXpP2exCp5hlhk,2169
@@ -32,7 +33,7 @@ rocket_welder_sdk/ui/icons.py,sha256=DcDklZkPmiEzlOD4IR7VTJOtGPCuuh_OM_WN7ScghWE
32
33
  rocket_welder_sdk/ui/ui_events_projection.py,sha256=siiNhjLEBOPfTKw1ZhOPGkwIN5rLDH7V9VCZTNrhEtQ,7836
33
34
  rocket_welder_sdk/ui/ui_service.py,sha256=uRdpyJGoCQmtOli_HKSrxLwhZYG-XRuHIYdkmFz1zNk,12026
34
35
  rocket_welder_sdk/ui/value_types.py,sha256=f7OA_9zgXEDPoITc8v8SfAR23I4XeFhE3E2_GcAbR6k,1616
35
- rocket_welder_sdk-1.1.34.dev9.dist-info/METADATA,sha256=W0yjZbx2vDuVpY1JQMQUiTbJmvG6wbwETaYVE8wnfYc,24852
36
- rocket_welder_sdk-1.1.34.dev9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
- rocket_welder_sdk-1.1.34.dev9.dist-info/top_level.txt,sha256=2iZvBjnwVCUW-uDE23-eJld5PZ9-mlPI69QiXM5IrTA,18
38
- rocket_welder_sdk-1.1.34.dev9.dist-info/RECORD,,
36
+ rocket_welder_sdk-1.1.34.dev11.dist-info/METADATA,sha256=coA5aGcozUnouyo6OlrAoFNkDXnqBMS1sxU5d9BE1p8,24853
37
+ rocket_welder_sdk-1.1.34.dev11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
+ rocket_welder_sdk-1.1.34.dev11.dist-info/top_level.txt,sha256=2iZvBjnwVCUW-uDE23-eJld5PZ9-mlPI69QiXM5IrTA,18
39
+ rocket_welder_sdk-1.1.34.dev11.dist-info/RECORD,,