antioch-py 2.0.6__py3-none-any.whl → 3.0.12__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.

Potentially problematic release.


This version of antioch-py might be problematic. Click here for more details.

Files changed (109) hide show
  1. antioch/__init__.py +101 -0
  2. antioch/{module/execution.py → execution.py} +1 -1
  3. antioch/{module/input.py → input.py} +2 -4
  4. antioch/{module/module.py → module.py} +17 -34
  5. antioch/{module/node.py → node.py} +17 -16
  6. {antioch_py-2.0.6.dist-info → antioch_py-3.0.12.dist-info}/METADATA +8 -11
  7. antioch_py-3.0.12.dist-info/RECORD +61 -0
  8. {antioch_py-2.0.6.dist-info → antioch_py-3.0.12.dist-info}/WHEEL +1 -1
  9. antioch_py-3.0.12.dist-info/licenses/LICENSE +21 -0
  10. common/ark/__init__.py +6 -16
  11. common/ark/ark.py +23 -60
  12. common/ark/hardware.py +13 -37
  13. common/ark/kinematics.py +1 -1
  14. common/ark/module.py +22 -0
  15. common/ark/node.py +46 -3
  16. common/ark/scheduler.py +2 -29
  17. common/ark/sim.py +1 -1
  18. {antioch/module → common/ark}/token.py +17 -0
  19. common/assets/rigging.usd +0 -0
  20. common/constants.py +83 -4
  21. common/core/__init__.py +37 -24
  22. common/core/auth.py +87 -114
  23. common/core/container.py +261 -0
  24. common/core/registry.py +131 -152
  25. common/core/rome.py +251 -0
  26. common/core/telemetry.py +176 -0
  27. common/core/types.py +219 -0
  28. common/message/__init__.py +19 -3
  29. common/message/annotation.py +174 -23
  30. common/message/array.py +25 -1
  31. common/message/camera.py +23 -1
  32. common/message/color.py +32 -6
  33. common/message/detection.py +40 -0
  34. common/message/foxglove.py +20 -0
  35. common/message/frame.py +71 -7
  36. common/message/image.py +58 -9
  37. common/message/imu.py +24 -4
  38. common/message/joint.py +69 -10
  39. common/message/log.py +52 -7
  40. common/message/pir.py +22 -5
  41. common/message/plot.py +57 -0
  42. common/message/point.py +55 -6
  43. common/message/point_cloud.py +55 -19
  44. common/message/pose.py +59 -19
  45. common/message/quaternion.py +105 -92
  46. common/message/radar.py +195 -29
  47. common/message/twist.py +34 -0
  48. common/message/types.py +40 -5
  49. common/message/vector.py +180 -245
  50. common/sim/__init__.py +49 -0
  51. common/sim/objects.py +460 -0
  52. common/sim/state.py +11 -0
  53. common/utils/comms.py +30 -12
  54. common/utils/logger.py +26 -7
  55. antioch/message.py +0 -87
  56. antioch/module/__init__.py +0 -53
  57. antioch/session/__init__.py +0 -150
  58. antioch/session/ark.py +0 -504
  59. antioch/session/asset.py +0 -65
  60. antioch/session/error.py +0 -80
  61. antioch/session/record.py +0 -158
  62. antioch/session/scene.py +0 -1521
  63. antioch/session/session.py +0 -220
  64. antioch/session/task.py +0 -323
  65. antioch/session/views/__init__.py +0 -40
  66. antioch/session/views/animation.py +0 -189
  67. antioch/session/views/articulation.py +0 -245
  68. antioch/session/views/basis_curve.py +0 -186
  69. antioch/session/views/camera.py +0 -92
  70. antioch/session/views/collision.py +0 -75
  71. antioch/session/views/geometry.py +0 -74
  72. antioch/session/views/ground_plane.py +0 -63
  73. antioch/session/views/imu.py +0 -73
  74. antioch/session/views/joint.py +0 -64
  75. antioch/session/views/light.py +0 -175
  76. antioch/session/views/pir_sensor.py +0 -140
  77. antioch/session/views/radar.py +0 -73
  78. antioch/session/views/rigid_body.py +0 -282
  79. antioch/session/views/xform.py +0 -119
  80. antioch_py-2.0.6.dist-info/RECORD +0 -99
  81. antioch_py-2.0.6.dist-info/entry_points.txt +0 -2
  82. common/core/agent.py +0 -296
  83. common/core/task.py +0 -36
  84. common/rome/__init__.py +0 -9
  85. common/rome/client.py +0 -430
  86. common/rome/error.py +0 -16
  87. common/session/__init__.py +0 -54
  88. common/session/environment.py +0 -31
  89. common/session/sim.py +0 -240
  90. common/session/views/__init__.py +0 -263
  91. common/session/views/animation.py +0 -73
  92. common/session/views/articulation.py +0 -184
  93. common/session/views/basis_curve.py +0 -102
  94. common/session/views/camera.py +0 -147
  95. common/session/views/collision.py +0 -59
  96. common/session/views/geometry.py +0 -102
  97. common/session/views/ground_plane.py +0 -41
  98. common/session/views/imu.py +0 -66
  99. common/session/views/joint.py +0 -81
  100. common/session/views/light.py +0 -96
  101. common/session/views/pir_sensor.py +0 -115
  102. common/session/views/radar.py +0 -82
  103. common/session/views/rigid_body.py +0 -236
  104. common/session/views/viewport.py +0 -21
  105. common/session/views/xform.py +0 -39
  106. common/utils/usd.py +0 -12
  107. /antioch/{module/clock.py → clock.py} +0 -0
  108. {antioch_py-2.0.6.dist-info → antioch_py-3.0.12.dist-info}/top_level.txt +0 -0
  109. /common/message/{base.py → message.py} +0 -0
