rocket-welder-sdk 1.1.34a2__tar.gz → 1.1.34.dev8__tar.gz

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 (67) hide show
  1. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/PKG-INFO +1 -1
  2. rocket_welder_sdk-1.1.34.dev8/VERSION +1 -0
  3. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/__init__.py +20 -1
  4. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/controllers.py +6 -2
  5. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/rocket_welder_client.py +34 -14
  6. rocket_welder_sdk-1.1.34.dev8/rocket_welder_sdk/session_id.py +238 -0
  7. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/transport/__init__.py +0 -8
  8. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk.egg-info/PKG-INFO +1 -1
  9. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_controllers.py +29 -10
  10. rocket_welder_sdk-1.1.34a2/VERSION +0 -1
  11. rocket_welder_sdk-1.1.34a2/rocket_welder_sdk/session_id.py +0 -115
  12. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/MANIFEST.in +0 -0
  13. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/README.md +0 -0
  14. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/logo.png +0 -0
  15. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/pyproject.toml +0 -0
  16. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/bytes_size.py +0 -0
  17. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/connection_string.py +0 -0
  18. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/external_controls/__init__.py +0 -0
  19. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/external_controls/contracts.py +0 -0
  20. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/external_controls/contracts_old.py +0 -0
  21. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/frame_metadata.py +0 -0
  22. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/gst_metadata.py +0 -0
  23. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/high_level/__init__.py +0 -0
  24. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/high_level/connection_strings.py +0 -0
  25. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/high_level/data_context.py +0 -0
  26. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/high_level/schema.py +0 -0
  27. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/high_level/transport_protocol.py +0 -0
  28. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/keypoints_protocol.py +0 -0
  29. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/opencv_controller.py +0 -0
  30. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/periodic_timer.py +0 -0
  31. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/py.typed +0 -0
  32. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/segmentation_result.py +0 -0
  33. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/transport/frame_sink.py +0 -0
  34. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/transport/frame_source.py +0 -0
  35. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/transport/nng_transport.py +0 -0
  36. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/transport/stream_transport.py +0 -0
  37. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/transport/tcp_transport.py +0 -0
  38. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/transport/unix_socket_transport.py +0 -0
  39. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/ui/__init__.py +0 -0
  40. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/ui/controls.py +0 -0
  41. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/ui/icons.py +0 -0
  42. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/ui/ui_events_projection.py +0 -0
  43. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/ui/ui_service.py +0 -0
  44. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk/ui/value_types.py +0 -0
  45. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk.egg-info/SOURCES.txt +0 -0
  46. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk.egg-info/dependency_links.txt +0 -0
  47. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk.egg-info/requires.txt +0 -0
  48. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/rocket_welder_sdk.egg-info/top_level.txt +0 -0
  49. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/setup.cfg +0 -0
  50. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/setup.py +0 -0
  51. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_bytes_size.py +0 -0
  52. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_connection_string.py +0 -0
  53. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_external_controls_serialization.py +0 -0
  54. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_external_controls_serialization_v2.py +0 -0
  55. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_frame_metadata.py +0 -0
  56. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_gst_metadata.py +0 -0
  57. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_high_level_api.py +0 -0
  58. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_icons.py +0 -0
  59. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_keypoints_cross_platform.py +0 -0
  60. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_keypoints_protocol.py +0 -0
  61. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_rocket_welder_client.py +0 -0
  62. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_segmentation_cross_platform.py +0 -0
  63. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_segmentation_result.py +0 -0
  64. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_session_id.py +0 -0
  65. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_transport_cross_platform.py +0 -0
  66. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_ui_controls.py +0 -0
  67. {rocket_welder_sdk-1.1.34a2 → rocket_welder_sdk-1.1.34.dev8}/tests/test_ui_service_happy_path.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rocket-welder-sdk
