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.
Files changed (40) hide show
  1. rocket_welder_sdk/__init__.py +95 -0
  2. rocket_welder_sdk/bytes_size.py +234 -0
  3. rocket_welder_sdk/connection_string.py +291 -0
  4. rocket_welder_sdk/controllers.py +831 -0
  5. rocket_welder_sdk/external_controls/__init__.py +30 -0
  6. rocket_welder_sdk/external_controls/contracts.py +100 -0
  7. rocket_welder_sdk/external_controls/contracts_old.py +105 -0
  8. rocket_welder_sdk/frame_metadata.py +138 -0
  9. rocket_welder_sdk/gst_metadata.py +411 -0
  10. rocket_welder_sdk/high_level/__init__.py +54 -0
  11. rocket_welder_sdk/high_level/client.py +235 -0
  12. rocket_welder_sdk/high_level/connection_strings.py +331 -0
  13. rocket_welder_sdk/high_level/data_context.py +169 -0
  14. rocket_welder_sdk/high_level/frame_sink_factory.py +118 -0
  15. rocket_welder_sdk/high_level/schema.py +195 -0
  16. rocket_welder_sdk/high_level/transport_protocol.py +238 -0
  17. rocket_welder_sdk/keypoints_protocol.py +642 -0
  18. rocket_welder_sdk/opencv_controller.py +278 -0
  19. rocket_welder_sdk/periodic_timer.py +303 -0
  20. rocket_welder_sdk/py.typed +2 -0
  21. rocket_welder_sdk/rocket_welder_client.py +497 -0
  22. rocket_welder_sdk/segmentation_result.py +420 -0
  23. rocket_welder_sdk/session_id.py +238 -0
  24. rocket_welder_sdk/transport/__init__.py +31 -0
  25. rocket_welder_sdk/transport/frame_sink.py +122 -0
  26. rocket_welder_sdk/transport/frame_source.py +74 -0
  27. rocket_welder_sdk/transport/nng_transport.py +197 -0
  28. rocket_welder_sdk/transport/stream_transport.py +193 -0
  29. rocket_welder_sdk/transport/tcp_transport.py +154 -0
  30. rocket_welder_sdk/transport/unix_socket_transport.py +339 -0
  31. rocket_welder_sdk/ui/__init__.py +48 -0
  32. rocket_welder_sdk/ui/controls.py +362 -0
  33. rocket_welder_sdk/ui/icons.py +21628 -0
  34. rocket_welder_sdk/ui/ui_events_projection.py +226 -0
  35. rocket_welder_sdk/ui/ui_service.py +358 -0
  36. rocket_welder_sdk/ui/value_types.py +72 -0
  37. rocket_welder_sdk-1.1.36.dev14.dist-info/METADATA +845 -0
  38. rocket_welder_sdk-1.1.36.dev14.dist-info/RECORD +40 -0
  39. rocket_welder_sdk-1.1.36.dev14.dist-info/WHEEL +5 -0
  40. 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}")