foodforthought-cli 0.2.7__py3-none-any.whl → 0.3.0__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 +6 -0
- ate/__main__.py +16 -0
- ate/auth/__init__.py +1 -0
- ate/auth/device_flow.py +141 -0
- ate/auth/token_store.py +96 -0
- ate/behaviors/__init__.py +100 -0
- ate/behaviors/approach.py +399 -0
- ate/behaviors/common.py +686 -0
- ate/behaviors/tree.py +454 -0
- ate/cli.py +855 -3995
- ate/client.py +90 -0
- ate/commands/__init__.py +168 -0
- ate/commands/auth.py +389 -0
- ate/commands/bridge.py +448 -0
- ate/commands/data.py +185 -0
- ate/commands/deps.py +111 -0
- ate/commands/generate.py +384 -0
- ate/commands/memory.py +907 -0
- ate/commands/parts.py +166 -0
- ate/commands/primitive.py +399 -0
- ate/commands/protocol.py +288 -0
- ate/commands/recording.py +524 -0
- ate/commands/repo.py +154 -0
- ate/commands/simulation.py +291 -0
- ate/commands/skill.py +303 -0
- ate/commands/skills.py +487 -0
- ate/commands/team.py +147 -0
- ate/commands/workflow.py +271 -0
- ate/detection/__init__.py +38 -0
- ate/detection/base.py +142 -0
- ate/detection/color_detector.py +399 -0
- ate/detection/trash_detector.py +322 -0
- ate/drivers/__init__.py +39 -0
- ate/drivers/ble_transport.py +405 -0
- ate/drivers/mechdog.py +942 -0
- ate/drivers/wifi_camera.py +477 -0
- ate/interfaces/__init__.py +187 -0
- ate/interfaces/base.py +273 -0
- ate/interfaces/body.py +267 -0
- ate/interfaces/detection.py +282 -0
- ate/interfaces/locomotion.py +422 -0
- ate/interfaces/manipulation.py +408 -0
- ate/interfaces/navigation.py +389 -0
- ate/interfaces/perception.py +362 -0
- ate/interfaces/sensors.py +247 -0
- ate/interfaces/types.py +371 -0
- ate/llm_proxy.py +239 -0
- ate/mcp_server.py +387 -0
- ate/memory/__init__.py +35 -0
- ate/memory/cloud.py +244 -0
- ate/memory/context.py +269 -0
- ate/memory/embeddings.py +184 -0
- ate/memory/export.py +26 -0
- ate/memory/merge.py +146 -0
- ate/memory/migrate/__init__.py +34 -0
- ate/memory/migrate/base.py +89 -0
- ate/memory/migrate/pipeline.py +189 -0
- ate/memory/migrate/sources/__init__.py +13 -0
- ate/memory/migrate/sources/chroma.py +170 -0
- ate/memory/migrate/sources/pinecone.py +120 -0
- ate/memory/migrate/sources/qdrant.py +110 -0
- ate/memory/migrate/sources/weaviate.py +160 -0
- ate/memory/reranker.py +353 -0
- ate/memory/search.py +26 -0
- ate/memory/store.py +548 -0
- ate/recording/__init__.py +83 -0
- ate/recording/demonstration.py +378 -0
- ate/recording/session.py +415 -0
- ate/recording/upload.py +304 -0
- ate/recording/visual.py +416 -0
- ate/recording/wrapper.py +95 -0
- ate/robot/__init__.py +221 -0
- ate/robot/agentic_servo.py +856 -0
- ate/robot/behaviors.py +493 -0
- ate/robot/ble_capture.py +1000 -0
- ate/robot/ble_enumerate.py +506 -0
- ate/robot/calibration.py +668 -0
- ate/robot/calibration_state.py +388 -0
- ate/robot/commands.py +3735 -0
- ate/robot/direction_calibration.py +554 -0
- ate/robot/discovery.py +441 -0
- ate/robot/introspection.py +330 -0
- ate/robot/llm_system_id.py +654 -0
- ate/robot/locomotion_calibration.py +508 -0
- ate/robot/manager.py +270 -0
- ate/robot/marker_generator.py +611 -0
- ate/robot/perception.py +502 -0
- ate/robot/primitives.py +614 -0
- ate/robot/profiles.py +281 -0
- ate/robot/registry.py +322 -0
- ate/robot/servo_mapper.py +1153 -0
- ate/robot/skill_upload.py +675 -0
- ate/robot/target_calibration.py +500 -0
- ate/robot/teach.py +515 -0
- ate/robot/types.py +242 -0
- ate/robot/visual_labeler.py +1048 -0
- ate/robot/visual_servo_loop.py +494 -0
- ate/robot/visual_servoing.py +570 -0
- ate/robot/visual_system_id.py +906 -0
- ate/transports/__init__.py +121 -0
- ate/transports/base.py +394 -0
- ate/transports/ble.py +405 -0
- ate/transports/hybrid.py +444 -0
- ate/transports/serial.py +345 -0
- ate/urdf/__init__.py +30 -0
- ate/urdf/capture.py +582 -0
- ate/urdf/cloud.py +491 -0
- ate/urdf/collision.py +271 -0
- ate/urdf/commands.py +708 -0
- ate/urdf/depth.py +360 -0
- ate/urdf/inertial.py +312 -0
- ate/urdf/kinematics.py +330 -0
- ate/urdf/lifting.py +415 -0
- ate/urdf/meshing.py +300 -0
- ate/urdf/models/__init__.py +110 -0
- ate/urdf/models/depth_anything.py +253 -0
- ate/urdf/models/sam2.py +324 -0
- ate/urdf/motion_analysis.py +396 -0
- ate/urdf/pipeline.py +468 -0
- ate/urdf/scale.py +256 -0
- ate/urdf/scan_session.py +411 -0
- ate/urdf/segmentation.py +299 -0
- ate/urdf/synthesis.py +319 -0
- ate/urdf/topology.py +336 -0
- ate/urdf/validation.py +371 -0
- {foodforthought_cli-0.2.7.dist-info → foodforthought_cli-0.3.0.dist-info}/METADATA +9 -1
- foodforthought_cli-0.3.0.dist-info/RECORD +166 -0
- {foodforthought_cli-0.2.7.dist-info → foodforthought_cli-0.3.0.dist-info}/WHEEL +1 -1
- foodforthought_cli-0.2.7.dist-info/RECORD +0 -44
- {foodforthought_cli-0.2.7.dist-info → foodforthought_cli-0.3.0.dist-info}/entry_points.txt +0 -0
- {foodforthought_cli-0.2.7.dist-info → foodforthought_cli-0.3.0.dist-info}/top_level.txt +0 -0
ate/robot/manager.py
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Robot Manager - unified interface for managing robot connections.
|
|
3
|
+
|
|
4
|
+
Handles:
|
|
5
|
+
- Loading robots from profiles
|
|
6
|
+
- Connecting/disconnecting
|
|
7
|
+
- Managing multiple robots
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Dict, List, Optional, Any
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
|
|
13
|
+
from .profiles import RobotProfile, load_profile, list_profiles
|
|
14
|
+
from .registry import KNOWN_ROBOTS, RobotType
|
|
15
|
+
from .introspection import get_capabilities, get_methods
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class ManagedRobot:
|
|
20
|
+
"""A robot instance managed by RobotManager."""
|
|
21
|
+
profile: RobotProfile
|
|
22
|
+
robot_type: RobotType
|
|
23
|
+
instance: Any = None
|
|
24
|
+
connected: bool = False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class RobotManager:
|
|
28
|
+
"""
|
|
29
|
+
Manages robot connections and lifecycle.
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
manager = RobotManager()
|
|
33
|
+
|
|
34
|
+
# Load from profile
|
|
35
|
+
dog = manager.load("my_mechdog")
|
|
36
|
+
dog.connect()
|
|
37
|
+
|
|
38
|
+
# Use robot
|
|
39
|
+
dog.instance.stand()
|
|
40
|
+
dog.instance.walk(Vector3.forward())
|
|
41
|
+
|
|
42
|
+
# Disconnect
|
|
43
|
+
manager.disconnect_all()
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self):
|
|
47
|
+
self._robots: Dict[str, ManagedRobot] = {}
|
|
48
|
+
|
|
49
|
+
def load(self, profile_name: str) -> Optional[ManagedRobot]:
|
|
50
|
+
"""
|
|
51
|
+
Load a robot from a saved profile.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
profile_name: Name of the profile
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
ManagedRobot or None if profile not found
|
|
58
|
+
"""
|
|
59
|
+
profile = load_profile(profile_name)
|
|
60
|
+
if profile is None:
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
robot_type = KNOWN_ROBOTS.get(profile.robot_type)
|
|
64
|
+
if robot_type is None:
|
|
65
|
+
print(f"Unknown robot type: {profile.robot_type}")
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
managed = ManagedRobot(
|
|
69
|
+
profile=profile,
|
|
70
|
+
robot_type=robot_type,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
self._robots[profile_name] = managed
|
|
74
|
+
return managed
|
|
75
|
+
|
|
76
|
+
def create(
|
|
77
|
+
self,
|
|
78
|
+
name: str,
|
|
79
|
+
robot_type: str,
|
|
80
|
+
**config
|
|
81
|
+
) -> Optional[ManagedRobot]:
|
|
82
|
+
"""
|
|
83
|
+
Create a robot instance without loading from profile.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
name: Name for this robot
|
|
87
|
+
robot_type: Robot type ID
|
|
88
|
+
**config: Configuration options
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
ManagedRobot
|
|
92
|
+
"""
|
|
93
|
+
rtype = KNOWN_ROBOTS.get(robot_type)
|
|
94
|
+
if rtype is None:
|
|
95
|
+
print(f"Unknown robot type: {robot_type}")
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
profile = RobotProfile(
|
|
99
|
+
name=name,
|
|
100
|
+
robot_type=robot_type,
|
|
101
|
+
**config
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
managed = ManagedRobot(
|
|
105
|
+
profile=profile,
|
|
106
|
+
robot_type=rtype,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
self._robots[name] = managed
|
|
110
|
+
return managed
|
|
111
|
+
|
|
112
|
+
def connect(self, name: str) -> bool:
|
|
113
|
+
"""
|
|
114
|
+
Connect to a loaded robot.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
name: Robot name
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
True if connected successfully
|
|
121
|
+
"""
|
|
122
|
+
managed = self._robots.get(name)
|
|
123
|
+
if managed is None:
|
|
124
|
+
print(f"Robot not loaded: {name}")
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
# Create robot instance
|
|
128
|
+
instance = self._create_instance(managed)
|
|
129
|
+
if instance is None:
|
|
130
|
+
return False
|
|
131
|
+
|
|
132
|
+
managed.instance = instance
|
|
133
|
+
|
|
134
|
+
# Connect
|
|
135
|
+
result = instance.connect()
|
|
136
|
+
if result.success:
|
|
137
|
+
managed.connected = True
|
|
138
|
+
return True
|
|
139
|
+
else:
|
|
140
|
+
print(f"Connection failed: {result.message}")
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
def disconnect(self, name: str) -> bool:
|
|
144
|
+
"""
|
|
145
|
+
Disconnect from a robot.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
name: Robot name
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
True if disconnected
|
|
152
|
+
"""
|
|
153
|
+
managed = self._robots.get(name)
|
|
154
|
+
if managed is None or not managed.connected:
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
if managed.instance:
|
|
158
|
+
managed.instance.disconnect()
|
|
159
|
+
managed.connected = False
|
|
160
|
+
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
def disconnect_all(self) -> None:
|
|
164
|
+
"""Disconnect from all robots."""
|
|
165
|
+
for name in list(self._robots.keys()):
|
|
166
|
+
self.disconnect(name)
|
|
167
|
+
|
|
168
|
+
def get(self, name: str) -> Optional[Any]:
|
|
169
|
+
"""
|
|
170
|
+
Get a robot instance.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
name: Robot name
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Robot instance or None
|
|
177
|
+
"""
|
|
178
|
+
managed = self._robots.get(name)
|
|
179
|
+
if managed and managed.instance:
|
|
180
|
+
return managed.instance
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
def list_loaded(self) -> List[str]:
|
|
184
|
+
"""List all loaded robots."""
|
|
185
|
+
return list(self._robots.keys())
|
|
186
|
+
|
|
187
|
+
def list_connected(self) -> List[str]:
|
|
188
|
+
"""List all connected robots."""
|
|
189
|
+
return [
|
|
190
|
+
name for name, managed in self._robots.items()
|
|
191
|
+
if managed.connected
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
def get_info(self, name: str) -> Optional[Dict[str, Any]]:
|
|
195
|
+
"""
|
|
196
|
+
Get information about a loaded robot.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
name: Robot name
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
Dict with robot info
|
|
203
|
+
"""
|
|
204
|
+
managed = self._robots.get(name)
|
|
205
|
+
if managed is None:
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
info = {
|
|
209
|
+
"name": managed.profile.name,
|
|
210
|
+
"type": managed.robot_type.name,
|
|
211
|
+
"manufacturer": managed.robot_type.manufacturer,
|
|
212
|
+
"archetype": managed.robot_type.archetype,
|
|
213
|
+
"connected": managed.connected,
|
|
214
|
+
"profile": {
|
|
215
|
+
"serial_port": managed.profile.serial_port,
|
|
216
|
+
"camera_ip": managed.profile.camera_ip,
|
|
217
|
+
"has_camera": managed.profile.has_camera,
|
|
218
|
+
"has_arm": managed.profile.has_arm,
|
|
219
|
+
},
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if managed.instance:
|
|
223
|
+
info["capabilities"] = list(get_capabilities(managed.instance).keys())
|
|
224
|
+
info["methods"] = {
|
|
225
|
+
cap: [m.name for m in methods]
|
|
226
|
+
for cap, methods in get_methods(managed.instance).items()
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return info
|
|
230
|
+
|
|
231
|
+
def _create_instance(self, managed: ManagedRobot) -> Optional[Any]:
|
|
232
|
+
"""Create a robot instance from profile and type."""
|
|
233
|
+
rtype = managed.robot_type
|
|
234
|
+
profile = managed.profile
|
|
235
|
+
|
|
236
|
+
# Import the driver module dynamically
|
|
237
|
+
try:
|
|
238
|
+
import importlib
|
|
239
|
+
module = importlib.import_module(rtype.driver_module)
|
|
240
|
+
driver_class = getattr(module, rtype.driver_class)
|
|
241
|
+
config_class = getattr(module, rtype.config_class, None)
|
|
242
|
+
except (ImportError, AttributeError) as e:
|
|
243
|
+
print(f"Failed to import driver: {e}")
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
# Build configuration
|
|
247
|
+
if config_class:
|
|
248
|
+
config_kwargs = {
|
|
249
|
+
"port": profile.serial_port or "/dev/ttyUSB0",
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if profile.has_camera and profile.camera_ip:
|
|
253
|
+
config_kwargs["has_camera"] = True
|
|
254
|
+
config_kwargs["camera_ip"] = profile.camera_ip
|
|
255
|
+
config_kwargs["camera_port"] = profile.camera_port
|
|
256
|
+
config_kwargs["camera_stream_port"] = profile.camera_stream_port
|
|
257
|
+
|
|
258
|
+
if profile.has_arm:
|
|
259
|
+
config_kwargs["has_arm"] = True
|
|
260
|
+
|
|
261
|
+
config = config_class(**config_kwargs)
|
|
262
|
+
return driver_class(config=config)
|
|
263
|
+
else:
|
|
264
|
+
return driver_class()
|
|
265
|
+
|
|
266
|
+
def __enter__(self) -> "RobotManager":
|
|
267
|
+
return self
|
|
268
|
+
|
|
269
|
+
def __exit__(self, *args) -> None:
|
|
270
|
+
self.disconnect_all()
|