3
- Version: 1.1.34a2
3
+ Version: 1.1.34.dev8
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
@@ -0,0 +1 @@
1
+ 1.1.34.dev8
@@ -16,11 +16,22 @@ from .opencv_controller import OpenCvController
16
16
  from .periodic_timer import PeriodicTimer, PeriodicTimerSync
17
17
  from .rocket_welder_client import RocketWelderClient
18
18
  from .session_id import (
19
+ # Explicit URL functions (PREFERRED - set by rocket-welder2)
20
+ ACTIONS_SINK_URL_ENV,
21
+ KEYPOINTS_SINK_URL_ENV,
22
+ SEGMENTATION_SINK_URL_ENV,
23
+ # SessionId-derived URL functions (fallback for backwards compatibility)
19
24
  get_actions_url,
25
+ get_actions_url_from_env,
26
+ get_configured_nng_urls,
20
27
  get_keypoints_url,
28
+ get_keypoints_url_from_env,
21
29
  get_nng_urls,
30
+ get_nng_urls_from_env,
22
31
  get_segmentation_url,
32
+ get_segmentation_url_from_env,
23
33
  get_session_id_from_env,
34
+ has_explicit_nng_urls,
24
35
  parse_session_id,
25
36
  )
26
37
 
@@ -49,7 +60,10 @@ if _log_level:
49
60
  pass # Invalid log level, ignore
50
61
 
