antioch-py 2.0.6__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 (99) hide show
  1. antioch/__init__.py +0 -0
  2. antioch/message.py +87 -0
  3. antioch/module/__init__.py +53 -0
  4. antioch/module/clock.py +62 -0
  5. antioch/module/execution.py +278 -0
  6. antioch/module/input.py +127 -0
  7. antioch/module/module.py +218 -0
  8. antioch/module/node.py +357 -0
  9. antioch/module/token.py +42 -0
  10. antioch/session/__init__.py +150 -0
  11. antioch/session/ark.py +504 -0
  12. antioch/session/asset.py +65 -0
  13. antioch/session/error.py +80 -0
  14. antioch/session/record.py +158 -0
  15. antioch/session/scene.py +1521 -0
  16. antioch/session/session.py +220 -0
  17. antioch/session/task.py +323 -0
  18. antioch/session/views/__init__.py +40 -0
  19. antioch/session/views/animation.py +189 -0
  20. antioch/session/views/articulation.py +245 -0
  21. antioch/session/views/basis_curve.py +186 -0
  22. antioch/session/views/camera.py +92 -0
  23. antioch/session/views/collision.py +75 -0
  24. antioch/session/views/geometry.py +74 -0
  25. antioch/session/views/ground_plane.py +63 -0
  26. antioch/session/views/imu.py +73 -0
  27. antioch/session/views/joint.py +64 -0
  28. antioch/session/views/light.py +175 -0
  29. antioch/session/views/pir_sensor.py +140 -0
  30. antioch/session/views/radar.py +73 -0
  31. antioch/session/views/rigid_body.py +282 -0
  32. antioch/session/views/xform.py +119 -0
  33. antioch_py-2.0.6.dist-info/METADATA +115 -0
  34. antioch_py-2.0.6.dist-info/RECORD +99 -0
  35. antioch_py-2.0.6.dist-info/WHEEL +5 -0
  36. antioch_py-2.0.6.dist-info/entry_points.txt +2 -0
  37. antioch_py-2.0.6.dist-info/top_level.txt +2 -0
  38. common/__init__.py +0 -0
  39. common/ark/__init__.py +60 -0
  40. common/ark/ark.py +128 -0
  41. common/ark/hardware.py +121 -0
  42. common/ark/kinematics.py +31 -0
  43. common/ark/module.py +85 -0
  44. common/ark/node.py +94 -0
  45. common/ark/scheduler.py +439 -0
  46. common/ark/sim.py +33 -0
  47. common/assets/__init__.py +3 -0
  48. common/constants.py +47 -0
  49. common/core/__init__.py +52 -0
  50. common/core/agent.py +296 -0
  51. common/core/auth.py +305 -0
  52. common/core/registry.py +331 -0
  53. common/core/task.py +36 -0
  54. common/message/__init__.py +59 -0
  55. common/message/annotation.py +89 -0
  56. common/message/array.py +500 -0
  57. common/message/base.py +517 -0
  58. common/message/camera.py +91 -0
  59. common/message/color.py +139 -0
  60. common/message/frame.py +50 -0
  61. common/message/image.py +171 -0
  62. common/message/imu.py +14 -0
  63. common/message/joint.py +47 -0
  64. common/message/log.py +31 -0
  65. common/message/pir.py +16 -0
  66. common/message/point.py +109 -0
  67. common/message/point_cloud.py +63 -0
  68. common/message/pose.py +148 -0
  69. common/message/quaternion.py +273 -0
  70. common/message/radar.py +58 -0
  71. common/message/types.py +37 -0
  72. common/message/vector.py +786 -0
  73. common/rome/__init__.py +9 -0
  74. common/rome/client.py +430 -0
  75. common/rome/error.py +16 -0
  76. common/session/__init__.py +54 -0
  77. common/session/environment.py +31 -0
  78. common/session/sim.py +240 -0
  79. common/session/views/__init__.py +263 -0
  80. common/session/views/animation.py +73 -0
  81. common/session/views/articulation.py +184 -0
  82. common/session/views/basis_curve.py +102 -0
  83. common/session/views/camera.py +147 -0
  84. common/session/views/collision.py +59 -0
  85. common/session/views/geometry.py +102 -0
  86. common/session/views/ground_plane.py +41 -0
  87. common/session/views/imu.py +66 -0
  88. common/session/views/joint.py +81 -0
  89. common/session/views/light.py +96 -0
  90. common/session/views/pir_sensor.py +115 -0
  91. common/session/views/radar.py +82 -0
  92. common/session/views/rigid_body.py +236 -0
  93. common/session/views/viewport.py +21 -0
  94. common/session/views/xform.py +39 -0
  95. common/utils/__init__.py +4 -0
  96. common/utils/comms.py +571 -0
  97. common/utils/logger.py +123 -0
  98. common/utils/time.py +42 -0
  99. common/utils/usd.py +12 -0
