foodforthought-cli 0.2.1__py3-none-any.whl → 0.2.4__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.
- ate/__init__.py +1 -1
- ate/bridge_server.py +622 -0
- ate/cli.py +2625 -242
- ate/compatibility.py +580 -0
- ate/generators/__init__.py +19 -0
- ate/generators/docker_generator.py +461 -0
- ate/generators/hardware_config.py +469 -0
- ate/generators/ros2_generator.py +617 -0
- ate/generators/skill_generator.py +783 -0
- ate/marketplace.py +524 -0
- ate/mcp_server.py +1341 -107
- ate/primitives.py +1016 -0
- ate/robot_setup.py +2222 -0
- ate/skill_schema.py +537 -0
- ate/telemetry/__init__.py +33 -0
- ate/telemetry/cli.py +455 -0
- ate/telemetry/collector.py +444 -0
- ate/telemetry/context.py +318 -0
- ate/telemetry/fleet_agent.py +419 -0
- ate/telemetry/formats/__init__.py +18 -0
- ate/telemetry/formats/hdf5_serializer.py +503 -0
- ate/telemetry/formats/mcap_serializer.py +457 -0
- ate/telemetry/types.py +334 -0
- foodforthought_cli-0.2.4.dist-info/METADATA +300 -0
- foodforthought_cli-0.2.4.dist-info/RECORD +44 -0
- foodforthought_cli-0.2.4.dist-info/top_level.txt +6 -0
- mechdog_labeled/__init__.py +3 -0
- mechdog_labeled/primitives.py +113 -0
- mechdog_labeled/servo_map.py +209 -0
- mechdog_output/__init__.py +3 -0
- mechdog_output/primitives.py +59 -0
- mechdog_output/servo_map.py +203 -0
- test_autodetect/__init__.py +3 -0
- test_autodetect/primitives.py +113 -0
- test_autodetect/servo_map.py +209 -0
- test_full_auto/__init__.py +3 -0
- test_full_auto/primitives.py +113 -0
- test_full_auto/servo_map.py +209 -0
- test_smart_detect/__init__.py +3 -0
- test_smart_detect/primitives.py +113 -0
- test_smart_detect/servo_map.py +209 -0
- foodforthought_cli-0.2.1.dist-info/METADATA +0 -151
- foodforthought_cli-0.2.1.dist-info/RECORD +0 -9
- foodforthought_cli-0.2.1.dist-info/top_level.txt +0 -1
- {foodforthought_cli-0.2.1.dist-info → foodforthought_cli-0.2.4.dist-info}/WHEEL +0 -0
- {foodforthought_cli-0.2.1.dist-info → foodforthought_cli-0.2.4.dist-info}/entry_points.txt +0 -0
ate/primitives.py
ADDED
|
@@ -0,0 +1,1016 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Primitive Registry - Catalog of all available robot primitives.
|
|
3
|
+
|
|
4
|
+
Primitives are the atomic building blocks of robot skills. This module
|
|
5
|
+
provides a registry of all available primitives with their signatures,
|
|
6
|
+
hardware requirements, and documentation.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Any, Dict, List, Optional, Set
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PrimitiveCategory(str, Enum):
|
|
15
|
+
"""Categories of primitives."""
|
|
16
|
+
MOTION = "motion" # Movement primitives
|
|
17
|
+
GRIPPER = "gripper" # Gripper/end-effector actions
|
|
18
|
+
SENSING = "sensing" # Sensor reading primitives
|
|
19
|
+
WAIT = "wait" # Timing and condition waits
|
|
20
|
+
CONTROL = "control" # Control flow primitives
|
|
21
|
+
COMMUNICATION = "communication" # Inter-system communication
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class ParameterDefinition:
|
|
26
|
+
"""Definition of a primitive parameter."""
|
|
27
|
+
type: str
|
|
28
|
+
description: str
|
|
29
|
+
required: bool = True
|
|
30
|
+
default: Optional[Any] = None
|
|
31
|
+
range: Optional[tuple] = None
|
|
32
|
+
units: Optional[str] = None
|
|
33
|
+
|
|
34
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
35
|
+
d = {"type": self.type, "description": self.description}
|
|
36
|
+
if not self.required:
|
|
37
|
+
d["required"] = False
|
|
38
|
+
if self.default is not None:
|
|
39
|
+
d["default"] = self.default
|
|
40
|
+
if self.range:
|
|
41
|
+
d["range"] = list(self.range)
|
|
42
|
+
if self.units:
|
|
43
|
+
d["units"] = self.units
|
|
44
|
+
return d
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class PrimitiveDefinition:
|
|
49
|
+
"""Complete definition of a primitive."""
|
|
50
|
+
name: str
|
|
51
|
+
description: str
|
|
52
|
+
category: PrimitiveCategory
|
|
53
|
+
parameters: Dict[str, ParameterDefinition]
|
|
54
|
+
returns: str
|
|
55
|
+
hardware: List[str]
|
|
56
|
+
blocking: bool = True # Whether this primitive blocks execution
|
|
57
|
+
timeout_default: Optional[float] = None
|
|
58
|
+
example: Optional[str] = None
|
|
59
|
+
|
|
60
|
+
def validate_parameters(self, provided: Dict[str, Any]) -> List[str]:
|
|
61
|
+
"""Validate provided parameters against this primitive's signature."""
|
|
62
|
+
errors = []
|
|
63
|
+
|
|
64
|
+
# Check required parameters
|
|
65
|
+
for name, param_def in self.parameters.items():
|
|
66
|
+
if param_def.required and name not in provided:
|
|
67
|
+
errors.append(f"Missing required parameter: {name}")
|
|
68
|
+
|
|
69
|
+
# Check for unknown parameters
|
|
70
|
+
for name in provided:
|
|
71
|
+
if name not in self.parameters:
|
|
72
|
+
errors.append(f"Unknown parameter: {name}")
|
|
73
|
+
|
|
74
|
+
# Validate types and ranges
|
|
75
|
+
for name, value in provided.items():
|
|
76
|
+
if name not in self.parameters:
|
|
77
|
+
continue
|
|
78
|
+
param_def = self.parameters[name]
|
|
79
|
+
|
|
80
|
+
# Range validation
|
|
81
|
+
if param_def.range and isinstance(value, (int, float)):
|
|
82
|
+
min_val, max_val = param_def.range
|
|
83
|
+
if value < min_val or value > max_val:
|
|
84
|
+
errors.append(
|
|
85
|
+
f"Parameter '{name}' value {value} out of range "
|
|
86
|
+
f"[{min_val}, {max_val}]"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return errors
|
|
90
|
+
|
|
91
|
+
def get_signature(self) -> str:
|
|
92
|
+
"""Get a function signature string for this primitive."""
|
|
93
|
+
params = []
|
|
94
|
+
for name, param_def in self.parameters.items():
|
|
95
|
+
if param_def.required:
|
|
96
|
+
params.append(f"{name}: {param_def.type}")
|
|
97
|
+
else:
|
|
98
|
+
default = repr(param_def.default)
|
|
99
|
+
params.append(f"{name}: {param_def.type} = {default}")
|
|
100
|
+
return f"{self.name}({', '.join(params)}) -> {self.returns}"
|
|
101
|
+
|
|
102
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
103
|
+
return {
|
|
104
|
+
"description": self.description,
|
|
105
|
+
"category": self.category.value,
|
|
106
|
+
"parameters": {
|
|
107
|
+
name: param.to_dict()
|
|
108
|
+
for name, param in self.parameters.items()
|
|
109
|
+
},
|
|
110
|
+
"returns": self.returns,
|
|
111
|
+
"hardware": self.hardware,
|
|
112
|
+
"blocking": self.blocking,
|
|
113
|
+
"timeout_default": self.timeout_default,
|
|
114
|
+
"example": self.example,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# =============================================================================
|
|
119
|
+
# PRIMITIVE REGISTRY
|
|
120
|
+
# =============================================================================
|
|
121
|
+
# All available primitives organized by category
|
|
122
|
+
|
|
123
|
+
PRIMITIVE_REGISTRY: Dict[str, PrimitiveDefinition] = {}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def register_primitive(prim: PrimitiveDefinition) -> PrimitiveDefinition:
|
|
127
|
+
"""Register a primitive in the global registry."""
|
|
128
|
+
PRIMITIVE_REGISTRY[prim.name] = prim
|
|
129
|
+
return prim
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# -----------------------------------------------------------------------------
|
|
133
|
+
# Motion Primitives
|
|
134
|
+
# -----------------------------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
register_primitive(PrimitiveDefinition(
|
|
137
|
+
name="move_to_pose",
|
|
138
|
+
description="Move end effector to target pose in Cartesian space",
|
|
139
|
+
category=PrimitiveCategory.MOTION,
|
|
140
|
+
parameters={
|
|
141
|
+
"pose": ParameterDefinition(
|
|
142
|
+
type="Pose",
|
|
143
|
+
description="Target pose with position (x, y, z) and orientation (quaternion)",
|
|
144
|
+
required=True
|
|
145
|
+
),
|
|
146
|
+
"speed": ParameterDefinition(
|
|
147
|
+
type="float",
|
|
148
|
+
description="Movement speed as fraction of max (0.0-1.0)",
|
|
149
|
+
required=False,
|
|
150
|
+
default=0.5,
|
|
151
|
+
range=(0.0, 1.0)
|
|
152
|
+
),
|
|
153
|
+
"acceleration": ParameterDefinition(
|
|
154
|
+
type="float",
|
|
155
|
+
description="Acceleration as fraction of max (0.0-1.0)",
|
|
156
|
+
required=False,
|
|
157
|
+
default=0.3,
|
|
158
|
+
range=(0.0, 1.0)
|
|
159
|
+
),
|
|
160
|
+
"blend_radius": ParameterDefinition(
|
|
161
|
+
type="float",
|
|
162
|
+
description="Blend radius for smooth path (meters)",
|
|
163
|
+
required=False,
|
|
164
|
+
default=0.0,
|
|
165
|
+
units="meters"
|
|
166
|
+
),
|
|
167
|
+
},
|
|
168
|
+
returns="bool",
|
|
169
|
+
hardware=["arm"],
|
|
170
|
+
blocking=True,
|
|
171
|
+
timeout_default=30.0,
|
|
172
|
+
example='move_to_pose(pose={"position": [0.5, 0.0, 0.3], "orientation": [0, 0, 0, 1]})'
|
|
173
|
+
))
|
|
174
|
+
|
|
175
|
+
register_primitive(PrimitiveDefinition(
|
|
176
|
+
name="move_to_joints",
|
|
177
|
+
description="Move to target joint configuration",
|
|
178
|
+
category=PrimitiveCategory.MOTION,
|
|
179
|
+
parameters={
|
|
180
|
+
"joint_positions": ParameterDefinition(
|
|
181
|
+
type="array",
|
|
182
|
+
description="Target joint positions in radians",
|
|
183
|
+
required=True
|
|
184
|
+
),
|
|
185
|
+
"speed": ParameterDefinition(
|
|
186
|
+
type="float",
|
|
187
|
+
description="Movement speed as fraction of max (0.0-1.0)",
|
|
188
|
+
required=False,
|
|
189
|
+
default=0.5,
|
|
190
|
+
range=(0.0, 1.0)
|
|
191
|
+
),
|
|
192
|
+
"acceleration": ParameterDefinition(
|
|
193
|
+
type="float",
|
|
194
|
+
description="Acceleration as fraction of max (0.0-1.0)",
|
|
195
|
+
required=False,
|
|
196
|
+
default=0.3,
|
|
197
|
+
range=(0.0, 1.0)
|
|
198
|
+
),
|
|
199
|
+
},
|
|
200
|
+
returns="bool",
|
|
201
|
+
hardware=["arm"],
|
|
202
|
+
blocking=True,
|
|
203
|
+
timeout_default=30.0,
|
|
204
|
+
example="move_to_joints(joint_positions=[0, -1.57, 1.57, 0, 0, 0])"
|
|
205
|
+
))
|
|
206
|
+
|
|
207
|
+
register_primitive(PrimitiveDefinition(
|
|
208
|
+
name="move_linear",
|
|
209
|
+
description="Move end effector in a straight line to target pose",
|
|
210
|
+
category=PrimitiveCategory.MOTION,
|
|
211
|
+
parameters={
|
|
212
|
+
"pose": ParameterDefinition(
|
|
213
|
+
type="Pose",
|
|
214
|
+
description="Target pose",
|
|
215
|
+
required=True
|
|
216
|
+
),
|
|
217
|
+
"speed": ParameterDefinition(
|
|
218
|
+
type="float",
|
|
219
|
+
description="Linear speed (m/s)",
|
|
220
|
+
required=False,
|
|
221
|
+
default=0.1,
|
|
222
|
+
range=(0.001, 1.0),
|
|
223
|
+
units="m/s"
|
|
224
|
+
),
|
|
225
|
+
},
|
|
226
|
+
returns="bool",
|
|
227
|
+
hardware=["arm"],
|
|
228
|
+
blocking=True,
|
|
229
|
+
timeout_default=60.0,
|
|
230
|
+
example='move_linear(pose={"position": [0.5, 0.0, 0.3], "orientation": [0, 0, 0, 1]}, speed=0.05)'
|
|
231
|
+
))
|
|
232
|
+
|
|
233
|
+
register_primitive(PrimitiveDefinition(
|
|
234
|
+
name="move_relative",
|
|
235
|
+
description="Move end effector relative to current pose",
|
|
236
|
+
category=PrimitiveCategory.MOTION,
|
|
237
|
+
parameters={
|
|
238
|
+
"delta_position": ParameterDefinition(
|
|
239
|
+
type="array",
|
|
240
|
+
description="Relative position change [dx, dy, dz] in meters",
|
|
241
|
+
required=False,
|
|
242
|
+
default=[0, 0, 0]
|
|
243
|
+
),
|
|
244
|
+
"delta_rotation": ParameterDefinition(
|
|
245
|
+
type="array",
|
|
246
|
+
description="Relative rotation [rx, ry, rz] in radians",
|
|
247
|
+
required=False,
|
|
248
|
+
default=[0, 0, 0]
|
|
249
|
+
),
|
|
250
|
+
"frame": ParameterDefinition(
|
|
251
|
+
type="string",
|
|
252
|
+
description="Reference frame: 'base', 'tool', or 'world'",
|
|
253
|
+
required=False,
|
|
254
|
+
default="tool"
|
|
255
|
+
),
|
|
256
|
+
"speed": ParameterDefinition(
|
|
257
|
+
type="float",
|
|
258
|
+
description="Movement speed as fraction of max (0.0-1.0)",
|
|
259
|
+
required=False,
|
|
260
|
+
default=0.3,
|
|
261
|
+
range=(0.0, 1.0)
|
|
262
|
+
),
|
|
263
|
+
},
|
|
264
|
+
returns="bool",
|
|
265
|
+
hardware=["arm"],
|
|
266
|
+
blocking=True,
|
|
267
|
+
timeout_default=30.0,
|
|
268
|
+
example="move_relative(delta_position=[0, 0, -0.1], frame='tool')"
|
|
269
|
+
))
|
|
270
|
+
|
|
271
|
+
register_primitive(PrimitiveDefinition(
|
|
272
|
+
name="move_servo",
|
|
273
|
+
description="Move a single servo to target position",
|
|
274
|
+
category=PrimitiveCategory.MOTION,
|
|
275
|
+
parameters={
|
|
276
|
+
"servo_id": ParameterDefinition(
|
|
277
|
+
type="int",
|
|
278
|
+
description="Servo ID",
|
|
279
|
+
required=True
|
|
280
|
+
),
|
|
281
|
+
"position": ParameterDefinition(
|
|
282
|
+
type="float",
|
|
283
|
+
description="Target position (unit depends on servo type)",
|
|
284
|
+
required=True
|
|
285
|
+
),
|
|
286
|
+
"time_ms": ParameterDefinition(
|
|
287
|
+
type="int",
|
|
288
|
+
description="Movement duration in milliseconds",
|
|
289
|
+
required=False,
|
|
290
|
+
default=500
|
|
291
|
+
),
|
|
292
|
+
},
|
|
293
|
+
returns="bool",
|
|
294
|
+
hardware=["arm"],
|
|
295
|
+
blocking=True,
|
|
296
|
+
timeout_default=5.0,
|
|
297
|
+
example="move_servo(servo_id=1, position=512, time_ms=1000)"
|
|
298
|
+
))
|
|
299
|
+
|
|
300
|
+
register_primitive(PrimitiveDefinition(
|
|
301
|
+
name="move_servos",
|
|
302
|
+
description="Move multiple servos simultaneously",
|
|
303
|
+
category=PrimitiveCategory.MOTION,
|
|
304
|
+
parameters={
|
|
305
|
+
"commands": ParameterDefinition(
|
|
306
|
+
type="array",
|
|
307
|
+
description="List of {servo_id, position, time_ms} commands",
|
|
308
|
+
required=True
|
|
309
|
+
),
|
|
310
|
+
},
|
|
311
|
+
returns="bool",
|
|
312
|
+
hardware=["arm"],
|
|
313
|
+
blocking=True,
|
|
314
|
+
timeout_default=5.0,
|
|
315
|
+
example="move_servos(commands=[{'servo_id': 1, 'position': 512}, {'servo_id': 2, 'position': 600}])"
|
|
316
|
+
))
|
|
317
|
+
|
|
318
|
+
register_primitive(PrimitiveDefinition(
|
|
319
|
+
name="execute_trajectory",
|
|
320
|
+
description="Execute a pre-planned trajectory",
|
|
321
|
+
category=PrimitiveCategory.MOTION,
|
|
322
|
+
parameters={
|
|
323
|
+
"trajectory": ParameterDefinition(
|
|
324
|
+
type="Trajectory",
|
|
325
|
+
description="Trajectory with waypoints and timing",
|
|
326
|
+
required=True
|
|
327
|
+
),
|
|
328
|
+
"speed_scale": ParameterDefinition(
|
|
329
|
+
type="float",
|
|
330
|
+
description="Speed scaling factor (0.0-2.0)",
|
|
331
|
+
required=False,
|
|
332
|
+
default=1.0,
|
|
333
|
+
range=(0.1, 2.0)
|
|
334
|
+
),
|
|
335
|
+
},
|
|
336
|
+
returns="bool",
|
|
337
|
+
hardware=["arm"],
|
|
338
|
+
blocking=True,
|
|
339
|
+
timeout_default=300.0,
|
|
340
|
+
example="execute_trajectory(trajectory=planned_traj, speed_scale=0.8)"
|
|
341
|
+
))
|
|
342
|
+
|
|
343
|
+
register_primitive(PrimitiveDefinition(
|
|
344
|
+
name="home",
|
|
345
|
+
description="Move robot to home/neutral position",
|
|
346
|
+
category=PrimitiveCategory.MOTION,
|
|
347
|
+
parameters={
|
|
348
|
+
"speed": ParameterDefinition(
|
|
349
|
+
type="float",
|
|
350
|
+
description="Movement speed as fraction of max (0.0-1.0)",
|
|
351
|
+
required=False,
|
|
352
|
+
default=0.3,
|
|
353
|
+
range=(0.0, 1.0)
|
|
354
|
+
),
|
|
355
|
+
},
|
|
356
|
+
returns="bool",
|
|
357
|
+
hardware=["arm"],
|
|
358
|
+
blocking=True,
|
|
359
|
+
timeout_default=30.0,
|
|
360
|
+
example="home(speed=0.5)"
|
|
361
|
+
))
|
|
362
|
+
|
|
363
|
+
# -----------------------------------------------------------------------------
|
|
364
|
+
# Gripper Primitives
|
|
365
|
+
# -----------------------------------------------------------------------------
|
|
366
|
+
|
|
367
|
+
register_primitive(PrimitiveDefinition(
|
|
368
|
+
name="close_gripper",
|
|
369
|
+
description="Close gripper to grasp an object",
|
|
370
|
+
category=PrimitiveCategory.GRIPPER,
|
|
371
|
+
parameters={
|
|
372
|
+
"force": ParameterDefinition(
|
|
373
|
+
type="float",
|
|
374
|
+
description="Gripping force in Newtons",
|
|
375
|
+
required=False,
|
|
376
|
+
default=10.0,
|
|
377
|
+
range=(1.0, 100.0),
|
|
378
|
+
units="N"
|
|
379
|
+
),
|
|
380
|
+
"width": ParameterDefinition(
|
|
381
|
+
type="float",
|
|
382
|
+
description="Target gripper width (optional, for partial close)",
|
|
383
|
+
required=False,
|
|
384
|
+
default=None,
|
|
385
|
+
units="meters"
|
|
386
|
+
),
|
|
387
|
+
"speed": ParameterDefinition(
|
|
388
|
+
type="float",
|
|
389
|
+
description="Closing speed as fraction of max (0.0-1.0)",
|
|
390
|
+
required=False,
|
|
391
|
+
default=0.5,
|
|
392
|
+
range=(0.0, 1.0)
|
|
393
|
+
),
|
|
394
|
+
},
|
|
395
|
+
returns="bool",
|
|
396
|
+
hardware=["gripper"],
|
|
397
|
+
blocking=True,
|
|
398
|
+
timeout_default=5.0,
|
|
399
|
+
example="close_gripper(force=20.0)"
|
|
400
|
+
))
|
|
401
|
+
|
|
402
|
+
register_primitive(PrimitiveDefinition(
|
|
403
|
+
name="open_gripper",
|
|
404
|
+
description="Open gripper to release an object",
|
|
405
|
+
category=PrimitiveCategory.GRIPPER,
|
|
406
|
+
parameters={
|
|
407
|
+
"width": ParameterDefinition(
|
|
408
|
+
type="float",
|
|
409
|
+
description="Target opening width",
|
|
410
|
+
required=False,
|
|
411
|
+
default=None,
|
|
412
|
+
units="meters"
|
|
413
|
+
),
|
|
414
|
+
"speed": ParameterDefinition(
|
|
415
|
+
type="float",
|
|
416
|
+
description="Opening speed as fraction of max (0.0-1.0)",
|
|
417
|
+
required=False,
|
|
418
|
+
default=0.5,
|
|
419
|
+
range=(0.0, 1.0)
|
|
420
|
+
),
|
|
421
|
+
},
|
|
422
|
+
returns="bool",
|
|
423
|
+
hardware=["gripper"],
|
|
424
|
+
blocking=True,
|
|
425
|
+
timeout_default=5.0,
|
|
426
|
+
example="open_gripper(width=0.08)"
|
|
427
|
+
))
|
|
428
|
+
|
|
429
|
+
register_primitive(PrimitiveDefinition(
|
|
430
|
+
name="set_gripper_position",
|
|
431
|
+
description="Set gripper to specific position",
|
|
432
|
+
category=PrimitiveCategory.GRIPPER,
|
|
433
|
+
parameters={
|
|
434
|
+
"position": ParameterDefinition(
|
|
435
|
+
type="float",
|
|
436
|
+
description="Target position (0.0=closed, 1.0=fully open)",
|
|
437
|
+
required=True,
|
|
438
|
+
range=(0.0, 1.0)
|
|
439
|
+
),
|
|
440
|
+
"speed": ParameterDefinition(
|
|
441
|
+
type="float",
|
|
442
|
+
description="Movement speed as fraction of max (0.0-1.0)",
|
|
443
|
+
required=False,
|
|
444
|
+
default=0.5,
|
|
445
|
+
range=(0.0, 1.0)
|
|
446
|
+
),
|
|
447
|
+
},
|
|
448
|
+
returns="bool",
|
|
449
|
+
hardware=["gripper"],
|
|
450
|
+
blocking=True,
|
|
451
|
+
timeout_default=5.0,
|
|
452
|
+
example="set_gripper_position(position=0.5)"
|
|
453
|
+
))
|
|
454
|
+
|
|
455
|
+
# -----------------------------------------------------------------------------
|
|
456
|
+
# Sensing Primitives
|
|
457
|
+
# -----------------------------------------------------------------------------
|
|
458
|
+
|
|
459
|
+
register_primitive(PrimitiveDefinition(
|
|
460
|
+
name="read_position",
|
|
461
|
+
description="Read current end effector pose",
|
|
462
|
+
category=PrimitiveCategory.SENSING,
|
|
463
|
+
parameters={
|
|
464
|
+
"frame": ParameterDefinition(
|
|
465
|
+
type="string",
|
|
466
|
+
description="Reference frame: 'base' or 'world'",
|
|
467
|
+
required=False,
|
|
468
|
+
default="base"
|
|
469
|
+
),
|
|
470
|
+
},
|
|
471
|
+
returns="Pose",
|
|
472
|
+
hardware=["arm"],
|
|
473
|
+
blocking=False,
|
|
474
|
+
example="current_pose = read_position()"
|
|
475
|
+
))
|
|
476
|
+
|
|
477
|
+
register_primitive(PrimitiveDefinition(
|
|
478
|
+
name="read_joints",
|
|
479
|
+
description="Read current joint positions",
|
|
480
|
+
category=PrimitiveCategory.SENSING,
|
|
481
|
+
parameters={},
|
|
482
|
+
returns="array",
|
|
483
|
+
hardware=["arm"],
|
|
484
|
+
blocking=False,
|
|
485
|
+
example="joint_positions = read_joints()"
|
|
486
|
+
))
|
|
487
|
+
|
|
488
|
+
register_primitive(PrimitiveDefinition(
|
|
489
|
+
name="read_force",
|
|
490
|
+
description="Read force/torque sensor values",
|
|
491
|
+
category=PrimitiveCategory.SENSING,
|
|
492
|
+
parameters={
|
|
493
|
+
"frame": ParameterDefinition(
|
|
494
|
+
type="string",
|
|
495
|
+
description="Reference frame: 'sensor', 'base', or 'world'",
|
|
496
|
+
required=False,
|
|
497
|
+
default="sensor"
|
|
498
|
+
),
|
|
499
|
+
},
|
|
500
|
+
returns="array",
|
|
501
|
+
hardware=["force_sensor"],
|
|
502
|
+
blocking=False,
|
|
503
|
+
example="forces = read_force() # Returns [fx, fy, fz, tx, ty, tz]"
|
|
504
|
+
))
|
|
505
|
+
|
|
506
|
+
register_primitive(PrimitiveDefinition(
|
|
507
|
+
name="read_gripper_state",
|
|
508
|
+
description="Read gripper position and force",
|
|
509
|
+
category=PrimitiveCategory.SENSING,
|
|
510
|
+
parameters={},
|
|
511
|
+
returns="dict",
|
|
512
|
+
hardware=["gripper"],
|
|
513
|
+
blocking=False,
|
|
514
|
+
example="state = read_gripper_state() # Returns {'position': 0.04, 'force': 5.2, 'object_detected': True}"
|
|
515
|
+
))
|
|
516
|
+
|
|
517
|
+
register_primitive(PrimitiveDefinition(
|
|
518
|
+
name="read_servo_state",
|
|
519
|
+
description="Read state of a single servo",
|
|
520
|
+
category=PrimitiveCategory.SENSING,
|
|
521
|
+
parameters={
|
|
522
|
+
"servo_id": ParameterDefinition(
|
|
523
|
+
type="int",
|
|
524
|
+
description="Servo ID to read",
|
|
525
|
+
required=True
|
|
526
|
+
),
|
|
527
|
+
},
|
|
528
|
+
returns="dict",
|
|
529
|
+
hardware=["arm"],
|
|
530
|
+
blocking=False,
|
|
531
|
+
example="state = read_servo_state(servo_id=1)"
|
|
532
|
+
))
|
|
533
|
+
|
|
534
|
+
register_primitive(PrimitiveDefinition(
|
|
535
|
+
name="detect_object",
|
|
536
|
+
description="Detect objects in camera view",
|
|
537
|
+
category=PrimitiveCategory.SENSING,
|
|
538
|
+
parameters={
|
|
539
|
+
"object_type": ParameterDefinition(
|
|
540
|
+
type="string",
|
|
541
|
+
description="Type of object to detect (or 'any')",
|
|
542
|
+
required=False,
|
|
543
|
+
default="any"
|
|
544
|
+
),
|
|
545
|
+
"confidence_threshold": ParameterDefinition(
|
|
546
|
+
type="float",
|
|
547
|
+
description="Minimum detection confidence (0.0-1.0)",
|
|
548
|
+
required=False,
|
|
549
|
+
default=0.7,
|
|
550
|
+
range=(0.0, 1.0)
|
|
551
|
+
),
|
|
552
|
+
},
|
|
553
|
+
returns="array",
|
|
554
|
+
hardware=["camera"],
|
|
555
|
+
blocking=False,
|
|
556
|
+
example="objects = detect_object(object_type='cube', confidence_threshold=0.8)"
|
|
557
|
+
))
|
|
558
|
+
|
|
559
|
+
register_primitive(PrimitiveDefinition(
|
|
560
|
+
name="get_object_pose",
|
|
561
|
+
description="Get pose of a detected object",
|
|
562
|
+
category=PrimitiveCategory.SENSING,
|
|
563
|
+
parameters={
|
|
564
|
+
"object_id": ParameterDefinition(
|
|
565
|
+
type="string",
|
|
566
|
+
description="ID of detected object",
|
|
567
|
+
required=True
|
|
568
|
+
),
|
|
569
|
+
"frame": ParameterDefinition(
|
|
570
|
+
type="string",
|
|
571
|
+
description="Reference frame for pose",
|
|
572
|
+
required=False,
|
|
573
|
+
default="base"
|
|
574
|
+
),
|
|
575
|
+
},
|
|
576
|
+
returns="Pose",
|
|
577
|
+
hardware=["camera"],
|
|
578
|
+
blocking=False,
|
|
579
|
+
example="object_pose = get_object_pose(object_id='cube_1')"
|
|
580
|
+
))
|
|
581
|
+
|
|
582
|
+
# -----------------------------------------------------------------------------
|
|
583
|
+
# Wait Primitives
|
|
584
|
+
# -----------------------------------------------------------------------------
|
|
585
|
+
|
|
586
|
+
register_primitive(PrimitiveDefinition(
|
|
587
|
+
name="wait",
|
|
588
|
+
description="Wait for specified duration",
|
|
589
|
+
category=PrimitiveCategory.WAIT,
|
|
590
|
+
parameters={
|
|
591
|
+
"duration": ParameterDefinition(
|
|
592
|
+
type="float",
|
|
593
|
+
description="Wait duration in seconds",
|
|
594
|
+
required=True,
|
|
595
|
+
range=(0.0, 300.0),
|
|
596
|
+
units="seconds"
|
|
597
|
+
),
|
|
598
|
+
},
|
|
599
|
+
returns="bool",
|
|
600
|
+
hardware=[],
|
|
601
|
+
blocking=True,
|
|
602
|
+
example="wait(duration=2.0)"
|
|
603
|
+
))
|
|
604
|
+
|
|
605
|
+
register_primitive(PrimitiveDefinition(
|
|
606
|
+
name="wait_for_contact",
|
|
607
|
+
description="Wait until force threshold is detected",
|
|
608
|
+
category=PrimitiveCategory.WAIT,
|
|
609
|
+
parameters={
|
|
610
|
+
"force_threshold": ParameterDefinition(
|
|
611
|
+
type="float",
|
|
612
|
+
description="Force threshold in Newtons",
|
|
613
|
+
required=False,
|
|
614
|
+
default=5.0,
|
|
615
|
+
range=(0.1, 100.0),
|
|
616
|
+
units="N"
|
|
617
|
+
),
|
|
618
|
+
"timeout": ParameterDefinition(
|
|
619
|
+
type="float",
|
|
620
|
+
description="Maximum wait time in seconds",
|
|
621
|
+
required=False,
|
|
622
|
+
default=10.0,
|
|
623
|
+
units="seconds"
|
|
624
|
+
),
|
|
625
|
+
"direction": ParameterDefinition(
|
|
626
|
+
type="string",
|
|
627
|
+
description="Force direction to monitor: 'any', 'x', 'y', 'z', '-x', '-y', '-z'",
|
|
628
|
+
required=False,
|
|
629
|
+
default="any"
|
|
630
|
+
),
|
|
631
|
+
},
|
|
632
|
+
returns="bool",
|
|
633
|
+
hardware=["force_sensor"],
|
|
634
|
+
blocking=True,
|
|
635
|
+
timeout_default=10.0,
|
|
636
|
+
example="contact = wait_for_contact(force_threshold=3.0, timeout=5.0)"
|
|
637
|
+
))
|
|
638
|
+
|
|
639
|
+
register_primitive(PrimitiveDefinition(
|
|
640
|
+
name="wait_for_motion_complete",
|
|
641
|
+
description="Wait until current motion is complete",
|
|
642
|
+
category=PrimitiveCategory.WAIT,
|
|
643
|
+
parameters={
|
|
644
|
+
"timeout": ParameterDefinition(
|
|
645
|
+
type="float",
|
|
646
|
+
description="Maximum wait time in seconds",
|
|
647
|
+
required=False,
|
|
648
|
+
default=30.0,
|
|
649
|
+
units="seconds"
|
|
650
|
+
),
|
|
651
|
+
},
|
|
652
|
+
returns="bool",
|
|
653
|
+
hardware=["arm"],
|
|
654
|
+
blocking=True,
|
|
655
|
+
timeout_default=30.0,
|
|
656
|
+
example="wait_for_motion_complete()"
|
|
657
|
+
))
|
|
658
|
+
|
|
659
|
+
register_primitive(PrimitiveDefinition(
|
|
660
|
+
name="wait_for_gripper",
|
|
661
|
+
description="Wait until gripper motion is complete",
|
|
662
|
+
category=PrimitiveCategory.WAIT,
|
|
663
|
+
parameters={
|
|
664
|
+
"timeout": ParameterDefinition(
|
|
665
|
+
type="float",
|
|
666
|
+
description="Maximum wait time in seconds",
|
|
667
|
+
required=False,
|
|
668
|
+
default=5.0,
|
|
669
|
+
units="seconds"
|
|
670
|
+
),
|
|
671
|
+
},
|
|
672
|
+
returns="bool",
|
|
673
|
+
hardware=["gripper"],
|
|
674
|
+
blocking=True,
|
|
675
|
+
timeout_default=5.0,
|
|
676
|
+
example="wait_for_gripper()"
|
|
677
|
+
))
|
|
678
|
+
|
|
679
|
+
register_primitive(PrimitiveDefinition(
|
|
680
|
+
name="wait_for_condition",
|
|
681
|
+
description="Wait until a condition is met",
|
|
682
|
+
category=PrimitiveCategory.WAIT,
|
|
683
|
+
parameters={
|
|
684
|
+
"condition": ParameterDefinition(
|
|
685
|
+
type="string",
|
|
686
|
+
description="Condition expression to evaluate",
|
|
687
|
+
required=True
|
|
688
|
+
),
|
|
689
|
+
"timeout": ParameterDefinition(
|
|
690
|
+
type="float",
|
|
691
|
+
description="Maximum wait time in seconds",
|
|
692
|
+
required=False,
|
|
693
|
+
default=30.0,
|
|
694
|
+
units="seconds"
|
|
695
|
+
),
|
|
696
|
+
"poll_rate": ParameterDefinition(
|
|
697
|
+
type="float",
|
|
698
|
+
description="Condition check frequency in Hz",
|
|
699
|
+
required=False,
|
|
700
|
+
default=10.0,
|
|
701
|
+
units="Hz"
|
|
702
|
+
),
|
|
703
|
+
},
|
|
704
|
+
returns="bool",
|
|
705
|
+
hardware=[],
|
|
706
|
+
blocking=True,
|
|
707
|
+
timeout_default=30.0,
|
|
708
|
+
example="wait_for_condition(condition='gripper.object_detected', timeout=10.0)"
|
|
709
|
+
))
|
|
710
|
+
|
|
711
|
+
# -----------------------------------------------------------------------------
|
|
712
|
+
# Control Primitives
|
|
713
|
+
# -----------------------------------------------------------------------------
|
|
714
|
+
|
|
715
|
+
register_primitive(PrimitiveDefinition(
|
|
716
|
+
name="set_torque",
|
|
717
|
+
description="Enable or disable servo torque",
|
|
718
|
+
category=PrimitiveCategory.CONTROL,
|
|
719
|
+
parameters={
|
|
720
|
+
"enabled": ParameterDefinition(
|
|
721
|
+
type="bool",
|
|
722
|
+
description="Enable (True) or disable (False) torque",
|
|
723
|
+
required=True
|
|
724
|
+
),
|
|
725
|
+
"servo_ids": ParameterDefinition(
|
|
726
|
+
type="array",
|
|
727
|
+
description="List of servo IDs (empty for all)",
|
|
728
|
+
required=False,
|
|
729
|
+
default=[]
|
|
730
|
+
),
|
|
731
|
+
},
|
|
732
|
+
returns="bool",
|
|
733
|
+
hardware=["arm"],
|
|
734
|
+
blocking=False,
|
|
735
|
+
example="set_torque(enabled=False) # Disable all servos"
|
|
736
|
+
))
|
|
737
|
+
|
|
738
|
+
register_primitive(PrimitiveDefinition(
|
|
739
|
+
name="set_compliance",
|
|
740
|
+
description="Set servo compliance/stiffness mode",
|
|
741
|
+
category=PrimitiveCategory.CONTROL,
|
|
742
|
+
parameters={
|
|
743
|
+
"mode": ParameterDefinition(
|
|
744
|
+
type="string",
|
|
745
|
+
description="Compliance mode: 'stiff', 'soft', 'free'",
|
|
746
|
+
required=True
|
|
747
|
+
),
|
|
748
|
+
"servo_ids": ParameterDefinition(
|
|
749
|
+
type="array",
|
|
750
|
+
description="List of servo IDs (empty for all)",
|
|
751
|
+
required=False,
|
|
752
|
+
default=[]
|
|
753
|
+
),
|
|
754
|
+
},
|
|
755
|
+
returns="bool",
|
|
756
|
+
hardware=["arm"],
|
|
757
|
+
blocking=False,
|
|
758
|
+
example="set_compliance(mode='soft', servo_ids=[1, 2, 3])"
|
|
759
|
+
))
|
|
760
|
+
|
|
761
|
+
register_primitive(PrimitiveDefinition(
|
|
762
|
+
name="set_speed_limit",
|
|
763
|
+
description="Set maximum speed for subsequent motions",
|
|
764
|
+
category=PrimitiveCategory.CONTROL,
|
|
765
|
+
parameters={
|
|
766
|
+
"max_speed": ParameterDefinition(
|
|
767
|
+
type="float",
|
|
768
|
+
description="Maximum speed as fraction of absolute max (0.0-1.0)",
|
|
769
|
+
required=True,
|
|
770
|
+
range=(0.0, 1.0)
|
|
771
|
+
),
|
|
772
|
+
},
|
|
773
|
+
returns="bool",
|
|
774
|
+
hardware=["arm"],
|
|
775
|
+
blocking=False,
|
|
776
|
+
example="set_speed_limit(max_speed=0.3)"
|
|
777
|
+
))
|
|
778
|
+
|
|
779
|
+
register_primitive(PrimitiveDefinition(
|
|
780
|
+
name="stop",
|
|
781
|
+
description="Immediately stop all motion",
|
|
782
|
+
category=PrimitiveCategory.CONTROL,
|
|
783
|
+
parameters={
|
|
784
|
+
"deceleration": ParameterDefinition(
|
|
785
|
+
type="float",
|
|
786
|
+
description="Deceleration rate (0.0=instant, 1.0=smooth)",
|
|
787
|
+
required=False,
|
|
788
|
+
default=0.5,
|
|
789
|
+
range=(0.0, 1.0)
|
|
790
|
+
),
|
|
791
|
+
},
|
|
792
|
+
returns="bool",
|
|
793
|
+
hardware=["arm"],
|
|
794
|
+
blocking=True,
|
|
795
|
+
timeout_default=2.0,
|
|
796
|
+
example="stop(deceleration=0.0) # Emergency stop"
|
|
797
|
+
))
|
|
798
|
+
|
|
799
|
+
register_primitive(PrimitiveDefinition(
|
|
800
|
+
name="enable_force_control",
|
|
801
|
+
description="Enable force control mode",
|
|
802
|
+
category=PrimitiveCategory.CONTROL,
|
|
803
|
+
parameters={
|
|
804
|
+
"target_force": ParameterDefinition(
|
|
805
|
+
type="array",
|
|
806
|
+
description="Target force/torque [fx, fy, fz, tx, ty, tz]",
|
|
807
|
+
required=True
|
|
808
|
+
),
|
|
809
|
+
"stiffness": ParameterDefinition(
|
|
810
|
+
type="array",
|
|
811
|
+
description="Stiffness for each axis",
|
|
812
|
+
required=False,
|
|
813
|
+
default=[1000, 1000, 1000, 100, 100, 100]
|
|
814
|
+
),
|
|
815
|
+
},
|
|
816
|
+
returns="bool",
|
|
817
|
+
hardware=["arm", "force_sensor"],
|
|
818
|
+
blocking=False,
|
|
819
|
+
example="enable_force_control(target_force=[0, 0, -10, 0, 0, 0])"
|
|
820
|
+
))
|
|
821
|
+
|
|
822
|
+
register_primitive(PrimitiveDefinition(
|
|
823
|
+
name="disable_force_control",
|
|
824
|
+
description="Disable force control mode",
|
|
825
|
+
category=PrimitiveCategory.CONTROL,
|
|
826
|
+
parameters={},
|
|
827
|
+
returns="bool",
|
|
828
|
+
hardware=["arm"],
|
|
829
|
+
blocking=False,
|
|
830
|
+
example="disable_force_control()"
|
|
831
|
+
))
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
# =============================================================================
|
|
835
|
+
# REGISTRY FUNCTIONS
|
|
836
|
+
# =============================================================================
|
|
837
|
+
|
|
838
|
+
def get_primitive(name: str) -> Optional[PrimitiveDefinition]:
|
|
839
|
+
"""Get a primitive definition by name."""
|
|
840
|
+
return PRIMITIVE_REGISTRY.get(name)
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
def list_primitives(
|
|
844
|
+
category: Optional[PrimitiveCategory] = None,
|
|
845
|
+
hardware: Optional[str] = None
|
|
846
|
+
) -> List[PrimitiveDefinition]:
|
|
847
|
+
"""
|
|
848
|
+
List all primitives, optionally filtered by category or hardware.
|
|
849
|
+
|
|
850
|
+
Args:
|
|
851
|
+
category: Filter by primitive category
|
|
852
|
+
hardware: Filter by required hardware type
|
|
853
|
+
|
|
854
|
+
Returns:
|
|
855
|
+
List of matching primitive definitions
|
|
856
|
+
"""
|
|
857
|
+
primitives = list(PRIMITIVE_REGISTRY.values())
|
|
858
|
+
|
|
859
|
+
if category:
|
|
860
|
+
primitives = [p for p in primitives if p.category == category]
|
|
861
|
+
|
|
862
|
+
if hardware:
|
|
863
|
+
primitives = [p for p in primitives if hardware in p.hardware]
|
|
864
|
+
|
|
865
|
+
return primitives
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
def get_primitive_names() -> List[str]:
|
|
869
|
+
"""Get all registered primitive names."""
|
|
870
|
+
return list(PRIMITIVE_REGISTRY.keys())
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
def validate_primitive_chain(primitives: List[str]) -> List[str]:
|
|
874
|
+
"""
|
|
875
|
+
Validate a chain of primitives for a skill.
|
|
876
|
+
|
|
877
|
+
Checks that:
|
|
878
|
+
- All primitives exist
|
|
879
|
+
- Hardware requirements are consistent
|
|
880
|
+
- No obvious sequencing issues
|
|
881
|
+
|
|
882
|
+
Args:
|
|
883
|
+
primitives: List of primitive names
|
|
884
|
+
|
|
885
|
+
Returns:
|
|
886
|
+
List of validation errors (empty if valid)
|
|
887
|
+
"""
|
|
888
|
+
errors = []
|
|
889
|
+
|
|
890
|
+
# Check all primitives exist
|
|
891
|
+
for name in primitives:
|
|
892
|
+
if name not in PRIMITIVE_REGISTRY:
|
|
893
|
+
errors.append(f"Unknown primitive: {name}")
|
|
894
|
+
|
|
895
|
+
if errors:
|
|
896
|
+
return errors
|
|
897
|
+
|
|
898
|
+
# Collect hardware requirements
|
|
899
|
+
all_hardware: Set[str] = set()
|
|
900
|
+
for name in primitives:
|
|
901
|
+
prim = PRIMITIVE_REGISTRY[name]
|
|
902
|
+
all_hardware.update(prim.hardware)
|
|
903
|
+
|
|
904
|
+
# Check for common issues
|
|
905
|
+
prim_sequence = [PRIMITIVE_REGISTRY[name] for name in primitives]
|
|
906
|
+
|
|
907
|
+
# Warn if force sensing used without force sensor
|
|
908
|
+
uses_force = any(
|
|
909
|
+
"force_sensor" in p.hardware or "force" in p.name.lower()
|
|
910
|
+
for p in prim_sequence
|
|
911
|
+
)
|
|
912
|
+
has_force_sensor = "force_sensor" in all_hardware
|
|
913
|
+
if uses_force and not has_force_sensor:
|
|
914
|
+
errors.append(
|
|
915
|
+
"Skill uses force-related primitives but no force_sensor "
|
|
916
|
+
"in hardware requirements"
|
|
917
|
+
)
|
|
918
|
+
|
|
919
|
+
# Warn if gripper used without gripper hardware
|
|
920
|
+
uses_gripper = any("gripper" in p.hardware for p in prim_sequence)
|
|
921
|
+
has_gripper = "gripper" in all_hardware
|
|
922
|
+
if uses_gripper and not has_gripper:
|
|
923
|
+
errors.append(
|
|
924
|
+
"Skill uses gripper primitives but no gripper "
|
|
925
|
+
"in hardware requirements"
|
|
926
|
+
)
|
|
927
|
+
|
|
928
|
+
return errors
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def get_required_hardware(primitives: List[str]) -> Set[str]:
|
|
932
|
+
"""
|
|
933
|
+
Get the set of all hardware required by a list of primitives.
|
|
934
|
+
|
|
935
|
+
Args:
|
|
936
|
+
primitives: List of primitive names
|
|
937
|
+
|
|
938
|
+
Returns:
|
|
939
|
+
Set of required hardware types
|
|
940
|
+
"""
|
|
941
|
+
hardware: Set[str] = set()
|
|
942
|
+
for name in primitives:
|
|
943
|
+
prim = PRIMITIVE_REGISTRY.get(name)
|
|
944
|
+
if prim:
|
|
945
|
+
hardware.update(prim.hardware)
|
|
946
|
+
return hardware
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
def get_primitives_by_hardware(hardware: str) -> List[PrimitiveDefinition]:
|
|
950
|
+
"""Get all primitives that require specific hardware."""
|
|
951
|
+
return [
|
|
952
|
+
p for p in PRIMITIVE_REGISTRY.values()
|
|
953
|
+
if hardware in p.hardware
|
|
954
|
+
]
|
|
955
|
+
|
|
956
|
+
|
|
957
|
+
def format_primitive_docs(name: str) -> str:
|
|
958
|
+
"""Format documentation for a primitive."""
|
|
959
|
+
prim = PRIMITIVE_REGISTRY.get(name)
|
|
960
|
+
if not prim:
|
|
961
|
+
return f"Unknown primitive: {name}"
|
|
962
|
+
|
|
963
|
+
lines = [
|
|
964
|
+
f"## {prim.name}",
|
|
965
|
+
"",
|
|
966
|
+
f"{prim.description}",
|
|
967
|
+
"",
|
|
968
|
+
f"**Category:** {prim.category.value}",
|
|
969
|
+
f"**Hardware:** {', '.join(prim.hardware) or 'None'}",
|
|
970
|
+
f"**Blocking:** {'Yes' if prim.blocking else 'No'}",
|
|
971
|
+
"",
|
|
972
|
+
"### Signature",
|
|
973
|
+
f"```python",
|
|
974
|
+
f"{prim.get_signature()}",
|
|
975
|
+
f"```",
|
|
976
|
+
"",
|
|
977
|
+
"### Parameters",
|
|
978
|
+
]
|
|
979
|
+
|
|
980
|
+
for param_name, param_def in prim.parameters.items():
|
|
981
|
+
required = "(required)" if param_def.required else "(optional)"
|
|
982
|
+
default = f" = {param_def.default}" if param_def.default is not None else ""
|
|
983
|
+
lines.append(f"- **{param_name}** `{param_def.type}` {required}{default}")
|
|
984
|
+
lines.append(f" - {param_def.description}")
|
|
985
|
+
if param_def.range:
|
|
986
|
+
lines.append(f" - Range: {param_def.range}")
|
|
987
|
+
if param_def.units:
|
|
988
|
+
lines.append(f" - Units: {param_def.units}")
|
|
989
|
+
|
|
990
|
+
if prim.example:
|
|
991
|
+
lines.extend([
|
|
992
|
+
"",
|
|
993
|
+
"### Example",
|
|
994
|
+
f"```python",
|
|
995
|
+
f"{prim.example}",
|
|
996
|
+
f"```",
|
|
997
|
+
])
|
|
998
|
+
|
|
999
|
+
return "\n".join(lines)
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
# Export commonly used items
|
|
1003
|
+
__all__ = [
|
|
1004
|
+
"PrimitiveCategory",
|
|
1005
|
+
"ParameterDefinition",
|
|
1006
|
+
"PrimitiveDefinition",
|
|
1007
|
+
"PRIMITIVE_REGISTRY",
|
|
1008
|
+
"get_primitive",
|
|
1009
|
+
"list_primitives",
|
|
1010
|
+
"get_primitive_names",
|
|
1011
|
+
"validate_primitive_chain",
|
|
1012
|
+
"get_required_hardware",
|
|
1013
|
+
"get_primitives_by_hardware",
|
|
1014
|
+
"format_primitive_docs",
|
|
1015
|
+
"register_primitive",
|
|
1016
|
+
]
|