rocket-welder-sdk 1.1.34__py3-none-any.whl → 1.1.34.dev7__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/controllers.py +1 -1
- rocket_welder_sdk/frame_metadata.py +2 -2
- rocket_welder_sdk/high_level/__init__.py +25 -11
- rocket_welder_sdk/high_level/connection_strings.py +55 -56
- rocket_welder_sdk/high_level/data_context.py +11 -17
- rocket_welder_sdk/high_level/schema.py +27 -44
- rocket_welder_sdk/high_level/transport_protocol.py +108 -180
- {rocket_welder_sdk-1.1.34.dist-info → rocket_welder_sdk-1.1.34.dev7.dist-info}/METADATA +1 -1
- {rocket_welder_sdk-1.1.34.dist-info → rocket_welder_sdk-1.1.34.dev7.dist-info}/RECORD +11 -12
- rocket_welder_sdk/high_level/client.py +0 -262
- {rocket_welder_sdk-1.1.34.dist-info → rocket_welder_sdk-1.1.34.dev7.dist-info}/WHEEL +0 -0
- {rocket_welder_sdk-1.1.34.dist-info → rocket_welder_sdk-1.1.34.dev7.dist-info}/top_level.txt +0 -0
rocket_welder_sdk/controllers.py
CHANGED
|
@@ -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 (
|
|
341
|
+
[FrameMetadata (16 bytes)][Pixel Data (W×H×C 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
|
|
21
|
+
from typing import 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:
|
|
116
|
+
_FORMAT_NAMES: dict[int, str] = {
|
|
117
117
|
0: "UNKNOWN",
|
|
118
118
|
2: "I420",
|
|
119
119
|
3: "YV12",
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
"""
|
|
2
2
|
High-level API for RocketWelder SDK.
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
Provides a simplified, user-friendly API for common video processing workflows
|
|
5
|
+
with automatic transport management and schema definitions.
|
|
5
6
|
|
|
6
7
|
Example:
|
|
7
|
-
from rocket_welder_sdk.high_level import RocketWelderClient
|
|
8
|
+
from rocket_welder_sdk.high_level import RocketWelderClient, Transport
|
|
8
9
|
|
|
9
|
-
with RocketWelderClient.from_environment() as client:
|
|
10
|
+
async with RocketWelderClient.from_environment() as client:
|
|
11
|
+
# Define keypoints schema
|
|
10
12
|
nose = client.keypoints.define_point("nose")
|
|
13
|
+
left_eye = client.keypoints.define_point("left_eye")
|
|
14
|
+
|
|
15
|
+
# Define segmentation classes
|
|
11
16
|
person = client.segmentation.define_class(1, "person")
|
|
12
|
-
|
|
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)
|
|
13
22
|
"""
|
|
14
23
|
|
|
15
|
-
from .client import RocketWelderClient, RocketWelderClientOptions
|
|
16
24
|
from .connection_strings import (
|
|
17
25
|
KeyPointsConnectionString,
|
|
18
26
|
SegmentationConnectionString,
|
|
@@ -26,11 +34,15 @@ from .data_context import (
|
|
|
26
34
|
from .schema import (
|
|
27
35
|
IKeyPointsSchema,
|
|
28
36
|
ISegmentationSchema,
|
|
29
|
-
|
|
37
|
+
KeyPoint,
|
|
30
38
|
SegmentClass,
|
|
31
39
|
)
|
|
32
40
|
from .transport_protocol import (
|
|
33
|
-
|
|
41
|
+
MessagingLibrary,
|
|
42
|
+
MessagingPattern,
|
|
43
|
+
Transport,
|
|
44
|
+
TransportBuilder,
|
|
45
|
+
TransportLayer,
|
|
34
46
|
TransportProtocol,
|
|
35
47
|
)
|
|
36
48
|
|
|
@@ -39,13 +51,15 @@ __all__ = [
|
|
|
39
51
|
"IKeyPointsSchema",
|
|
40
52
|
"ISegmentationDataContext",
|
|
41
53
|
"ISegmentationSchema",
|
|
42
|
-
"
|
|
54
|
+
"KeyPoint",
|
|
43
55
|
"KeyPointsConnectionString",
|
|
44
|
-
"
|
|
45
|
-
"
|
|
56
|
+
"MessagingLibrary",
|
|
57
|
+
"MessagingPattern",
|
|
46
58
|
"SegmentClass",
|
|
47
59
|
"SegmentationConnectionString",
|
|
48
|
-
"
|
|
60
|
+
"Transport",
|
|
61
|
+
"TransportBuilder",
|
|
62
|
+
"TransportLayer",
|
|
49
63
|
"TransportProtocol",
|
|
50
64
|
"VideoSourceConnectionString",
|
|
51
65
|
"VideoSourceType",
|
|
@@ -6,8 +6,7 @@ Connection string format: protocol://path?param1=value1¶m2=value2
|
|
|
6
6
|
Examples:
|
|
7
7
|
nng+push+ipc://tmp/keypoints?masterFrameInterval=300
|
|
8
8
|
nng+pub+tcp://localhost:5555
|
|
9
|
-
file
|
|
10
|
-
socket:///tmp/my.sock
|
|
9
|
+
file://path/to/output.bin
|
|
11
10
|
"""
|
|
12
11
|
|
|
13
12
|
from __future__ import annotations
|
|
@@ -145,19 +144,19 @@ class KeyPointsConnectionString:
|
|
|
145
144
|
"""
|
|
146
145
|
Strongly-typed connection string for KeyPoints output.
|
|
147
146
|
|
|
148
|
-
Supported protocols:
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
- nng+push+tcp://host:port - NNG Push over TCP
|
|
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
|
|
153
151
|
|
|
154
152
|
Supported parameters:
|
|
155
153
|
- masterFrameInterval: Interval between master frames (default: 300)
|
|
156
154
|
"""
|
|
157
155
|
|
|
158
156
|
value: str
|
|
159
|
-
protocol: TransportProtocol
|
|
160
|
-
|
|
157
|
+
protocol: Optional[TransportProtocol] = None
|
|
158
|
+
is_file: bool = False
|
|
159
|
+
address: str = ""
|
|
161
160
|
master_frame_interval: int = 300
|
|
162
161
|
parameters: Dict[str, str] = field(default_factory=dict)
|
|
163
162
|
|
|
@@ -200,26 +199,25 @@ class KeyPointsConnectionString:
|
|
|
200
199
|
|
|
201
200
|
# Parse protocol and address
|
|
202
201
|
scheme_end = endpoint_part.find("://")
|
|
203
|
-
if scheme_end
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
elif
|
|
218
|
-
#
|
|
219
|
-
address =
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
address = protocol.create_nng_address(path_part)
|
|
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
|
|
223
221
|
else:
|
|
224
222
|
return None
|
|
225
223
|
|
|
@@ -232,6 +230,7 @@ class KeyPointsConnectionString:
|
|
|
232
230
|
return cls(
|
|
233
231
|
value=s,
|
|
234
232
|
protocol=protocol,
|
|
233
|
+
is_file=is_file,
|
|
235
234
|
address=address,
|
|
236
235
|
master_frame_interval=master_frame_interval,
|
|
237
236
|
parameters=parameters,
|
|
@@ -246,16 +245,16 @@ class SegmentationConnectionString:
|
|
|
246
245
|
"""
|
|
247
246
|
Strongly-typed connection string for Segmentation output.
|
|
248
247
|
|
|
249
|
-
Supported protocols:
|
|
250
|
-
-
|
|
251
|
-
-
|
|
252
|
-
-
|
|
253
|
-
- nng+push+tcp://host:port - NNG Push over TCP
|
|
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
|
|
254
252
|
"""
|
|
255
253
|
|
|
256
254
|
value: str
|
|
257
|
-
protocol: TransportProtocol
|
|
258
|
-
|
|
255
|
+
protocol: Optional[TransportProtocol] = None
|
|
256
|
+
is_file: bool = False
|
|
257
|
+
address: str = ""
|
|
259
258
|
parameters: Dict[str, str] = field(default_factory=dict)
|
|
260
259
|
|
|
261
260
|
@classmethod
|
|
@@ -297,32 +296,32 @@ class SegmentationConnectionString:
|
|
|
297
296
|
|
|
298
297
|
# Parse protocol and address
|
|
299
298
|
scheme_end = endpoint_part.find("://")
|
|
300
|
-
if scheme_end
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
elif
|
|
315
|
-
#
|
|
316
|
-
address =
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
address = protocol.create_nng_address(path_part)
|
|
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
|
|
320
318
|
else:
|
|
321
319
|
return None
|
|
322
320
|
|
|
323
321
|
return cls(
|
|
324
322
|
value=s,
|
|
325
323
|
protocol=protocol,
|
|
324
|
+
is_file=is_file,
|
|
326
325
|
address=address,
|
|
327
326
|
parameters=parameters,
|
|
328
327
|
)
|
|
@@ -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
|
|
20
|
+
from .schema import KeyPoint, 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:
|
|
40
|
+
def add(self, point: KeyPoint, 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:
|
|
45
|
+
point: KeyPoint 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,22 +50,17 @@ class IKeyPointsDataContext(ABC):
|
|
|
50
50
|
pass
|
|
51
51
|
|
|
52
52
|
@abstractmethod
|
|
53
|
-
def add_point(self, point:
|
|
53
|
+
def add_point(self, point: KeyPoint, position: Point, confidence: float) -> None:
|
|
54
54
|
"""
|
|
55
55
|
Add a keypoint detection using a Point tuple.
|
|
56
56
|
|
|
57
57
|
Args:
|
|
58
|
-
point:
|
|
58
|
+
point: KeyPoint 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
|
-
|
|
69
64
|
|
|
70
65
|
class ISegmentationDataContext(ABC):
|
|
71
66
|
"""
|
|
@@ -97,11 +92,6 @@ class ISegmentationDataContext(ABC):
|
|
|
97
92
|
"""
|
|
98
93
|
pass
|
|
99
94
|
|
|
100
|
-
@abstractmethod
|
|
101
|
-
def commit(self) -> None:
|
|
102
|
-
"""Commit the context (called automatically when delegate returns)."""
|
|
103
|
-
pass
|
|
104
|
-
|
|
105
95
|
|
|
106
96
|
class KeyPointsDataContext(IKeyPointsDataContext):
|
|
107
97
|
"""Implementation of keypoints data context."""
|
|
@@ -111,6 +101,8 @@ class KeyPointsDataContext(IKeyPointsDataContext):
|
|
|
111
101
|
frame_id: int,
|
|
112
102
|
writer: IKeyPointsWriter,
|
|
113
103
|
) -> None:
|
|
104
|
+
from .schema import KeyPoint # noqa: F401
|
|
105
|
+
|
|
114
106
|
self._frame_id = frame_id
|
|
115
107
|
self._writer = writer
|
|
116
108
|
|
|
@@ -118,11 +110,11 @@ class KeyPointsDataContext(IKeyPointsDataContext):
|
|
|
118
110
|
def frame_id(self) -> int:
|
|
119
111
|
return self._frame_id
|
|
120
112
|
|
|
121
|
-
def add(self, point:
|
|
113
|
+
def add(self, point: KeyPoint, x: int, y: int, confidence: float) -> None:
|
|
122
114
|
"""Add a keypoint detection for this frame."""
|
|
123
115
|
self._writer.append(point.id, x, y, confidence)
|
|
124
116
|
|
|
125
|
-
def add_point(self, point:
|
|
117
|
+
def add_point(self, point: KeyPoint, position: Point, confidence: float) -> None:
|
|
126
118
|
"""Add a keypoint detection using a Point tuple."""
|
|
127
119
|
self._writer.append_point(point.id, position, confidence)
|
|
128
120
|
|
|
@@ -139,6 +131,8 @@ class SegmentationDataContext(ISegmentationDataContext):
|
|
|
139
131
|
frame_id: int,
|
|
140
132
|
writer: SegmentationResultWriter,
|
|
141
133
|
) -> None:
|
|
134
|
+
from .schema import SegmentClass # noqa: F401
|
|
135
|
+
|
|
142
136
|
self._frame_id = frame_id
|
|
143
137
|
self._writer = writer
|
|
144
138
|
|
|
@@ -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
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
@dataclass(frozen=True)
|
|
17
|
-
class
|
|
17
|
+
class KeyPoint:
|
|
18
18
|
"""
|
|
19
19
|
A keypoint definition with ID and name.
|
|
20
20
|
|
|
@@ -26,7 +26,7 @@ class KeyPointDefinition:
|
|
|
26
26
|
name: str
|
|
27
27
|
|
|
28
28
|
def __str__(self) -> str:
|
|
29
|
-
return f"
|
|
29
|
+
return f"KeyPoint({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) ->
|
|
57
|
+
def define_point(self, name: str) -> KeyPoint:
|
|
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
|
-
|
|
65
|
+
KeyPoint 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[
|
|
71
|
+
def defined_points(self) -> List[KeyPoint]:
|
|
72
72
|
"""Get all defined keypoints."""
|
|
73
73
|
pass
|
|
74
74
|
|
|
@@ -116,41 +116,34 @@ class KeyPointsSchema(IKeyPointsSchema):
|
|
|
116
116
|
"""Implementation of keypoints schema."""
|
|
117
117
|
|
|
118
118
|
def __init__(self) -> None:
|
|
119
|
-
self._points: Dict[str,
|
|
119
|
+
self._points: Dict[str, KeyPoint] = {}
|
|
120
120
|
self._next_id = 0
|
|
121
121
|
|
|
122
|
-
def define_point(self, name: str) ->
|
|
122
|
+
def define_point(self, name: str) -> KeyPoint:
|
|
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 =
|
|
127
|
+
point = KeyPoint(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[
|
|
133
|
+
def defined_points(self) -> List[KeyPoint]:
|
|
134
134
|
"""Get all defined keypoints."""
|
|
135
135
|
return list(self._points.values())
|
|
136
136
|
|
|
137
137
|
def get_metadata_json(self) -> str:
|
|
138
|
-
"""
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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)
|
|
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
|
+
)
|
|
154
147
|
|
|
155
148
|
|
|
156
149
|
class SegmentationSchema(ISegmentationSchema):
|
|
@@ -177,21 +170,11 @@ class SegmentationSchema(ISegmentationSchema):
|
|
|
177
170
|
return list(self._classes.values())
|
|
178
171
|
|
|
179
172
|
def get_metadata_json(self) -> str:
|
|
180
|
-
"""
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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)
|
|
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
|
+
)
|
|
@@ -1,204 +1,103 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Transport protocol types with composable + operator.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
|
4
|
+
Allows building transport protocols like:
|
|
5
|
+
protocol = Transport.Nng + Transport.Push + Transport.Ipc
|
|
6
|
+
# Results in TransportProtocol("nng", "push", "ipc")
|
|
11
7
|
"""
|
|
12
8
|
|
|
13
9
|
from __future__ import annotations
|
|
14
10
|
|
|
15
|
-
from
|
|
16
|
-
from typing import
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class TransportKind(Enum):
|
|
20
|
-
"""Transport kind enumeration."""
|
|
21
|
-
|
|
22
|
-
FILE = auto()
|
|
23
|
-
"""File output."""
|
|
24
|
-
|
|
25
|
-
SOCKET = auto()
|
|
26
|
-
"""Unix domain socket (direct, no messaging library)."""
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Optional
|
|
27
13
|
|
|
28
|
-
NNG_PUSH_IPC = auto()
|
|
29
|
-
"""NNG Push over IPC."""
|
|
30
14
|
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class MessagingLibrary:
|
|
17
|
+
"""Messaging library (nng, zeromq, etc.)."""
|
|
33
18
|
|
|
34
|
-
|
|
35
|
-
"""NNG Pull over IPC."""
|
|
19
|
+
name: str
|
|
36
20
|
|
|
37
|
-
|
|
38
|
-
|
|
21
|
+
def __add__(self, pattern: MessagingPattern) -> TransportBuilder:
|
|
22
|
+
"""Compose with messaging pattern: Nng + Push."""
|
|
23
|
+
return TransportBuilder(library=self, pattern=pattern)
|
|
39
24
|
|
|
40
|
-
|
|
41
|
-
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
return self.name
|
|
42
27
|
|
|
43
|
-
NNG_PUB_TCP = auto()
|
|
44
|
-
"""NNG Pub over TCP."""
|
|
45
28
|
|
|
46
|
-
|
|
47
|
-
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class MessagingPattern:
|
|
31
|
+
"""Messaging pattern (push/pull, pub/sub, etc.)."""
|
|
48
32
|
|
|
49
|
-
|
|
50
|
-
"""NNG Sub over TCP."""
|
|
33
|
+
name: str
|
|
51
34
|
|
|
35
|
+
def __str__(self) -> str:
|
|
36
|
+
return self.name
|
|
52
37
|
|
|
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
38
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return self._kind
|
|
39
|
+
@dataclass(frozen=True)
|
|
40
|
+
class TransportLayer:
|
|
41
|
+
"""Transport layer (ipc, tcp, etc.)."""
|
|
95
42
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
"""The schema string (e.g., 'file', 'socket', 'nng+push+ipc')."""
|
|
99
|
-
return self._schema
|
|
43
|
+
name: str
|
|
44
|
+
uri_prefix: str
|
|
100
45
|
|
|
101
|
-
|
|
46
|
+
def __str__(self) -> str:
|
|
47
|
+
return self.name
|
|
102
48
|
|
|
103
|
-
@property
|
|
104
|
-
def is_file(self) -> bool:
|
|
105
|
-
"""True if this is a file transport."""
|
|
106
|
-
return self._kind == TransportKind.FILE
|
|
107
49
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return self._kind == TransportKind.SOCKET
|
|
50
|
+
@dataclass(frozen=True)
|
|
51
|
+
class TransportBuilder:
|
|
52
|
+
"""Builder for constructing transport protocols."""
|
|
112
53
|
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
}
|
|
54
|
+
library: MessagingLibrary
|
|
55
|
+
pattern: MessagingPattern
|
|
126
56
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return self._kind in {TransportKind.NNG_PUSH_IPC, TransportKind.NNG_PUSH_TCP}
|
|
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)
|
|
131
60
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
"""True if this is a Pull pattern."""
|
|
135
|
-
return self._kind in {TransportKind.NNG_PULL_IPC, TransportKind.NNG_PULL_TCP}
|
|
61
|
+
def __str__(self) -> str:
|
|
62
|
+
return f"{self.library}+{self.pattern}"
|
|
136
63
|
|
|
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}
|
|
141
64
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return self._kind in {TransportKind.NNG_SUB_IPC, TransportKind.NNG_SUB_TCP}
|
|
65
|
+
@dataclass(frozen=True)
|
|
66
|
+
class TransportProtocol:
|
|
67
|
+
"""Complete transport protocol specification."""
|
|
146
68
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
}
|
|
69
|
+
library: MessagingLibrary
|
|
70
|
+
pattern: MessagingPattern
|
|
71
|
+
layer: TransportLayer
|
|
156
72
|
|
|
157
73
|
@property
|
|
158
|
-
def
|
|
159
|
-
"""
|
|
160
|
-
return self.
|
|
161
|
-
TransportKind.NNG_PUSH_TCP,
|
|
162
|
-
TransportKind.NNG_PULL_TCP,
|
|
163
|
-
TransportKind.NNG_PUB_TCP,
|
|
164
|
-
TransportKind.NNG_SUB_TCP,
|
|
165
|
-
}
|
|
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}"
|
|
166
77
|
|
|
167
78
|
def create_nng_address(self, path_or_host: str) -> str:
|
|
168
79
|
"""
|
|
169
80
|
Create the NNG address from a path/host.
|
|
170
81
|
|
|
171
|
-
For IPC:
|
|
172
|
-
For TCP:
|
|
173
|
-
|
|
174
|
-
Raises:
|
|
175
|
-
ValueError: If this is not an NNG protocol.
|
|
82
|
+
For IPC: adds leading "/" to make absolute path
|
|
83
|
+
For TCP: uses as-is
|
|
176
84
|
"""
|
|
177
|
-
if not
|
|
178
|
-
|
|
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}"
|
|
179
88
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return f"ipc://{path_or_host}"
|
|
89
|
+
@property
|
|
90
|
+
def is_push(self) -> bool:
|
|
91
|
+
"""Check if this is a push pattern."""
|
|
92
|
+
return self.pattern == Transport.Push
|
|
185
93
|
|
|
186
|
-
|
|
187
|
-
|
|
94
|
+
@property
|
|
95
|
+
def is_pub(self) -> bool:
|
|
96
|
+
"""Check if this is a pub pattern."""
|
|
97
|
+
return self.pattern == Transport.Pub
|
|
188
98
|
|
|
189
99
|
def __str__(self) -> str:
|
|
190
|
-
return self.
|
|
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)
|
|
100
|
+
return self.protocol_string
|
|
202
101
|
|
|
203
102
|
@classmethod
|
|
204
103
|
def parse(cls, s: str) -> TransportProtocol:
|
|
@@ -209,30 +108,59 @@ class TransportProtocol:
|
|
|
209
108
|
return result
|
|
210
109
|
|
|
211
110
|
@classmethod
|
|
212
|
-
def try_parse(cls, s:
|
|
111
|
+
def try_parse(cls, s: str) -> Optional[TransportProtocol]:
|
|
213
112
|
"""Try to parse a protocol string."""
|
|
214
113
|
if not s:
|
|
215
114
|
return None
|
|
216
115
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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:
|
|
220
136
|
return None
|
|
221
137
|
|
|
222
|
-
|
|
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")
|
|
223
154
|
|
|
155
|
+
# Messaging patterns
|
|
156
|
+
Push: MessagingPattern = MessagingPattern("push")
|
|
157
|
+
Pull: MessagingPattern = MessagingPattern("pull")
|
|
158
|
+
Pub: MessagingPattern = MessagingPattern("pub")
|
|
159
|
+
Sub: MessagingPattern = MessagingPattern("sub")
|
|
224
160
|
|
|
225
|
-
#
|
|
226
|
-
|
|
227
|
-
|
|
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")
|
|
161
|
+
# Transport layers
|
|
162
|
+
Ipc: TransportLayer = TransportLayer("ipc", "ipc://")
|
|
163
|
+
Tcp: TransportLayer = TransportLayer("tcp", "tcp://")
|
|
236
164
|
|
|
237
|
-
#
|
|
238
|
-
|
|
165
|
+
# File output (not a real transport)
|
|
166
|
+
File: str = "file"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rocket-welder-sdk
|
|
3
|
-
Version: 1.1.34
|
|
3
|
+
Version: 1.1.34.dev7
|
|
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=
|
|
5
|
-
rocket_welder_sdk/frame_metadata.py,sha256=
|
|
4
|
+
rocket_welder_sdk/controllers.py,sha256=WY3fgUUrlicA_ibDhvsfsF0ucD7RuoHuL-BWojAhjtQ,32692
|
|
5
|
+
rocket_welder_sdk/frame_metadata.py,sha256=8AbCgSQ17QeCHSzdQiSN0E1KxE5rrNpOlTYZ2em8kes,3903
|
|
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,12 +14,11 @@ 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=
|
|
18
|
-
rocket_welder_sdk/high_level/
|
|
19
|
-
rocket_welder_sdk/high_level/
|
|
20
|
-
rocket_welder_sdk/high_level/
|
|
21
|
-
rocket_welder_sdk/high_level/
|
|
22
|
-
rocket_welder_sdk/high_level/transport_protocol.py,sha256=EFF0bgNn9hxRMj67FwU6MVu-UiEFINSGhd2VC8agrgc,7393
|
|
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
|
|
23
22
|
rocket_welder_sdk/transport/__init__.py,sha256=DYmZpohGPU7RhS6EdVT_BwCy5MZzyTQ6Eymm8TpmxJ8,751
|
|
24
23
|
rocket_welder_sdk/transport/frame_sink.py,sha256=16dUefZF1QJv62Ig0ezPR6nEho_7A3WJu4M9_PPMqJM,2164
|
|
25
24
|
rocket_welder_sdk/transport/frame_source.py,sha256=G1rBAQS1AgOOdtASB0_CYon8g20hUGXpP2exCp5hlhk,2169
|
|
@@ -33,7 +32,7 @@ rocket_welder_sdk/ui/icons.py,sha256=DcDklZkPmiEzlOD4IR7VTJOtGPCuuh_OM_WN7ScghWE
|
|
|
33
32
|
rocket_welder_sdk/ui/ui_events_projection.py,sha256=siiNhjLEBOPfTKw1ZhOPGkwIN5rLDH7V9VCZTNrhEtQ,7836
|
|
34
33
|
rocket_welder_sdk/ui/ui_service.py,sha256=uRdpyJGoCQmtOli_HKSrxLwhZYG-XRuHIYdkmFz1zNk,12026
|
|
35
34
|
rocket_welder_sdk/ui/value_types.py,sha256=f7OA_9zgXEDPoITc8v8SfAR23I4XeFhE3E2_GcAbR6k,1616
|
|
36
|
-
rocket_welder_sdk-1.1.34.dist-info/METADATA,sha256=
|
|
37
|
-
rocket_welder_sdk-1.1.34.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
38
|
-
rocket_welder_sdk-1.1.34.dist-info/top_level.txt,sha256=2iZvBjnwVCUW-uDE23-eJld5PZ9-mlPI69QiXM5IrTA,18
|
|
39
|
-
rocket_welder_sdk-1.1.34.dist-info/RECORD,,
|
|
35
|
+
rocket_welder_sdk-1.1.34.dev7.dist-info/METADATA,sha256=qUlyc_-dz2TRBVvqqV6N38UvFF9Gb6r-Vw1PsqppeYw,24852
|
|
36
|
+
rocket_welder_sdk-1.1.34.dev7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
37
|
+
rocket_welder_sdk-1.1.34.dev7.dist-info/top_level.txt,sha256=2iZvBjnwVCUW-uDE23-eJld5PZ9-mlPI69QiXM5IrTA,18
|
|
38
|
+
rocket_welder_sdk-1.1.34.dev7.dist-info/RECORD,,
|
|
@@ -1,262 +0,0 @@
|
|
|
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()
|
|
File without changes
|
{rocket_welder_sdk-1.1.34.dist-info → rocket_welder_sdk-1.1.34.dev7.dist-info}/top_level.txt
RENAMED
|
File without changes
|