@@ -0,0 +1,331 @@
1
+ import json
2
+ from collections import defaultdict
3
+ from datetime import datetime
4
+ from pathlib import Path
5
+
6
+ from common.ark import Ark as ArkDefinition, ArkReference, ArkVersionReference, AssetReference, AssetVersionReference
7
+ from common.constants import ANTIOCH_API_URL, get_ark_dir, get_asset_dir
8
+ from common.core.auth import AuthError, AuthHandler
9
+ from common.rome import RomeClient
10
+
11
+
12
+ def list_local_arks() -> list[ArkReference]:
13
+ """
14
+ List all locally available Arks.
15
+
16
+ :return: List of ArkReference objects from local storage.
17
+ """
18
+
19
+ arks_dir = get_ark_dir()
20
+ files_by_name = defaultdict(list)
21
+ for file_path in arks_dir.iterdir():
22
+ if file_path.is_file() and (file_path.name.endswith(":ark.json") or file_path.name.endswith(":asset.usdz")):
23
+ name = file_path.name.split(":")[0]
24
+ files_by_name[name].append(file_path)
25
+
26
+ results = []
27
+ for name, files in files_by_name.items():
28
+ ref = _build_ark_reference(name, files)
29
+ if ref is not None:
30
+ results.append(ref)
31
+
32
+ return results
33
+
34
+
35
+ def load_local_ark(name: str, version: str) -> ArkDefinition:
36
+ """
37
+ Load Ark definition from local storage.
38
+
39
+ :param name: Name of the Ark.
40
+ :param version: Version of the Ark.
41
+ :return: The loaded Ark definition.
42
+ """
43
+
44
+ with open(get_ark_version_reference(name, version).full_path) as f:
45
+ return ArkDefinition(**json.load(f))
46
+
47
+
48
+ def get_ark_version_reference(name: str, version: str) -> ArkVersionReference:
49
+ """
50
+ Get version reference for an Ark.
51
+
52
+ :param name: Name of the Ark.
53
+ :param version: Version of the Ark.
54
+ :return: Version reference for the Ark.
55
+ """
56
+
57
+ available_arks = list_local_arks()
58
+ for ark_ref in available_arks:
59
+ if ark_ref.name == name:
60
+ for version_ref in ark_ref.versions:
61
+ if version_ref.version == version:
62
+ return version_ref
63
+ raise FileNotFoundError(f"Version {version} of Ark {name} not found in local storage")
64
+ raise FileNotFoundError(f"No versions of Ark {name} found in local storage")
65
+
66
+
67
+ def get_asset_path(name: str, version: str, extension: str = "usdz", assert_exists: bool = True) -> Path:
68
+ """
69
+ Get the local file path for a specific asset version.
70
+
71
+ :param name: Name of the asset.
72
+ :param version: Version of the asset.
73
+ :param extension: File extension (without dot), defaults to 'usdz'.
74
+ :param assert_exists: If True, raises error if asset file doesn't exist.
75
+ :return: Path to the asset file.
76
+ """
77
+
78
+ assets_dir = get_asset_dir()
79
+ asset_file = assets_dir / f"{name}:{version}:file.{extension}"
80
+ if assert_exists and not asset_file.exists():
81
+ raise FileNotFoundError(f"Asset {name}:{version} with extension .{extension} does not exist")
82
+
83
+ return asset_file
84
+
85
+
86
+ def list_local_assets() -> list[AssetReference]:
87
+ """
88
+ List all locally cached assets.
89
+
90
+ :return: List of AssetReference objects from local storage.
91
+ """
92
+
93
+ assets_dir = get_asset_dir()
94
+ if not assets_dir.exists():
95
+ return []
96
+
97
+ files_by_name: dict[str, list[Path]] = defaultdict(list)
98
+ for file_path in assets_dir.iterdir():
99
+ if file_path.is_file():
100
+ # Parse filename format: {name}:{version}:file.{extension}
101
+ parts = file_path.stem.split(":")
102
+ if len(parts) == 3 and parts[-1] == "file":
103
+ files_by_name[parts[0]].append(file_path)
104
+
105
+ results = []
106
+ for name, files in files_by_name.items():
107
+ ref = _build_asset_reference(name, files)
108
+ if ref is not None:
109
+ results.append(ref)
110
+
111
+ return results
112
+
113
+
114
+ def list_remote_arks() -> list[ArkReference]:
115
+ """
116
+ List all Arks from remote registry.
117
+
118
+ Requires authentication.
119
+
120
+ :return: List of ArkReference objects from remote registry.
121
+ :raises AuthError: If not authenticated.
122
+ """
123
+
124
+ # Get auth token
125
+ auth = AuthHandler()
126
+ token = auth.get_token()
127
+ if token is None:
128
+ raise AuthError("User not authenticated. Please login first")
129
+
130
+ # Create Rome client and list arks
131
+ rome_client = RomeClient(api_url=ANTIOCH_API_URL, token=token)
132
+ return rome_client.list_arks()
133
+
134
+
135
+ def pull_remote_ark(name: str, version: str, overwrite: bool = False) -> ArkDefinition:
136
+ """
137
+ Pull an Ark from remote registry to local storage.
138
+
139
+ Requires authentication.
140
+
141
+ :param name: Name of the Ark.
142
+ :param version: Version of the Ark.
143
+ :param overwrite: Overwrite local Ark if it already exists.
144
+ :return: The loaded Ark definition.
145
+ :raises AuthError: If not authenticated.
146
+ """
147
+
148
+ # Check if Ark already exists locally
149
+ arks_dir = get_ark_dir()
150
+ ark_json_path = arks_dir / f"{name}:{version}:ark.json"
151
+ if ark_json_path.exists() and not overwrite:
152
+ return load_local_ark(name, version)
153
+
154
+ # Get auth token
155
+ auth = AuthHandler()
156
+ token = auth.get_token()
157
+ if not token:
158
+ raise AuthError("User not authenticated. Please login first")
159
+
160
+ # Create Rome client and fetch Ark definition and save to local storage
161
+ rome_client = RomeClient(api_url=ANTIOCH_API_URL, token=token)
162
+ ark = rome_client.get_ark(name=name, version=version)
163
+
164
+ # Save Ark JSON
165
+ with open(ark_json_path, "wb") as f:
166
+ f.write(json.dumps(ark).encode("utf-8"))
167
+
168
+ # Download asset usdz only if ark has asset_hash (hardware modules exist)
169
+ if ark.get("metadata", {}).get("asset_hash") is not None:
170
+ asset_content = rome_client.download_ark_assets(name=name, version=version)
171
+ with open(arks_dir / f"{name}:{version}:asset.usdz", "wb") as f:
172
+ f.write(asset_content)
173
+
174
+ return ArkDefinition(**ark)
175
+
176
+
177
+ def list_remote_assets() -> list[AssetReference]:
178
+ """
179
+ List all assets from remote registry.
180
+
181
+ Requires authentication.
182
+
183
+ :return: List of AssetReference objects from remote registry.
184
+ :raises AuthError: If not authenticated.
185
+ """
186
+
187
+ # Get auth token
188
+ token = AuthHandler().get_token()
189
+ if token is None:
190
+ raise AuthError("User not authenticated. Please login first")
191
+
192
+ # Create Rome client and list assets
193
+ rome_client = RomeClient(api_url=ANTIOCH_API_URL, token=token)
194
+ return rome_client.list_assets()
195
+
196
+
197
+ def pull_remote_asset(name: str, version: str, overwrite: bool = False, show_progress: bool = True) -> Path:
198
+ """
199
+ Pull an asset from remote registry to local storage.
200
+
201
+ Requires authentication.
202
+
203
+ :param name: Name of the asset.
204
+ :param version: Version of the asset.
205
+ :param overwrite: Overwrite local asset if it already exists.
206
+ :param show_progress: Show download progress bar.
207
+ :return: Path to the downloaded asset file.
208
+ :raises AuthError: If not authenticated.
209
+ """
210
+
211
+ # Get auth token
212
+ token = AuthHandler().get_token()
213
+ if token is None:
214
+ raise AuthError("User not authenticated. Please login first")
215
+
216
+ # Create Rome client and get asset metadata to determine extension
217
+ rome_client = RomeClient(api_url=ANTIOCH_API_URL, token=token)
218
+ metadata = rome_client.get_asset_metadata(name=name, version=version)
219
+ extension = metadata.get("extension", "usdz")
220
+
221
+ # Check if asset already exists locally
222
+ asset_file_path = get_asset_path(name=name, version=version, extension=extension, assert_exists=False)
223
+ if asset_file_path.exists() and not overwrite:
224
+ print(f"Asset {name}:{version} already exists locally, skipping download")
225
+ return asset_file_path
226
+
227
+ # Download the asset file
228
+ rome_client.download_asset(name=name, version=version, output_path=str(asset_file_path), show_progress=show_progress)
229
+ return asset_file_path
230
+
231
+
232
+ def _build_ark_reference(name: str, files: list[Path]) -> ArkReference | None:
233
+ """
234
+ Create an ArkReference from a list of files for a given ark.
235
+
236
+ :param name: The name of the ark.
237
+ :param files: List of file paths (ark JSON and asset USDZ files).
238
+ :return: ArkReference object or None if no valid versions found.
239
+ """
240
+
241
+ file_stats = [f.stat() for f in files]
242
+ created_at = min(datetime.fromtimestamp(stat.st_ctime) for stat in file_stats).isoformat()
243
+ updated_at = max(datetime.fromtimestamp(stat.st_mtime) for stat in file_stats).isoformat()
244
+
245
+ # Group files by version - parse from {name}:{version}:ark.json or {name}:{version}:asset.usdz
246
+ files_by_version: dict[str, list[Path]] = defaultdict(list)
247
+ for file_path in files:
248
+ files_by_version[file_path.name.split(":")[1]].append(file_path)
249
+
250
+ # Create an ArkVersionReference for each version
251
+ version_refs = []
252
+ for version, version_files in files_by_version.items():
253
+ ark_file = None
254
+ asset_file = None
255
+ for file_path in version_files:
256
+ if file_path.name.endswith(":ark.json"):
257
+ ark_file = file_path
258
+ elif file_path.name.endswith(":asset.usdz"):
259
+ asset_file = file_path
260
+ if ark_file is None:
261
+ continue
262
+
263
+ ark_stat = ark_file.stat()
264
+ version_refs.append(
265
+ ArkVersionReference(
266
+ version=version,
267
+ full_path=str(ark_file),
268
+ asset_path=str(asset_file) if asset_file else None,
269
+ size_bytes=ark_stat.st_size,
270
+ created_at=datetime.fromtimestamp(ark_stat.st_ctime).isoformat(),
271
+ updated_at=datetime.fromtimestamp(ark_stat.st_mtime).isoformat(),
272
+ asset_size_bytes=asset_file.stat().st_size if asset_file else None,
273
+ )
274
+ )
275
+
276
+ if not version_refs:
277
+ return None
278
+
279
+ return ArkReference(
280
+ name=name,
281
+ versions=version_refs,
282
+ created_at=created_at,
283
+ updated_at=updated_at,
284
+ )
285
+
286
+
287
+ def _build_asset_reference(name: str, files: list[Path]) -> AssetReference | None:
288
+ """
289
+ Create an AssetReference from a list of files for a given asset.
290
+
291
+ :param name: The name of the asset.
292
+ :param files: List of file paths (.usdz files).
293
+ :return: AssetReference object or None if no valid versions found.
294
+ """
295
+
296
+ file_stats = [f.stat() for f in files]
297
+ created_at = min(datetime.fromtimestamp(stat.st_ctime) for stat in file_stats).isoformat()
298
+ updated_at = max(datetime.fromtimestamp(stat.st_mtime) for stat in file_stats).isoformat()
299
+
300
+ # Group files by version
301
+ files_by_version: dict[str, list[Path]] = defaultdict(list)
302
+ for file_path in files:
303
+ # Parse filename format: {name}:{version}:file.usdz
304
+ version = file_path.name.split(":")[1]
305
+ files_by_version[version].append(file_path)
306
+
307
+ # Create an AssetVersionReference for each version
308
+ version_refs = []
309
+ for version, version_files in files_by_version.items():
310
+ # Should only be one file per version, but take first if multiple
311
+ asset_file = version_files[0]
312
+ asset_stat = asset_file.stat()
313
+ version_refs.append(
314
+ AssetVersionReference(
315
+ version=version,
316
+ full_path=str(asset_file),
317
+ size_bytes=asset_stat.st_size,
318
+ created_at=datetime.fromtimestamp(asset_stat.st_ctime).isoformat(),
319
+ updated_at=datetime.fromtimestamp(asset_stat.st_mtime).isoformat(),
320
+ )
321
+ )
322
+
323
+ if not version_refs:
324
+ return None
325
+
326
+ return AssetReference(
327
+ name=name,
328
+ versions=version_refs,
329
+ created_at=created_at,
330
+ updated_at=updated_at,
331
+ )
common/core/task.py ADDED
@@ -0,0 +1,36 @@
1
+ from datetime import datetime
2
+ from enum import Enum
3
+
4
+ from common.message import Message
5
+
6
+
7
+ class TaskOutcome(str, Enum):
8
+ """
9
+ Task outcome status.
10
+ """
11
+
12
+ SUCCESS = "success"
13
+ FAILURE = "failure"
14
+
15
+
16
+ class TaskCompletion(Message):
17
+ """
18
+ Task completion message (does not include task ID, as that is the lookup key).
19
+ """
20
+
21
+ ark_name: str
22
+ ark_version: str
23
+ ark_hash: str
24
+ task_start_time: datetime
25
+ task_complete_time: datetime
26
+ outcome: TaskOutcome
27
+ result: dict | None = None
28
+
29
+
30
+ class TaskFileType(str, Enum):
31
+ """
32
+ Task file type.
33
+ """
34
+
35
+ MCAP = "mcap"
36
+ BUNDLE = "bundle"
@@ -0,0 +1,59 @@
1
+ from common.message.annotation import CircleAnnotation, ImageAnnotations, PointsAnnotation, PointsAnnotationType, TextAnnotation
2
+ from common.message.array import Array
3
+ from common.message.base import DeserializationError, Message, MessageError, MismatchError, SerializationError
4
+ from common.message.camera import CameraInfo
5
+ from common.message.color import Color
6
+ from common.message.frame import FrameTransform, FrameTransforms
7
+ from common.message.image import Image, ImageEncoding
8
+ from common.message.imu import ImuSample
9
+ from common.message.joint import JointState, JointStates, JointTarget, JointTargets
10
+ from common.message.log import Log, LogLevel
11
+ from common.message.pir import PirStatus
12
+ from common.message.point import Point2, Point3
13
+ from common.message.point_cloud import PointCloud
14
+ from common.message.pose import Pose
15
+ from common.message.quaternion import Quaternion
16
+ from common.message.radar import RadarDetection, RadarScan
17
+ from common.message.types import Bool, Float, Int, String
18
+ from common.message.vector import Vector2, Vector3
19
+
20
+ __all__ = [
21
+ "Array",
22
+ "Bool",
23
+ "CameraInfo",
24
+ "CircleAnnotation",
25
+ "Color",
26
+ "DeserializationError",
27
+ "Float",
28
+ "FrameTransform",
29
+ "FrameTransforms",
30
+ "Image",
31
+ "ImageAnnotations",
32
+ "ImageEncoding",
33
+ "ImuSample",
34
+ "Int",
35
+ "JointState",
36
+ "JointStates",
37
+ "JointTarget",
38
+ "JointTargets",
39
+ "Log",
40
+ "LogLevel",
41
+ "Message",
42
+ "MessageError",
43
+ "MismatchError",
44
+ "PirStatus",
45
+ "Point2",
46
+ "Point3",
47
+ "PointCloud",
48
+ "PointsAnnotation",
49
+ "PointsAnnotationType",
50
+ "Pose",
51
+ "Quaternion",
52
+ "RadarDetection",
53
+ "RadarScan",
54
+ "SerializationError",
55
+ "String",
56
+ "TextAnnotation",
57
+ "Vector2",
58
+ "Vector3",
59
+ ]
@@ -0,0 +1,89 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import IntEnum
4
+
5
+ from common.message.base import Message
6
+ from common.message.color import Color
7
+ from common.message.point import Point2
8
+
9
+
10
+ class PointsAnnotationType(IntEnum):
11
+ """
12
+ Type of points annotation.
13
+ """
14
+
15
+ UNKNOWN = 0
16
+ POINTS = 1
17
+ LINE_LOOP = 2
18
+ LINE_STRIP = 3
19
+ LINE_LIST = 4
20
+
21
+
22
+ class CircleAnnotation(Message):
23
+ """
24
+ A circle annotation on a 2D image.
25
+
26
+ Coordinates use the top-left corner of the top-left pixel as the origin.
27
+ """
28
+
29
+ timestamp_us: int
30
+ position: Point2
31
+ diameter: float
32
+ thickness: float
33
+ fill_color: Color
34
+ outline_color: Color
35
+
36
+
37
+ class PointsAnnotation(Message):
38
+ """
39
+ An array of points on a 2D image.
40
+
41
+ Coordinates use the top-left corner of the top-left pixel as the origin.
42
+ """
43
+
44
+ timestamp_us: int
45
+ type: PointsAnnotationType
46
+ points: list[Point2]
47
+ outline_color: Color
48
+ outline_colors: list[Color] | None = None
49
+ fill_color: Color | None = None
50
+ thickness: float
51
+
52
+
53
+ class TextAnnotation(Message):
54
+ """
55
+ A text label on a 2D image.
56
+
57
+ Position uses the bottom-left origin of the text label.
58
+ Coordinates use the top-left corner of the top-left pixel as the origin.
59
+ """
60
+
61
+ timestamp_us: int
62
+ position: Point2
63
+ text: str
64
+ font_size: float
65
+ text_color: Color
66
+ background_color: Color
67
+
68
+
69
+ class ImageAnnotations(Message):
70
+ """
71
+ Array of annotations for a 2D image.
72
+
73
+ Used in the Foxglove Image panel for visualization.
74
+ """
75
+
76
+ _type = "antioch/image_annotations"
77
+ circles: list[CircleAnnotation]
78
+ points: list[PointsAnnotation]
79
+ texts: list[TextAnnotation]
80
+
81
+ @classmethod
82
+ def empty(cls) -> ImageAnnotations:
83
+ """
84
+ Create an empty ImageAnnotations instance.
85
+
86
+ :return: ImageAnnotations with no annotations.
87
+ """
88
+
89
+ return cls(circles=[], points=[], texts=[])