51
62
  __all__ = [
63
+ "ACTIONS_SINK_URL_ENV",
52
64
  "FRAME_METADATA_SIZE",
65
+ "KEYPOINTS_SINK_URL_ENV",
66
+ "SEGMENTATION_SINK_URL_ENV",
53
67
  "BytesSize",
54
68
  "Client",
55
69
  "ConnectionMode",
@@ -66,11 +80,16 @@ __all__ = [
66
80
  "PeriodicTimerSync",
67
81
  "Protocol",
68
82
  "RocketWelderClient",
69
- # SessionId utilities for NNG URL generation
70
83
  "get_actions_url",
84
+ "get_actions_url_from_env",
85
+ "get_configured_nng_urls",
71
86
  "get_keypoints_url",
87
+ "get_keypoints_url_from_env",
72
88
  "get_nng_urls",
89
+ "get_nng_urls_from_env",
73
90
  "get_segmentation_url",
91
+ "get_segmentation_url_from_env",
74
92
  "get_session_id_from_env",
93
+ "has_explicit_nng_urls",
75
94
  "parse_session_id",
76
95
  ]
@@ -438,7 +438,9 @@ class OneWayShmController(IController):
438
438
  sqrt_pixels = math.sqrt(pixels)
439
439
  if sqrt_pixels == int(sqrt_pixels):
440
440
  dimension = int(sqrt_pixels)
441
- logger.info(f"Pixel data size {pixel_data_size} suggests {dimension}x{dimension} RGB")
441
+ logger.info(
442
+ f"Pixel data size {pixel_data_size} suggests {dimension}x{dimension} RGB"
443
+ )
442
444
  pixel_data = np.frombuffer(frame.data[FRAME_METADATA_SIZE:], dtype=np.uint8)
443
445
  return pixel_data.reshape((dimension, dimension, 3)) # type: ignore[no-any-return]
444
446
 
@@ -448,7 +450,9 @@ class OneWayShmController(IController):
448
450
  sqrt_pixels = math.sqrt(pixels)
449
451
  if sqrt_pixels == int(sqrt_pixels):
450
452
  dimension = int(sqrt_pixels)
451
- logger.info(f"Pixel data size {pixel_data_size} suggests {dimension}x{dimension} RGBA")
453
+ logger.info(
454
+ f"Pixel data size {pixel_data_size} suggests {dimension}x{dimension} RGBA"
455
+ )
452
456
  pixel_data = np.frombuffer(frame.data[FRAME_METADATA_SIZE:], dtype=np.uint8)
453
457
  return pixel_data.reshape((dimension, dimension, 4)) # type: ignore[no-any-return]
454
458
 
@@ -16,7 +16,11 @@ from .connection_string import ConnectionMode, ConnectionString, Protocol
16
16
  from .controllers import DuplexShmController, IController, OneWayShmController
17
17
  from .frame_metadata import FrameMetadata # noqa: TC001 - used at runtime in callbacks
18
18
  from .opencv_controller import OpenCvController
19
- from .session_id import get_nng_urls, get_session_id_from_env
19
+ from .session_id import (
20
+ get_configured_nng_urls,
21
+ get_nng_urls_from_env,
22
+ has_explicit_nng_urls,
23
+ )
20
24
  from .transport.nng_transport import NngFrameSink
21
25
 
22
26
  if TYPE_CHECKING:
@@ -90,27 +94,33 @@ class RocketWelderClient:
90
94
  """
91
95
  return self._nng_publishers
92
96
 
93
- def _create_nng_publishers(self, session_id: str) -> None:
97
+ def _create_nng_publishers(self) -> None:
94
98
  """Create NNG publishers for result streaming.
95
99
 
96
- Args:
97
- session_id: SessionId string (e.g., "ps-{guid}")
100
+ URLs are read from environment variables (preferred) or derived from SessionId (fallback).
101
+
102
+ Priority:
103
+ 1. Explicit URLs: SEGMENTATION_SINK_URL, KEYPOINTS_SINK_URL, ACTIONS_SINK_URL
104
+ 2. Derived from SessionId environment variable (backwards compatibility)
98
105
  """
99
106
  try:
100
- urls = get_nng_urls(session_id)
107
+ urls = get_configured_nng_urls()
101
108
 
102
109
  for name, url in urls.items():
103
110
  sink = NngFrameSink.create_publisher(url)
104
111
  self._nng_publishers[name] = sink
105
112
  logger.info("NNG publisher ready: %s at %s", name, url)
106
113
 
114
+ # Log configuration summary
107
115
  logger.info(
108
- "NNG publishers created for SessionId=%s: seg=%s, kp=%s, actions=%s",
109
- session_id,
110
- urls["segmentation"],
111
- urls["keypoints"],
112
- urls["actions"],
116
+ "NNG publishers configured: seg=%s, kp=%s, actions=%s",
117
+ urls.get("segmentation", "(not configured)"),
118
+ urls.get("keypoints", "(not configured)"),
119
+ urls.get("actions", "(not configured)"),
113
120
  )
121
+ except ValueError as ex:
122
+ # No URLs configured - this is expected for containers that don't publish results
123
+ logger.debug("NNG publishers not configured: %s", ex)
114
124
  except Exception as ex:
115
125
  logger.warning("Failed to create NNG publishers: %s", ex)
116
126
  # Don't fail start() - NNG is optional for backwards compatibility
@@ -162,10 +172,20 @@ class RocketWelderClient:
162
172
  else:
163
173
  raise ValueError(f"Unsupported protocol: {self._connection.protocol}")
164
174
 
165
- # Auto-create NNG publishers if SessionId env var is set
166
- session_id = get_session_id_from_env()
167
- if session_id:
168
- self._create_nng_publishers(session_id)
175
+ # Auto-create NNG publishers if URLs are configured
176
+ # (explicit URLs via SEGMENTATION_SINK_URL etc., or derived from SessionId)
177
+ if has_explicit_nng_urls():
178
+ self._create_nng_publishers()
179
+ else:
180
+ # Log that NNG is not configured (informational)
181
+ urls = get_nng_urls_from_env()
182
+ logger.info(
183
+ "NNG sink URLs not configured (this is normal if not publishing AI results). "
184
+ "seg=%s, kp=%s, actions=%s",
185
+ urls.get("segmentation") or "(not set)",
186
+ urls.get("keypoints") or "(not set)",
187
+ urls.get("actions") or "(not set)",
188
+ )
169
189
 
170
190
  # If preview is enabled, wrap the callback to capture frames
171
191
  if self._preview_enabled:
@@ -0,0 +1,238 @@
1
+ """SessionId parsing utilities for NNG URL generation.
2
+
3
+ SessionId format: ps-{guid} (e.g., ps-a1b2c3d4-e5f6-7890-abcd-ef1234567890)
4
+ Prefix "ps" = PipelineSession.
5
+
6
+ This module provides utilities to:
7
+ 1. Parse SessionId from environment variable
8
+ 2. Extract the Guid portion
9
+ 3. Generate NNG IPC URLs for streaming results
10
+ 4. Read explicit NNG URLs from environment variables (preferred)
11
+
12
+ ## URL Configuration Priority
13
+
14
+ The SDK supports two ways to configure NNG URLs:
15
+
16
+ 1. **Explicit URLs (PREFERRED)** - Set by rocket-welder2:
17
+ - SEGMENTATION_SINK_URL
18
+ - KEYPOINTS_SINK_URL
19
+ - ACTIONS_SINK_URL
20
+
21
+ 2. **Derived from SessionId (FALLBACK)** - For backwards compatibility:
22
+ - SessionId env var → parse GUID → generate URLs
23
+
24
+ Use `get_nng_urls_from_env()` for explicit URLs (preferred).
25
+ Use `get_nng_urls(session_id)` for SessionId-derived URLs (fallback).
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import logging
31
+ import os
32
+ import uuid
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+ SESSION_ID_PREFIX = "ps-"
37
+ SESSION_ID_ENV_VAR = "SessionId"
38
+
39
+ # Explicit URL environment variables (set by rocket-welder2)
40
+ SEGMENTATION_SINK_URL_ENV = "SEGMENTATION_SINK_URL"
41
+ KEYPOINTS_SINK_URL_ENV = "KEYPOINTS_SINK_URL"
42
+ ACTIONS_SINK_URL_ENV = "ACTIONS_SINK_URL"
43
+
44
+
45
+ def parse_session_id(session_id: str) -> uuid.UUID:
46
+ """Parse SessionId (ps-{guid}) to extract Guid.
47
+
48
+ Args:
49
+ session_id: SessionId string (e.g., "ps-a1b2c3d4-...")
50
+
51
+ Returns:
52
+ UUID extracted from SessionId
53
+
54
+ Raises:
55
+ ValueError: If session_id format is invalid
56
+
57
+ Examples:
58
+ >>> parse_session_id("ps-a1b2c3d4-e5f6-7890-abcd-ef1234567890")
59
+ UUID('a1b2c3d4-e5f6-7890-abcd-ef1234567890')
60
+ >>> parse_session_id("a1b2c3d4-e5f6-7890-abcd-ef1234567890") # backwards compat
61
+ UUID('a1b2c3d4-e5f6-7890-abcd-ef1234567890')
62
+ """
63
+ if session_id.startswith(SESSION_ID_PREFIX):
64
+ return uuid.UUID(session_id[len(SESSION_ID_PREFIX) :])
65
+ # Fallback: try parsing as raw guid for backwards compatibility
66
+ return uuid.UUID(session_id)
67
+
68
+
69
+ def get_session_id_from_env() -> str | None:
70
+ """Get SessionId from environment variable.
71
+
72
+ Returns:
73
+ SessionId string or None if not set
74
+ """
75
+ return os.environ.get(SESSION_ID_ENV_VAR)
76
+
77
+
78
+ def get_nng_urls(session_id: str) -> dict[str, str]:
79
+ """Generate NNG IPC URLs from SessionId.
80
+
81
+ Args:
82
+ session_id: SessionId string (e.g., "ps-a1b2c3d4-...")
83
+
84
+ Returns:
85
+ Dictionary with 'segmentation', 'keypoints', 'actions' URLs
86
+
87
+ Examples:
88
+ >>> urls = get_nng_urls("ps-a1b2c3d4-e5f6-7890-abcd-ef1234567890")
89
+ >>> urls["segmentation"]
90
+ 'ipc:///tmp/rw-a1b2c3d4-e5f6-7890-abcd-ef1234567890-seg.sock'
91
+ """
92
+ guid = parse_session_id(session_id)
93
+ return {
94
+ "segmentation": f"ipc:///tmp/rw-{guid}-seg.sock",
95
+ "keypoints": f"ipc:///tmp/rw-{guid}-kp.sock",
96
+ "actions": f"ipc:///tmp/rw-{guid}-actions.sock",
97
+ }
98
+
99
+
100
+ def get_segmentation_url(session_id: str) -> str:
101
+ """Get NNG URL for segmentation stream.
102
+
103
+ Args:
104
+ session_id: SessionId string (e.g., "ps-a1b2c3d4-...")
105
+
106
+ Returns:
107
+ IPC URL for segmentation stream
108
+ """
109
+ guid = parse_session_id(session_id)
110
+ return f"ipc:///tmp/rw-{guid}-seg.sock"
111
+
112
+
113
+ def get_keypoints_url(session_id: str) -> str:
114
+ """Get NNG URL for keypoints stream.
115
+
116
+ Args:
117
+ session_id: SessionId string (e.g., "ps-a1b2c3d4-...")
118
+
119
+ Returns:
120
+ IPC URL for keypoints stream
121
+ """
122
+ guid = parse_session_id(session_id)
123
+ return f"ipc:///tmp/rw-{guid}-kp.sock"
124
+
125
+
126
+ def get_actions_url(session_id: str) -> str:
127
+ """Get NNG URL for actions stream.
128
+
129
+ Args:
130
+ session_id: SessionId string (e.g., "ps-a1b2c3d4-...")
131
+
132
+ Returns:
133
+ IPC URL for actions stream
134
+ """
135
+ guid = parse_session_id(session_id)
136
+ return f"ipc:///tmp/rw-{guid}-actions.sock"
137
+
138
+
139
+ # ============================================================================
140
+ # Explicit URL functions (PREFERRED - URLs set by rocket-welder2)
141
+ # ============================================================================
142
+
143
+
144
+ def get_nng_urls_from_env() -> dict[str, str | None]:
145
+ """Get NNG URLs from explicit environment variables.
146
+
147
+ This is the PREFERRED method for getting NNG URLs. rocket-welder2
148
+ sets these environment variables when starting containers.
149
+
150
+ Returns:
151
+ Dictionary with 'segmentation', 'keypoints', 'actions' URLs.
152
+ Values are None if not configured.
153
+
154
+ Examples:
155
+ >>> os.environ["SEGMENTATION_SINK_URL"] = "ipc:///tmp/rw-abc-seg.sock"
156
+ >>> urls = get_nng_urls_from_env()
157
+ >>> urls["segmentation"]
158
+ 'ipc:///tmp/rw-abc-seg.sock'
159
+ """
160
+ return {
161
+ "segmentation": os.environ.get(SEGMENTATION_SINK_URL_ENV),
162
+ "keypoints": os.environ.get(KEYPOINTS_SINK_URL_ENV),
163
+ "actions": os.environ.get(ACTIONS_SINK_URL_ENV),
164
+ }
165
+
166
+
167
+ def get_segmentation_url_from_env() -> str | None:
168
+ """Get segmentation NNG URL from environment variable.
169
+
170
+ Returns:
171
+ IPC URL for segmentation stream, or None if not configured.
172
+ """
173
+ return os.environ.get(SEGMENTATION_SINK_URL_ENV)
174
+
175
+
176
+ def get_keypoints_url_from_env() -> str | None:
177
+ """Get keypoints NNG URL from environment variable.
178
+
179
+ Returns:
180
+ IPC URL for keypoints stream, or None if not configured.
181
+ """
182
+ return os.environ.get(KEYPOINTS_SINK_URL_ENV)
183
+
184
+
185
+ def get_actions_url_from_env() -> str | None:
186
+ """Get actions NNG URL from environment variable.
187
+
188
+ Returns:
189
+ IPC URL for actions stream, or None if not configured.
190
+ """
191
+ return os.environ.get(ACTIONS_SINK_URL_ENV)
192
+
193
+
194
+ def has_explicit_nng_urls() -> bool:
195
+ """Check if explicit NNG URLs are configured.
196
+
197
+ Returns:
198
+ True if at least segmentation OR keypoints URL is configured.
199
+ """
200
+ urls = get_nng_urls_from_env()
201
+ return bool(urls["segmentation"] or urls["keypoints"])
202
+
203
+
204
+ def get_configured_nng_urls() -> dict[str, str]:
205
+ """Get all configured NNG URLs (explicit or derived from SessionId).
206
+
207
+ Priority:
208
+ 1. Explicit URLs from environment (SEGMENTATION_SINK_URL, etc.)
209
+ 2. Derived from SessionId environment variable (fallback)
210
+
211
+ Returns:
212
+ Dictionary with 'segmentation', 'keypoints', 'actions' URLs.
213
+ Only includes URLs that are actually configured.
214
+
215
+ Raises:
216
+ ValueError: If no NNG URLs are configured (neither explicit nor SessionId).
217
+ """
218
+ # Try explicit URLs first (preferred)
219
+ explicit_urls = get_nng_urls_from_env()
220
+ result: dict[str, str] = {}
221
+
222
+ for name, url in explicit_urls.items():
223
+ if url:
224
+ result[name] = url
225
+
226
+ # If we have at least one explicit URL, return what we have
227
+ if result:
228
+ return result
229
+
230
+ # Fallback: derive from SessionId
231
+ session_id = get_session_id_from_env()
232
+ if session_id:
233
+ return get_nng_urls(session_id)
234
+
235
+ raise ValueError(
236
+ "No NNG URLs configured. Set SEGMENTATION_SINK_URL/KEYPOINTS_SINK_URL "
237
+ "environment variables, or set SessionId for URL derivation."
238
+ )
@@ -28,11 +28,3 @@ __all__ = [
28
28
  "UnixSocketFrameSource",
29
29
  "UnixSocketServer",
30
30
  ]
31
-
32
- # NNG transport is optional (requires pynng package)
33
- try:
34
- from .nng_transport import NngFrameSink, NngFrameSource
35
-
36
- __all__.extend(["NngFrameSink", "NngFrameSource"])
37
- except ImportError:
38
- pass # pynng not installed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rocket-welder-sdk
3
- Version: 1.1.34a2
3
+ Version: 1.1.34.dev8
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
@@ -93,10 +93,13 @@ class TestOneWayShmController:
93
93
  controller._gst_caps = GstCaps.from_simple(width=2, height=2, format="RGB")
94
94
  on_frame = Mock()
95
95
 
96
- # Create mock frame with correct data
97
- frame_data = np.zeros((12,), dtype=np.uint8) # 2x2x3
96
+ # Create mock frame with 16-byte metadata prefix + pixel data (2x2x3 = 12 bytes)
97
+ metadata_prefix = bytes(16) # 16-byte FrameMetadata
98
+ pixel_data = np.zeros((12,), dtype=np.uint8) # 2x2x3
99
+ frame_data = metadata_prefix + bytes(pixel_data)
98
100
  mock_frame = MagicMock()
99
101
  mock_frame.data = memoryview(frame_data)
102
+ mock_frame.size = len(frame_data)
100
103
 
101
104
  # Process the frame (simulate what happens in the read loop)
102
105
  mat = controller._create_mat_from_frame(mock_frame)
@@ -124,8 +127,12 @@ class TestOneWayShmController:
124
127
  def test_create_mat_from_frame_no_caps(self, controller):
125
128
  """Test _create_mat_from_frame when no caps are available."""
126
129
  frame = MagicMock()
127
- # Use 5 bytes so it's not a perfect square (no square root of 5)
128
- frame.data = memoryview(b"tests")
130
+ # Use 16-byte prefix + 5 bytes pixel data (not a perfect square)
131
+ metadata_prefix = bytes(16)
132
+ pixel_data = b"tests"
133
+ frame_data = metadata_prefix + pixel_data
134
+ frame.data = memoryview(frame_data)
135
+ frame.size = len(frame_data)
129
136
 
130
137
  result = controller._create_mat_from_frame(frame)
131
138
  assert result is None
@@ -135,9 +142,13 @@ class TestOneWayShmController:
135
142
  # Set up GstCaps
136
143
  controller._gst_caps = GstCaps.from_simple(width=2, height=2, format="RGB")
137
144
 
138
- # Create frame with correct data size (2x2x3 = 12 bytes)
145
+ # Create frame with 16-byte prefix + pixel data (2x2x3 = 12 bytes)
146
+ metadata_prefix = bytes(16)
147
+ pixel_data = np.zeros((12,), dtype=np.uint8)
148
+ frame_data = metadata_prefix + bytes(pixel_data)
139
149
  frame = MagicMock()
140
- frame.data = memoryview(np.zeros((12,), dtype=np.uint8))
150
+ frame.data = memoryview(frame_data)
151
+ frame.size = len(frame_data)
141
152
 
142
153
  result = controller._create_mat_from_frame(frame)
143
154
  assert result is not None
@@ -147,9 +158,13 @@ class TestOneWayShmController:
147
158
  """Test _create_mat_from_frame with grayscale format."""
148
159
  controller._gst_caps = GstCaps.from_simple(width=2, height=2, format="GRAY8")
149
160
 
150
- # Create frame with correct data size (2x2x1 = 4 bytes)
161
+ # Create frame with 16-byte prefix + pixel data (2x2x1 = 4 bytes)
162
+ metadata_prefix = bytes(16)
163
+ pixel_data = np.zeros((4,), dtype=np.uint8)
164
+ frame_data = metadata_prefix + bytes(pixel_data)
151
165
  frame = MagicMock()
152
- frame.data = memoryview(np.zeros((4,), dtype=np.uint8))
166
+ frame.data = memoryview(frame_data)
167
+ frame.size = len(frame_data)
153
168
 
154
169
  result = controller._create_mat_from_frame(frame)
155
170
  assert result is not None
@@ -159,9 +174,13 @@ class TestOneWayShmController:
159
174
  """Test _create_mat_from_frame with RGBA format."""
160
175
  controller._gst_caps = GstCaps.from_simple(width=2, height=2, format="RGBA")
161
176
 
162
- # Create frame with correct data size (2x2x4 = 16 bytes)
177
+ # Create frame with 16-byte prefix + pixel data (2x2x4 = 16 bytes)
178
+ metadata_prefix = bytes(16)
179
+ pixel_data = np.zeros((16,), dtype=np.uint8)
180
+ frame_data = metadata_prefix + bytes(pixel_data)
163
181
  frame = MagicMock()
164
- frame.data = memoryview(np.zeros((16,), dtype=np.uint8))
182
+ frame.data = memoryview(frame_data)
183
+ frame.size = len(frame_data)
165
184
 
166
185
  result = controller._create_mat_from_frame(frame)
167
186
  assert result is not None
@@ -1 +0,0 @@
1
- 1.1.34a2
@@ -1,115 +0,0 @@
1
- """SessionId parsing utilities for NNG URL generation.
2
-
3
- SessionId format: ps-{guid} (e.g., ps-a1b2c3d4-e5f6-7890-abcd-ef1234567890)
4
- Prefix "ps" = PipelineSession.
5
-
6
- This module provides utilities to:
7
- 1. Parse SessionId from environment variable
8
- 2. Extract the Guid portion
9
- 3. Generate NNG IPC URLs for streaming results
10
- """
11
-
12
- from __future__ import annotations
13
-
14
- import logging
15
- import os
16
- import uuid
17
-
18
- logger = logging.getLogger(__name__)
19
-
20
- SESSION_ID_PREFIX = "ps-"
21
- SESSION_ID_ENV_VAR = "SessionId"
22
-
23
-
24
- def parse_session_id(session_id: str) -> uuid.UUID:
25
- """Parse SessionId (ps-{guid}) to extract Guid.
26
-
27
- Args:
28
- session_id: SessionId string (e.g., "ps-a1b2c3d4-...")
29
-
30
- Returns:
31
- UUID extracted from SessionId
32
-
33
- Raises:
34
- ValueError: If session_id format is invalid
35
-
36
- Examples:
37
- >>> parse_session_id("ps-a1b2c3d4-e5f6-7890-abcd-ef1234567890")
38
- UUID('a1b2c3d4-e5f6-7890-abcd-ef1234567890')
39
- >>> parse_session_id("a1b2c3d4-e5f6-7890-abcd-ef1234567890") # backwards compat
40
- UUID('a1b2c3d4-e5f6-7890-abcd-ef1234567890')
41
- """
42
- if session_id.startswith(SESSION_ID_PREFIX):
43
- return uuid.UUID(session_id[len(SESSION_ID_PREFIX) :])
44
- # Fallback: try parsing as raw guid for backwards compatibility
45
- return uuid.UUID(session_id)
46
-
47
-
48
- def get_session_id_from_env() -> str | None:
49
- """Get SessionId from environment variable.
50
-
51
- Returns:
52
- SessionId string or None if not set
53
- """
54
- return os.environ.get(SESSION_ID_ENV_VAR)
55
-
56
-
57
- def get_nng_urls(session_id: str) -> dict[str, str]:
58
- """Generate NNG IPC URLs from SessionId.
59
-
60
- Args:
61
- session_id: SessionId string (e.g., "ps-a1b2c3d4-...")
62
-
63
- Returns:
64
- Dictionary with 'segmentation', 'keypoints', 'actions' URLs
65
-
66
- Examples:
67
- >>> urls = get_nng_urls("ps-a1b2c3d4-e5f6-7890-abcd-ef1234567890")
68
- >>> urls["segmentation"]
69
- 'ipc:///tmp/rw-a1b2c3d4-e5f6-7890-abcd-ef1234567890-seg.sock'
70
- """
71
- guid = parse_session_id(session_id)
72
- return {
73
- "segmentation": f"ipc:///tmp/rw-{guid}-seg.sock",
74
- "keypoints": f"ipc:///tmp/rw-{guid}-kp.sock",
75
- "actions": f"ipc:///tmp/rw-{guid}-actions.sock",
76
- }
77
-
78
-
79
- def get_segmentation_url(session_id: str) -> str:
80
- """Get NNG URL for segmentation stream.
81
-
82
- Args:
83
- session_id: SessionId string (e.g., "ps-a1b2c3d4-...")
84
-
85
- Returns:
86
- IPC URL for segmentation stream
87
- """
88
- guid = parse_session_id(session_id)
89
- return f"ipc:///tmp/rw-{guid}-seg.sock"
90
-
91
-
92
- def get_keypoints_url(session_id: str) -> str:
93
- """Get NNG URL for keypoints stream.
94
-
95
- Args:
96
- session_id: SessionId string (e.g., "ps-a1b2c3d4-...")
97
-
98
- Returns:
99
- IPC URL for keypoints stream
100
- """
101
- guid = parse_session_id(session_id)
102
- return f"ipc:///tmp/rw-{guid}-kp.sock"
103
-
104
-
105
- def get_actions_url(session_id: str) -> str:
106
- """Get NNG URL for actions stream.
107
-
108
- Args:
109
- session_id: SessionId string (e.g., "ps-a1b2c3d4-...")
110
-
111
- Returns:
112
- IPC URL for actions stream
113
- """
114
- guid = parse_session_id(session_id)
115
- return f"ipc:///tmp/rw-{guid}-actions.sock"