@@ -0,0 +1,176 @@
1
+ import time
2
+ from pathlib import Path
3
+
4
+ import foxglove
5
+ import zenoh
6
+ from foxglove import Capability, MCAPWriter # type: ignore[attr-defined]
7
+
8
+ from common.constants import FOXGLOVE_WEBSOCKET_PORT
9
+ from common.message import (
10
+ DetectionDistances,
11
+ FrameTransforms,
12
+ Image,
13
+ ImageAnnotations,
14
+ Log,
15
+ Message,
16
+ PlotData,
17
+ PointCloud,
18
+ Pose,
19
+ RadarScan,
20
+ RangeMap,
21
+ Vector2,
22
+ Vector3,
23
+ )
24
+ from common.utils.comms import CommsSession
25
+
26
+ TYPE_MAP: dict[str, type] = {
27
+ "antioch/vector2": Vector2,
28
+ "antioch/vector3": Vector3,
29
+ "antioch/pose": Pose,
30
+ "antioch/image": Image,
31
+ "antioch/image_annotations": ImageAnnotations,
32
+ "antioch/point_cloud": PointCloud,
33
+ "antioch/frame_transforms": FrameTransforms,
34
+ "antioch/log": Log,
35
+ "antioch/radar_scan": RadarScan,
36
+ "antioch/range_map": RangeMap,
37
+ "antioch/plot_data": PlotData,
38
+ "antioch/detection_distances": DetectionDistances,
39
+ }
40
+
41
+
42
+ class TelemetryManager:
43
+ """
44
+ Manages Foxglove WebSocket server and MCAP recording.
45
+
46
+ The WebSocket server persists across tasks. MCAP recording is per-task.
47
+ Uses a callback subscriber to process logs from Zenoh and forward to Foxglove.
48
+ """
49
+
50
+ def __init__(self, run_websocket: bool = True) -> None:
51
+ """
52
+ Create a new telemetry manager.
53
+
54
+ Initializes the Foxglove WebSocket server (if enabled) and starts listening for logs.
55
+
56
+ :param run_websocket: Whether to start the WebSocket server. Set to False for
57
+ headless non-streaming mode to avoid port conflicts.
58
+ """
59
+
60
+ self._comms = CommsSession()
61
+ self._mcap_writer: MCAPWriter | None = None
62
+ self._start_timestamp_ns: int = time.time_ns()
63
+ self._time_offset_ns: int = 0
64
+ self._last_let_us: int = 0
65
+ self._run_websocket = run_websocket
66
+
67
+ if run_websocket:
68
+ try:
69
+ self._server = foxglove.start_server(
70
+ name="antioch-telemetry",
71
+ host="0.0.0.0",
72
+ port=FOXGLOVE_WEBSOCKET_PORT,
73
+ capabilities=[Capability.Time],
74
+ )
75
+ except RuntimeError as e:
76
+ if "Address already in use" in str(e):
77
+ raise RuntimeError(
78
+ f"Foxglove server port {FOXGLOVE_WEBSOCKET_PORT} is already in use. "
79
+ f"Another simulation may be running. Stop it first with sim.stop(), "
80
+ f"or kill the process: lsof -ti :{FOXGLOVE_WEBSOCKET_PORT} | xargs -r kill"
81
+ ) from None
82
+ raise
83
+ else:
84
+ self._server = None
85
+
86
+ self._subscriber = self._comms.declare_callback_subscriber("_logs", self._on_log)
87
+
88
+ def stop(self) -> None:
89
+ """
90
+ Stop the telemetry manager and clean up resources.
91
+
92
+ Stops the log subscriber, MCAP recording, and WebSocket server.
93
+ """
94
+
95
+ self._subscriber.undeclare()
96
+ self.stop_recording()
97
+ if self._server is not None:
98
+ self._server.stop()
99
+ self._comms.close()
100
+
101
+ def start_recording(self, mcap_path: str) -> None:
102
+ """
103
+ Start recording telemetry to an MCAP file.
104
+
105
+ :param mcap_path: Path to save the MCAP file.
106
+ """
107
+
108
+ self.stop_recording()
109
+ path = Path(mcap_path)
110
+ path.parent.mkdir(parents=True, exist_ok=True)
111
+ self._mcap_writer = foxglove.open_mcap(str(path), allow_overwrite=True)
112
+
113
+ def stop_recording(self) -> None:
114
+ """
115
+ Stop MCAP recording and finalize the file.
116
+ """
117
+
118
+ if self._mcap_writer:
119
+ self._mcap_writer.close()
120
+ self._mcap_writer = None
121
+
122
+ def reset_time(self) -> None:
123
+ """
124
+ Accumulate the current time offset for monotonic timestamps.
125
+
126
+ Call this when the simulation is cleared or stopped. This ensures that
127
+ timestamps continue monotonically increasing across multiple episodes
128
+ in the same MCAP file.
129
+ """
130
+
131
+ self._time_offset_ns += self._last_let_us * 1000
132
+ self._last_let_us = 0
133
+
134
+ def _on_log(self, sample: zenoh.Sample) -> None:
135
+ """
136
+ Callback for incoming log samples.
137
+ """
138
+
139
+ log = Log.unpack(bytes(sample.payload))
140
+ if log.channel is None:
141
+ return
142
+
143
+ self._last_let_us = log.let_us
144
+
145
+ # Calculate monotonically increasing timestamp
146
+ log_time_ns = self._start_timestamp_ns + self._time_offset_ns + (log.let_us * 1000)
147
+
148
+ # Broadcast time if websocket server is running
149
+ if self._server is not None:
150
+ self._server.broadcast_time(log_time_ns)
151
+
152
+ if log.telemetry is not None:
153
+ msg_type = Message.extract_type(log.telemetry)
154
+ self._log_payload(log.channel, log.telemetry, msg_type, log_time_ns)
155
+ return
156
+
157
+ foxglove_log = log.to_foxglove()
158
+ if foxglove_log is not None:
159
+ foxglove.log(log.channel, foxglove_log, log_time=log_time_ns)
160
+
161
+ def _log_payload(self, channel: str, data: bytes, msg_type: str | None, log_time_ns: int) -> None:
162
+ """
163
+ Extract and log a telemetry payload.
164
+ """
165
+
166
+ msg_class = TYPE_MAP.get(msg_type) if msg_type else None
167
+ if msg_class is None:
168
+ # Unknown type - log as JSON directly
169
+ json_data = Message.extract_data_as_json(data)
170
+ foxglove.log(channel, json_data, log_time=log_time_ns)
171
+ return
172
+
173
+ msg = msg_class.unpack(data)
174
+ foxglove_msg = msg.to_foxglove()
175
+ if foxglove_msg is not None:
176
+ foxglove.log(channel, foxglove_msg, log_time=log_time_ns)
common/core/types.py ADDED
@@ -0,0 +1,219 @@
1
+ from datetime import datetime
2
+ from enum import Enum
3
+
4
+ from common.message import Message
5
+
6
+ # =============================================================================
7
+ # Task Types
8
+ # =============================================================================
9
+
10
+
11
+ class TaskOutcome(str, Enum):
12
+ """
13
+ Task outcome status.
14
+ """
15
+
16
+ SUCCESS = "success"
17
+ FAILURE = "failure"
18
+ ERROR = "error"
19
+ TIMEOUT = "timeout"
20
+ SKIPPED = "skipped"
21
+
22
+
23
+ class TaskTriggerSource(str, Enum):
24
+ """
25
+ How the task was triggered.
26
+ """
27
+
28
+ MANUAL = "manual"
29
+ CI = "ci"
30
+ SCHEDULED = "scheduled"
31
+ API = "api"
32
+
33
+
34
+ class TaskRunner(Message):
35
+ """
36
+ Runner/execution environment metadata for a task run.
37
+ """
38
+
39
+ runner_id: str | None = None
40
+ runner_name: str | None = None
41
+ environment: dict | None = None
42
+
43
+
44
+ class TaskRun(Message):
45
+ """
46
+ A single execution of a task with timing, outcome, and results.
47
+
48
+ Task runs are grouped by task_name for display.
49
+ """
50
+
51
+ # Identity - run_id is generated server-side, org_id/user_id set from context
52
+ run_id: str | None = None
53
+ org_id: str | None = None
54
+ user_id: str | None = None
55
+
56
+ # Timing
57
+ started_at: datetime
58
+ completed_at: datetime
59
+ duration_ms: int | None = None
60
+
61
+ # Task identity
62
+ task_name: str
63
+ description: str | None = None
64
+
65
+ # Outcome and results
66
+ outcome: TaskOutcome
67
+ result: dict | None = None
68
+
69
+ # User info (display names for search/filtering)
70
+ user_name: str | None = None
71
+ user_email: str | None = None
72
+
73
+ # Filtering
74
+ tags: list[str] | None = None
75
+ parameters: dict | None = None
76
+
77
+ # Execution context
78
+ runner: TaskRunner | None = None
79
+ trigger_source: TaskTriggerSource | None = None
80
+
81
+
82
+ # =============================================================================
83
+ # Registry Types
84
+ # =============================================================================
85
+
86
+
87
+ class ArkRegistryMetadata(Message):
88
+ """
89
+ Metadata stored in GCS for a published Ark version.
90
+
91
+ This is the canonical schema for Ark registry metadata. All fields are
92
+ populated by Rome during push and validated against this schema.
93
+ """
94
+
95
+ module_count: int
96
+ image_count: int
97
+ capability: str
98
+ has_assets: bool
99
+ description: str
100
+ timestamp: str
101
+ digest: str
102
+
103
+ @classmethod
104
+ def from_dict(cls, data: dict[str, str]) -> "ArkRegistryMetadata":
105
+ """
106
+ Parse metadata from GCS blob metadata (all values are strings).
107
+
108
+ :param data: Raw metadata dict from GCS.
109
+ :return: Parsed ArkRegistryMetadata.
110
+ """
111
+
112
+ return cls(
113
+ module_count=int(data.get("module_count", "0")),
114
+ image_count=int(data.get("image_count", "0")),
115
+ capability=data.get("capability", ""),
116
+ has_assets=data.get("has_assets", "false").lower() == "true",
117
+ description=data.get("description", ""),
118
+ timestamp=data.get("timestamp", ""),
119
+ digest=data.get("digest", ""),
120
+ )
121
+
122
+ def to_gcs_metadata(self) -> dict[str, str]:
123
+ """
124
+ Convert to GCS metadata format (all values as strings).
125
+
126
+ :return: Dict suitable for GCS blob metadata.
127
+ """
128
+
129
+ return {
130
+ "module_count": str(self.module_count),
131
+ "image_count": str(self.image_count),
132
+ "capability": self.capability,
133
+ "has_assets": str(self.has_assets).lower(),
134
+ "description": self.description,
135
+ "timestamp": self.timestamp,
136
+ "digest": self.digest,
137
+ }
138
+
139
+
140
+ class ArkVersionReference(Message):
141
+ """
142
+ Reference to an Ark version in the remote Ark registry.
143
+ """
144
+
145
+ version: str
146
+ created_at: str
147
+ updated_at: str
148
+ full_path: str
149
+ size_bytes: int
150
+ asset_path: str | None = None
151
+ asset_size_bytes: int | None = None
152
+ metadata: ArkRegistryMetadata | None = None
153
+
154
+
155
+ class ArkReference(Message):
156
+ """
157
+ Reference to an Ark in the remote Ark registry.
158
+ """
159
+
160
+ name: str
161
+ versions: list[ArkVersionReference]
162
+ created_at: str
163
+ updated_at: str
164
+
165
+ def __str__(self) -> str:
166
+ """
167
+ Return a string representation of the ArkReference.
168
+ """
169
+
170
+ versions_str = ", ".join(v.version for v in self.versions)
171
+ return f"{self.name} [{versions_str}]"
172
+
173
+ def __repr__(self) -> str:
174
+ """
175
+ Return a detailed representation of the ArkReference.
176
+ """
177
+
178
+ versions_list = [v.version for v in self.versions]
179
+ return f"ArkReference(name='{self.name}', versions={versions_list})"
180
+
181
+
182
+ class AssetVersionReference(Message):
183
+ """
184
+ Reference to a specific asset version.
185
+ """
186
+
187
+ version: str
188
+ full_path: str
189
+ size_bytes: int
190
+ created_at: str
191
+ updated_at: str
192
+ metadata: dict[str, str] = {}
193
+
194
+
195
+ class AssetReference(Message):
196
+ """
197
+ Reference to an asset with all its versions.
198
+ """
199
+
200
+ name: str
201
+ versions: list[AssetVersionReference]
202
+ created_at: str
203
+ updated_at: str
204
+
205
+ def __str__(self) -> str:
206
+ """
207
+ Return a string representation of the AssetReference.
208
+ """
209
+
210
+ versions_str = ", ".join(v.version for v in self.versions)
211
+ return f"{self.name} [{versions_str}]"
212
+
213
+ def __repr__(self) -> str:
214
+ """
215
+ Return a detailed representation of the AssetReference.
216
+ """
217
+
218
+ versions_list = [v.version for v in self.versions]
219
+ return f"AssetReference(name='{self.name}', versions={versions_list})"
@@ -1,19 +1,30 @@
1
1
  from common.message.annotation import CircleAnnotation, ImageAnnotations, PointsAnnotation, PointsAnnotationType, TextAnnotation
