rocket-welder-sdk 1.1.36.dev14__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 +95 -0
- rocket_welder_sdk/bytes_size.py +234 -0
- rocket_welder_sdk/connection_string.py +291 -0
- rocket_welder_sdk/controllers.py +831 -0
- rocket_welder_sdk/external_controls/__init__.py +30 -0
- rocket_welder_sdk/external_controls/contracts.py +100 -0
- rocket_welder_sdk/external_controls/contracts_old.py +105 -0
- rocket_welder_sdk/frame_metadata.py +138 -0
- rocket_welder_sdk/gst_metadata.py +411 -0
- rocket_welder_sdk/high_level/__init__.py +54 -0
- rocket_welder_sdk/high_level/client.py +235 -0
- rocket_welder_sdk/high_level/connection_strings.py +331 -0
- rocket_welder_sdk/high_level/data_context.py +169 -0
- rocket_welder_sdk/high_level/frame_sink_factory.py +118 -0
- rocket_welder_sdk/high_level/schema.py +195 -0
- rocket_welder_sdk/high_level/transport_protocol.py +238 -0
- rocket_welder_sdk/keypoints_protocol.py +642 -0
- rocket_welder_sdk/opencv_controller.py +278 -0
- rocket_welder_sdk/periodic_timer.py +303 -0
- rocket_welder_sdk/py.typed +2 -0
- rocket_welder_sdk/rocket_welder_client.py +497 -0
- rocket_welder_sdk/segmentation_result.py +420 -0
- rocket_welder_sdk/session_id.py +238 -0
- rocket_welder_sdk/transport/__init__.py +31 -0
- rocket_welder_sdk/transport/frame_sink.py +122 -0
- rocket_welder_sdk/transport/frame_source.py +74 -0
- rocket_welder_sdk/transport/nng_transport.py +197 -0
- rocket_welder_sdk/transport/stream_transport.py +193 -0
- rocket_welder_sdk/transport/tcp_transport.py +154 -0
- rocket_welder_sdk/transport/unix_socket_transport.py +339 -0
- rocket_welder_sdk/ui/__init__.py +48 -0
- rocket_welder_sdk/ui/controls.py +362 -0
- rocket_welder_sdk/ui/icons.py +21628 -0
- rocket_welder_sdk/ui/ui_events_projection.py +226 -0
- rocket_welder_sdk/ui/ui_service.py +358 -0
- rocket_welder_sdk/ui/value_types.py +72 -0
- rocket_welder_sdk-1.1.36.dev14.dist-info/METADATA +845 -0
- rocket_welder_sdk-1.1.36.dev14.dist-info/RECORD +40 -0
- rocket_welder_sdk-1.1.36.dev14.dist-info/WHEEL +5 -0
- rocket_welder_sdk-1.1.36.dev14.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""External Controls module for RocketWelder SDK."""
|
|
2
|
+
|
|
3
|
+
from rocket_welder_sdk.ui.value_types import ControlType
|
|
4
|
+
|
|
5
|
+
from .contracts import (
|
|
6
|
+
ArrowDirection,
|
|
7
|
+
# Events (UI → Container)
|
|
8
|
+
ButtonDown,
|
|
9
|
+
ButtonUp,
|
|
10
|
+
ChangeControls,
|
|
11
|
+
# Commands (Container → UI)
|
|
12
|
+
DefineControl,
|
|
13
|
+
DeleteControl, # Legacy alias - deprecated
|
|
14
|
+
DeleteControls,
|
|
15
|
+
KeyDown,
|
|
16
|
+
KeyUp,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"ArrowDirection",
|
|
21
|
+
"ButtonDown",
|
|
22
|
+
"ButtonUp",
|
|
23
|
+
"ChangeControls",
|
|
24
|
+
"ControlType", # Now using the single ControlType from ui.value_types
|
|
25
|
+
"DefineControl",
|
|
26
|
+
"DeleteControl", # Legacy - deprecated
|
|
27
|
+
"DeleteControls",
|
|
28
|
+
"KeyDown",
|
|
29
|
+
"KeyUp",
|
|
30
|
+
]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""External Controls contracts using Pydantic v2 for modern serialization.
|
|
2
|
+
|
|
3
|
+
This module defines the contracts for external controls communication between
|
|
4
|
+
containers and the RocketWelder UI via EventStore, using Pydantic v2 for
|
|
5
|
+
proper serialization with PascalCase support.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from uuid import UUID, uuid4
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field, field_serializer
|
|
12
|
+
from pydantic.alias_generators import to_pascal
|
|
13
|
+
|
|
14
|
+
from rocket_welder_sdk.ui.value_types import ControlType
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ArrowDirection(str, Enum):
|
|
18
|
+
"""Arrow directions for arrow grid control."""
|
|
19
|
+
|
|
20
|
+
UP = "Up"
|
|
21
|
+
DOWN = "Down"
|
|
22
|
+
LEFT = "Left"
|
|
23
|
+
RIGHT = "Right"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BaseContract(BaseModel):
|
|
27
|
+
"""Base class for all contracts with PascalCase serialization."""
|
|
28
|
+
|
|
29
|
+
model_config = ConfigDict(
|
|
30
|
+
# Convert snake_case fields to PascalCase for serialization
|
|
31
|
+
alias_generator=to_pascal,
|
|
32
|
+
# Allow both snake_case and PascalCase when deserializing
|
|
33
|
+
populate_by_name=True,
|
|
34
|
+
# Use Enum values for serialization
|
|
35
|
+
use_enum_values=True,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
id: UUID = Field(default_factory=uuid4)
|
|
39
|
+
|
|
40
|
+
@field_serializer("id")
|
|
41
|
+
def serialize_id(self, value: UUID) -> str:
|
|
42
|
+
"""Serialize UUID as string for JSON compatibility."""
|
|
43
|
+
return str(value)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Container → UI Commands (Stream: ExternalCommands-{SessionId})
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class DefineControl(BaseContract):
|
|
50
|
+
"""Command to define a new control in the UI."""
|
|
51
|
+
|
|
52
|
+
control_id: str
|
|
53
|
+
type: ControlType
|
|
54
|
+
properties: dict[str, str]
|
|
55
|
+
region_name: str
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class DeleteControls(BaseContract):
|
|
59
|
+
"""Command to delete multiple controls from the UI."""
|
|
60
|
+
|
|
61
|
+
control_ids: list[str]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# Legacy alias for backward compatibility (will be removed)
|
|
65
|
+
DeleteControl = DeleteControls
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ChangeControls(BaseContract):
|
|
69
|
+
"""Command to update properties of multiple controls."""
|
|
70
|
+
|
|
71
|
+
updates: dict[str, dict[str, str]] # ControlId -> {PropertyId -> Value}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# UI → Container Events (Stream: ExternalEvents-{SessionId})
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class ButtonDown(BaseContract):
|
|
78
|
+
"""Event when a button is pressed down."""
|
|
79
|
+
|
|
80
|
+
control_id: str
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class ButtonUp(BaseContract):
|
|
84
|
+
"""Event when a button is released."""
|
|
85
|
+
|
|
86
|
+
control_id: str
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class KeyDown(BaseContract):
|
|
90
|
+
"""Event when a key is pressed down."""
|
|
91
|
+
|
|
92
|
+
control_id: str
|
|
93
|
+
code: str # KeyCode value like "ArrowUp", "Enter", etc.
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class KeyUp(BaseContract):
|
|
97
|
+
"""Event when a key is released."""
|
|
98
|
+
|
|
99
|
+
control_id: str
|
|
100
|
+
code: str # KeyCode value like "ArrowUp", "Enter", etc.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""External Controls event contracts for RocketWelder SDK (legacy - for backward compatibility)."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from uuid import UUID, uuid4
|
|
6
|
+
|
|
7
|
+
from rocket_welder_sdk.ui.value_types import ControlType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ArrowDirection(Enum):
|
|
11
|
+
"""Arrow directions for ArrowGrid control."""
|
|
12
|
+
|
|
13
|
+
UP = "Up"
|
|
14
|
+
DOWN = "Down"
|
|
15
|
+
LEFT = "Left"
|
|
16
|
+
RIGHT = "Right"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Container → UI Commands (Stream: ExternalCommands-{SessionId})
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class DefineControl:
|
|
24
|
+
"""Command to define a new control in the UI."""
|
|
25
|
+
|
|
26
|
+
control_id: str
|
|
27
|
+
type: ControlType
|
|
28
|
+
properties: dict[str, str]
|
|
29
|
+
region_name: str
|
|
30
|
+
id: UUID = field(default_factory=uuid4)
|
|
31
|
+
|
|
32
|
+
def to_dict(self) -> dict[str, object]:
|
|
33
|
+
"""Convert to dictionary for EventStore."""
|
|
34
|
+
return {
|
|
35
|
+
"Id": str(self.id),
|
|
36
|
+
"ControlId": self.control_id,
|
|
37
|
+
"Type": self.type.value,
|
|
38
|
+
"Properties": self.properties,
|
|
39
|
+
"RegionName": self.region_name,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class DeleteControl:
|
|
45
|
+
"""Command to delete a control from the UI."""
|
|
46
|
+
|
|
47
|
+
control_id: str
|
|
48
|
+
id: UUID = field(default_factory=uuid4)
|
|
49
|
+
|
|
50
|
+
def to_dict(self) -> dict[str, str]:
|
|
51
|
+
"""Convert to dictionary for EventStore."""
|
|
52
|
+
return {"Id": str(self.id), "ControlId": self.control_id}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class ChangeControls:
|
|
57
|
+
"""Command to update properties of multiple controls."""
|
|
58
|
+
|
|
59
|
+
updates: dict[str, dict[str, str]] # ControlId -> { PropertyId -> Value }
|
|
60
|
+
id: UUID = field(default_factory=uuid4)
|
|
61
|
+
|
|
62
|
+
def to_dict(self) -> dict[str, object]:
|
|
63
|
+
"""Convert to dictionary for EventStore."""
|
|
64
|
+
return {"Id": str(self.id), "Updates": self.updates}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# UI → Container Events (Stream: ExternalEvents-{SessionId})
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class ButtonDown:
|
|
72
|
+
"""Event when button is pressed."""
|
|
73
|
+
|
|
74
|
+
control_id: str
|
|
75
|
+
id: UUID = field(default_factory=uuid4)
|
|
76
|
+
|
|
77
|
+
def to_dict(self) -> dict[str, str]:
|
|
78
|
+
"""Convert to dictionary for EventStore."""
|
|
79
|
+
return {"Id": str(self.id), "ControlId": self.control_id}
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def from_dict(cls, data: dict[str, object]) -> "ButtonDown":
|
|
83
|
+
"""Create from EventStore data."""
|
|
84
|
+
return cls(
|
|
85
|
+
control_id=str(data["ControlId"]), id=UUID(str(data["Id"])) if "Id" in data else uuid4()
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass
|
|
90
|
+
class ButtonUp:
|
|
91
|
+
"""Event when button is released."""
|
|
92
|
+
|
|
93
|
+
control_id: str
|
|
94
|
+
id: UUID = field(default_factory=uuid4)
|
|
95
|
+
|
|
96
|
+
def to_dict(self) -> dict[str, str]:
|
|
97
|
+
"""Convert to dictionary for EventStore."""
|
|
98
|
+
return {"Id": str(self.id), "ControlId": self.control_id}
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def from_dict(cls, data: dict[str, object]) -> "ButtonUp":
|
|
102
|
+
"""Create from EventStore data."""
|
|
103
|
+
return cls(
|
|
104
|
+
control_id=str(data["ControlId"]), id=UUID(str(data["Id"])) if "Id" in data else uuid4()
|
|
105
|
+
)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Frame metadata structure prepended to each frame in zerobuffer shared memory.
|
|
3
|
+
|
|
4
|
+
This module provides the FrameMetadata dataclass that matches the C++ struct
|
|
5
|
+
defined in frame_metadata.h.
|
|
6
|
+
|
|
7
|
+
Protocol Layout (16 bytes, 8-byte aligned):
|
|
8
|
+
[0-7] frame_number - Sequential frame index (0-based)
|
|
9
|
+
[8-15] timestamp_ns - GStreamer PTS in nanoseconds (UINT64_MAX if unavailable)
|
|
10
|
+
|
|
11
|
+
Note: Width, height, and format are NOT included here because they are
|
|
12
|
+
stream-level properties that never change per-frame. They are stored once
|
|
13
|
+
in the ZeroBuffer metadata section as GstCaps (via GstMetadata).
|
|
14
|
+
This avoids redundant data and follows single-source-of-truth principle.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import struct
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
from typing import ClassVar, Dict, Optional
|
|
22
|
+
|
|
23
|
+
# Size of the FrameMetadata structure in bytes
|
|
24
|
+
FRAME_METADATA_SIZE = 16
|
|
25
|
+
|
|
26
|
+
# Value indicating timestamp is unavailable
|
|
27
|
+
TIMESTAMP_UNAVAILABLE = 0xFFFFFFFFFFFFFFFF # UINT64_MAX
|
|
28
|
+
|
|
29
|
+
# Struct format: little-endian, 2 uint64
|
|
30
|
+
# Q = unsigned long long (8 bytes)
|
|
31
|
+
_FRAME_METADATA_FORMAT = "<QQ"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class FrameMetadata:
|
|
36
|
+
"""
|
|
37
|
+
Frame metadata prepended to each frame in zerobuffer shared memory.
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
frame_number: Sequential frame index (0-based, increments per frame)
|
|
41
|
+
timestamp_ns: GStreamer PTS in nanoseconds (TIMESTAMP_UNAVAILABLE if not set)
|
|
42
|
+
|
|
43
|
+
Note: Width, height, and format come from GstCaps in ZeroBuffer metadata section,
|
|
44
|
+
not from per-frame metadata. This avoids redundant data.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
frame_number: int
|
|
48
|
+
timestamp_ns: int
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def from_bytes(cls, data: bytes | memoryview) -> FrameMetadata:
|
|
52
|
+
"""
|
|
53
|
+
Parse FrameMetadata from raw bytes.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
data: At least 16 bytes of data
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
FrameMetadata instance
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
ValueError: If data is too short
|
|
63
|
+
"""
|
|
64
|
+
if len(data) < FRAME_METADATA_SIZE:
|
|
65
|
+
raise ValueError(f"Data must be at least {FRAME_METADATA_SIZE} bytes, got {len(data)}")
|
|
66
|
+
|
|
67
|
+
# Unpack the struct
|
|
68
|
+
frame_number, timestamp_ns = struct.unpack(
|
|
69
|
+
_FRAME_METADATA_FORMAT, data[:FRAME_METADATA_SIZE]
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
return cls(
|
|
73
|
+
frame_number=frame_number,
|
|
74
|
+
timestamp_ns=timestamp_ns,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def has_timestamp(self) -> bool:
|
|
79
|
+
"""Check if timestamp is available."""
|
|
80
|
+
return self.timestamp_ns != TIMESTAMP_UNAVAILABLE
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def timestamp_ms(self) -> Optional[float]:
|
|
84
|
+
"""Get timestamp in milliseconds, or None if unavailable."""
|
|
85
|
+
if self.has_timestamp:
|
|
86
|
+
return self.timestamp_ns / 1_000_000.0
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
def __str__(self) -> str:
|
|
90
|
+
"""Return string representation."""
|
|
91
|
+
timestamp = f"{self.timestamp_ns / 1_000_000.0:.3f}ms" if self.has_timestamp else "N/A"
|
|
92
|
+
return f"Frame {self.frame_number} @ {timestamp}"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# Common GstVideoFormat values - kept for reference when working with GstCaps
|
|
96
|
+
class GstVideoFormat:
|
|
97
|
+
"""Common GStreamer video format values (for use with GstCaps)."""
|
|
98
|
+
|
|
99
|
+
UNKNOWN = 0
|
|
100
|
+
I420 = 2
|
|
101
|
+
YV12 = 3
|
|
102
|
+
YUY2 = 4
|
|
103
|
+
UYVY = 5
|
|
104
|
+
RGBA = 11
|
|
105
|
+
BGRA = 12
|
|
106
|
+
ARGB = 13
|
|
107
|
+
ABGR = 14
|
|
108
|
+
RGB = 15
|
|
109
|
+
BGR = 16
|
|
110
|
+
NV12 = 23
|
|
111
|
+
NV21 = 24
|
|
112
|
+
GRAY8 = 25
|
|
113
|
+
GRAY16_BE = 26
|
|
114
|
+
GRAY16_LE = 27
|
|
115
|
+
|
|
116
|
+
_FORMAT_NAMES: ClassVar[Dict[int, str]] = {
|
|
117
|
+
0: "UNKNOWN",
|
|
118
|
+
2: "I420",
|
|
119
|
+
3: "YV12",
|
|
120
|
+
4: "YUY2",
|
|
121
|
+
5: "UYVY",
|
|
122
|
+
11: "RGBA",
|
|
123
|
+
12: "BGRA",
|
|
124
|
+
13: "ARGB",
|
|
125
|
+
14: "ABGR",
|
|
126
|
+
15: "RGB",
|
|
127
|
+
16: "BGR",
|
|
128
|
+
23: "NV12",
|
|
129
|
+
24: "NV21",
|
|
130
|
+
25: "GRAY8",
|
|
131
|
+
26: "GRAY16_BE",
|
|
132
|
+
27: "GRAY16_LE",
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def to_string(cls, format_value: int) -> str:
|
|
137
|
+
"""Convert format value to string name."""
|
|
138
|
+
return cls._FORMAT_NAMES.get(format_value, f"FORMAT_{format_value}")
|