2
2
  from common.message.array import Array
3
- from common.message.base import DeserializationError, Message, MessageError, MismatchError, SerializationError
4
3
  from common.message.camera import CameraInfo
5
4
  from common.message.color import Color
5
+ from common.message.detection import DetectionDistances
6
+ from common.message.foxglove import FoxgloveConvertible
6
7
  from common.message.frame import FrameTransform, FrameTransforms
7
8
  from common.message.image import Image, ImageEncoding
8
9
  from common.message.imu import ImuSample
9
10
  from common.message.joint import JointState, JointStates, JointTarget, JointTargets
10
11
  from common.message.log import Log, LogLevel
12
+ from common.message.message import (
13
+ DeserializationError,
14
+ FileAccessError,
15
+ Message,
16
+ MessageError,
17
+ MismatchError,
18
+ SerializationError,
19
+ )
11
20
  from common.message.pir import PirStatus
21
+ from common.message.plot import PlotData
12
22
  from common.message.point import Point2, Point3
13
23
  from common.message.point_cloud import PointCloud
14
24
  from common.message.pose import Pose
15
25
  from common.message.quaternion import Quaternion
16
- from common.message.radar import RadarDetection, RadarScan
26
+ from common.message.radar import RadarScan, RangeMap
27
+ from common.message.twist import Twist
17
28
  from common.message.types import Bool, Float, Int, String
18
29
  from common.message.vector import Vector2, Vector3
19
30
 
@@ -24,7 +35,10 @@ __all__ = [
24
35
  "CircleAnnotation",
25
36
  "Color",
26
37
  "DeserializationError",
38
+ "DetectionDistances",
39
+ "FileAccessError",
27
40
  "Float",
41
+ "FoxgloveConvertible",
28
42
  "FrameTransform",
29
43
  "FrameTransforms",
30
44
  "Image",
@@ -42,6 +56,7 @@ __all__ = [
42
56
  "MessageError",
43
57
  "MismatchError",
44
58
  "PirStatus",
59
+ "PlotData",
45
60
  "Point2",
46
61
  "Point3",
47
62
  "PointCloud",
@@ -49,11 +64,12 @@ __all__ = [
49
64
  "PointsAnnotationType",
50
65
  "Pose",
51
66
  "Quaternion",
52
- "RadarDetection",
53
67
  "RadarScan",
68
+ "RangeMap",
54
69
  "SerializationError",
55
70
  "String",
56
71
  "TextAnnotation",
72
+ "Twist",
57
73
  "Vector2",
58
74
  "Vector3",
59
75
  ]