foodforthought-cli 0.2.8__py3-none-any.whl → 0.3.1__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 +12 -0
- ate/behaviors/approach.py +399 -0
- ate/cli.py +855 -4551
- 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 +402 -0
- ate/detection/trash_detector.py +322 -0
- ate/drivers/__init__.py +18 -6
- ate/drivers/ble_transport.py +405 -0
- ate/drivers/mechdog.py +360 -24
- ate/drivers/wifi_camera.py +477 -0
- ate/interfaces/__init__.py +16 -0
- ate/interfaces/base.py +2 -0
- ate/interfaces/sensors.py +247 -0
- ate/llm_proxy.py +239 -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 +42 -3
- ate/recording/session.py +12 -2
- ate/recording/visual.py +416 -0
- ate/robot/__init__.py +142 -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 +88 -3
- ate/robot/calibration_state.py +388 -0
- ate/robot/commands.py +143 -11
- ate/robot/direction_calibration.py +554 -0
- ate/robot/discovery.py +104 -2
- ate/robot/llm_system_id.py +654 -0
- ate/robot/locomotion_calibration.py +508 -0
- ate/robot/marker_generator.py +611 -0
- ate/robot/perception.py +502 -0
- ate/robot/primitives.py +614 -0
- ate/robot/profiles.py +6 -0
- ate/robot/registry.py +5 -2
- ate/robot/servo_mapper.py +1153 -0
- ate/robot/skill_upload.py +285 -3
- ate/robot/target_calibration.py +500 -0
- ate/robot/teach.py +515 -0
- ate/robot/types.py +242 -0
- ate/robot/visual_labeler.py +9 -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.8.dist-info → foodforthought_cli-0.3.1.dist-info}/METADATA +1 -1
- foodforthought_cli-0.3.1.dist-info/RECORD +166 -0
- {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.1.dist-info}/WHEEL +1 -1
- foodforthought_cli-0.2.8.dist-info/RECORD +0 -73
- {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.1.dist-info}/entry_points.txt +0 -0
- {foodforthought_cli-0.2.8.dist-info → foodforthought_cli-0.3.1.dist-info}/top_level.txt +0 -0
ate/commands/protocol.py
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Protocol registry commands for FoodforThought CLI.
|
|
3
|
+
|
|
4
|
+
Commands:
|
|
5
|
+
- ate protocol list - List protocols
|
|
6
|
+
- ate protocol get - Get protocol details
|
|
7
|
+
- ate protocol init - Initialize new protocol template
|
|
8
|
+
- ate protocol push - Upload protocol to FoodForThought
|
|
9
|
+
- ate protocol scan-serial - Scan for serial ports
|
|
10
|
+
- ate protocol scan-ble - Scan for BLE devices
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def protocol_list(client, robot: Optional[str], transport: Optional[str],
|
|
20
|
+
verified: bool, search: Optional[str]) -> None:
|
|
21
|
+
"""List protocols from the registry."""
|
|
22
|
+
print("Fetching protocols...")
|
|
23
|
+
|
|
24
|
+
params = {}
|
|
25
|
+
if robot:
|
|
26
|
+
params["robot"] = robot
|
|
27
|
+
if transport:
|
|
28
|
+
params["transport"] = transport
|
|
29
|
+
if verified:
|
|
30
|
+
params["verified"] = "true"
|
|
31
|
+
if search:
|
|
32
|
+
params["search"] = search
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
response = client._request("GET", "/protocols", params=params)
|
|
36
|
+
protocols = response.get("protocols", [])
|
|
37
|
+
|
|
38
|
+
if not protocols:
|
|
39
|
+
print("\nNo protocols found matching criteria.")
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
print(f"\n{'=' * 70}")
|
|
43
|
+
print(f"{'Robot Model':<25} {'Transport':<12} {'Status':<12} {'Commands'}")
|
|
44
|
+
print(f"{'=' * 70}")
|
|
45
|
+
|
|
46
|
+
for proto in protocols:
|
|
47
|
+
robot_model = proto.get("robotModel", "")[:23]
|
|
48
|
+
transport_type = proto.get("transport", "")[:10]
|
|
49
|
+
status = "✓ Verified" if proto.get("verified") else "○ Community"
|
|
50
|
+
cmd_count = len(proto.get("commands", []))
|
|
51
|
+
print(f"{robot_model:<25} {transport_type:<12} {status:<12} {cmd_count}")
|
|
52
|
+
|
|
53
|
+
except Exception as e:
|
|
54
|
+
print(f"\n✗ Failed to list protocols: {e}", file=sys.stderr)
|
|
55
|
+
sys.exit(1)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def protocol_get(client, protocol_id: str) -> None:
|
|
59
|
+
"""Get detailed information about a protocol."""
|
|
60
|
+
try:
|
|
61
|
+
response = client._request("GET", f"/protocols/{protocol_id}")
|
|
62
|
+
proto = response.get("protocol", {})
|
|
63
|
+
|
|
64
|
+
print(f"\n{'=' * 60}")
|
|
65
|
+
print(f"Protocol: {proto.get('robotModel')}")
|
|
66
|
+
print(f"{'=' * 60}")
|
|
67
|
+
|
|
68
|
+
print(f"\nTransport: {proto.get('transport')}")
|
|
69
|
+
print(f"Verified: {'Yes' if proto.get('verified') else 'No'}")
|
|
70
|
+
print(f"Format: {proto.get('commandFormat')}")
|
|
71
|
+
|
|
72
|
+
# Connection info
|
|
73
|
+
if proto.get("connectionInfo"):
|
|
74
|
+
info = proto["connectionInfo"]
|
|
75
|
+
print(f"\nConnection:")
|
|
76
|
+
if info.get("baudRate"):
|
|
77
|
+
print(f" Baud Rate: {info['baudRate']}")
|
|
78
|
+
if info.get("serviceUuid"):
|
|
79
|
+
print(f" Service UUID: {info['serviceUuid']}")
|
|
80
|
+
if info.get("charUuid"):
|
|
81
|
+
print(f" Characteristic UUID: {info['charUuid']}")
|
|
82
|
+
|
|
83
|
+
# Commands
|
|
84
|
+
commands = proto.get("commands", [])
|
|
85
|
+
if commands:
|
|
86
|
+
print(f"\nCommands ({len(commands)}):")
|
|
87
|
+
for cmd in commands[:10]: # Show first 10
|
|
88
|
+
print(f" • {cmd.get('name')}: {cmd.get('template')}")
|
|
89
|
+
if len(commands) > 10:
|
|
90
|
+
print(f" ... and {len(commands) - 10} more")
|
|
91
|
+
|
|
92
|
+
except Exception as e:
|
|
93
|
+
print(f"\n✗ Failed to get protocol: {e}", file=sys.stderr)
|
|
94
|
+
sys.exit(1)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def protocol_init(client, robot_model: str, transport: str, output: str) -> None:
|
|
98
|
+
"""Initialize a new protocol template."""
|
|
99
|
+
print(f"Initializing protocol for {robot_model}")
|
|
100
|
+
print(f" Transport: {transport}")
|
|
101
|
+
print(f" Output: {output}")
|
|
102
|
+
|
|
103
|
+
output_path = Path(output)
|
|
104
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
105
|
+
|
|
106
|
+
# Create protocol template
|
|
107
|
+
protocol_template = {
|
|
108
|
+
"robotModel": robot_model,
|
|
109
|
+
"transport": transport,
|
|
110
|
+
"commandFormat": "ascii" if transport == "serial" else "binary",
|
|
111
|
+
"connectionInfo": {},
|
|
112
|
+
"commands": [
|
|
113
|
+
{
|
|
114
|
+
"name": "example_command",
|
|
115
|
+
"template": "CMD {param1}",
|
|
116
|
+
"description": "Example command - replace with actual commands",
|
|
117
|
+
"parameters": [
|
|
118
|
+
{"name": "param1", "type": "int", "range": [0, 100]}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
"version": "1.0.0"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# Add transport-specific defaults
|
|
126
|
+
if transport == "serial":
|
|
127
|
+
protocol_template["connectionInfo"]["baudRate"] = 115200
|
|
128
|
+
protocol_template["connectionInfo"]["dataBits"] = 8
|
|
129
|
+
protocol_template["connectionInfo"]["stopBits"] = 1
|
|
130
|
+
elif transport == "ble":
|
|
131
|
+
protocol_template["connectionInfo"]["serviceUuid"] = ""
|
|
132
|
+
protocol_template["connectionInfo"]["charUuid"] = ""
|
|
133
|
+
elif transport == "wifi":
|
|
134
|
+
protocol_template["connectionInfo"]["port"] = 8080
|
|
135
|
+
|
|
136
|
+
# Write protocol file
|
|
137
|
+
protocol_file = output_path / "protocol.json"
|
|
138
|
+
with open(protocol_file, 'w') as f:
|
|
139
|
+
json.dump(protocol_template, f, indent=2)
|
|
140
|
+
|
|
141
|
+
print(f"\n✓ Created: {protocol_file}")
|
|
142
|
+
print(f"\nNext steps:")
|
|
143
|
+
print(f" 1. Edit {protocol_file} with your robot's commands")
|
|
144
|
+
print(f" 2. Test connection with: ate bridge connect <port>")
|
|
145
|
+
print(f" 3. Publish with: ate protocol push {protocol_file}")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def protocol_push(client, file: Optional[str]) -> None:
|
|
149
|
+
"""Upload protocol to FoodForThought."""
|
|
150
|
+
protocol_file = Path(file or "./protocol.json")
|
|
151
|
+
|
|
152
|
+
if not protocol_file.exists():
|
|
153
|
+
print(f"Error: Protocol file not found: {protocol_file}", file=sys.stderr)
|
|
154
|
+
sys.exit(1)
|
|
155
|
+
|
|
156
|
+
with open(protocol_file) as f:
|
|
157
|
+
protocol_data = json.load(f)
|
|
158
|
+
|
|
159
|
+
print(f"Uploading protocol: {protocol_data.get('robotModel')}")
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
response = client._request("POST", "/protocols", json=protocol_data)
|
|
163
|
+
proto = response.get("protocol", {})
|
|
164
|
+
|
|
165
|
+
print(f"\n✓ Protocol uploaded!")
|
|
166
|
+
print(f" ID: {proto.get('id')}")
|
|
167
|
+
print(f" Status: Pending review")
|
|
168
|
+
|
|
169
|
+
except Exception as e:
|
|
170
|
+
print(f"\n✗ Upload failed: {e}", file=sys.stderr)
|
|
171
|
+
sys.exit(1)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def protocol_scan_serial() -> None:
|
|
175
|
+
"""Scan for available serial ports."""
|
|
176
|
+
try:
|
|
177
|
+
import serial.tools.list_ports
|
|
178
|
+
except ImportError:
|
|
179
|
+
print("Error: pyserial is required. Install with: pip install pyserial", file=sys.stderr)
|
|
180
|
+
sys.exit(1)
|
|
181
|
+
|
|
182
|
+
print("Scanning for serial ports...\n")
|
|
183
|
+
|
|
184
|
+
ports = list(serial.tools.list_ports.comports())
|
|
185
|
+
|
|
186
|
+
if not ports:
|
|
187
|
+
print("No serial ports found.")
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
print(f"Found {len(ports)} port(s):\n")
|
|
191
|
+
|
|
192
|
+
for port in ports:
|
|
193
|
+
print(f" {port.device}")
|
|
194
|
+
if port.description and port.description != "n/a":
|
|
195
|
+
print(f" Description: {port.description}")
|
|
196
|
+
if port.manufacturer:
|
|
197
|
+
print(f" Manufacturer: {port.manufacturer}")
|
|
198
|
+
if port.vid and port.pid:
|
|
199
|
+
print(f" VID:PID: {port.vid:04x}:{port.pid:04x}")
|
|
200
|
+
print()
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def protocol_scan_ble() -> None:
|
|
204
|
+
"""Scan for BLE devices."""
|
|
205
|
+
try:
|
|
206
|
+
import asyncio
|
|
207
|
+
from bleak import BleakScanner
|
|
208
|
+
except ImportError:
|
|
209
|
+
print("Error: bleak is required. Install with: pip install bleak", file=sys.stderr)
|
|
210
|
+
sys.exit(1)
|
|
211
|
+
|
|
212
|
+
async def scan():
|
|
213
|
+
print("Scanning for BLE devices (5 seconds)...\n")
|
|
214
|
+
|
|
215
|
+
devices = await BleakScanner.discover(timeout=5.0)
|
|
216
|
+
|
|
217
|
+
if not devices:
|
|
218
|
+
print("No BLE devices found.")
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
print(f"Found {len(devices)} device(s):\n")
|
|
222
|
+
|
|
223
|
+
for device in sorted(devices, key=lambda d: d.rssi or -100, reverse=True):
|
|
224
|
+
rssi = f"{device.rssi} dBm" if device.rssi else "N/A"
|
|
225
|
+
print(f" {device.address}")
|
|
226
|
+
if device.name:
|
|
227
|
+
print(f" Name: {device.name}")
|
|
228
|
+
print(f" RSSI: {rssi}")
|
|
229
|
+
print()
|
|
230
|
+
|
|
231
|
+
asyncio.run(scan())
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def register_parser(subparsers):
|
|
235
|
+
"""Register protocol commands with argparse."""
|
|
236
|
+
protocol_parser = subparsers.add_parser("protocol", help="Manage protocol registry")
|
|
237
|
+
protocol_subparsers = protocol_parser.add_subparsers(dest="protocol_action", help="Protocol action")
|
|
238
|
+
|
|
239
|
+
# protocol list
|
|
240
|
+
protocol_list_parser = protocol_subparsers.add_parser("list", help="List protocols")
|
|
241
|
+
protocol_list_parser.add_argument("-r", "--robot", help="Filter by robot model")
|
|
242
|
+
protocol_list_parser.add_argument("-t", "--transport",
|
|
243
|
+
choices=["ble", "serial", "wifi", "can", "i2c", "spi", "mqtt", "ros2"],
|
|
244
|
+
help="Filter by transport type")
|
|
245
|
+
protocol_list_parser.add_argument("--verified", action="store_true", help="Show only verified protocols")
|
|
246
|
+
protocol_list_parser.add_argument("-s", "--search", help="Search in command format and notes")
|
|
247
|
+
|
|
248
|
+
# protocol get
|
|
249
|
+
protocol_get_parser = protocol_subparsers.add_parser("get", help="Get protocol details")
|
|
250
|
+
protocol_get_parser.add_argument("protocol_id", help="Protocol ID")
|
|
251
|
+
|
|
252
|
+
# protocol init
|
|
253
|
+
protocol_init_parser = protocol_subparsers.add_parser("init", help="Initialize new protocol template")
|
|
254
|
+
protocol_init_parser.add_argument("robot_model", help="Robot model name (e.g., hiwonder-mechdog-pro)")
|
|
255
|
+
protocol_init_parser.add_argument("-t", "--transport", required=True,
|
|
256
|
+
choices=["ble", "serial", "wifi", "can", "i2c", "spi", "mqtt", "ros2"],
|
|
257
|
+
help="Transport type")
|
|
258
|
+
protocol_init_parser.add_argument("-o", "--output", default="./protocol",
|
|
259
|
+
help="Output directory (default: ./protocol)")
|
|
260
|
+
|
|
261
|
+
# protocol push
|
|
262
|
+
protocol_push_parser = protocol_subparsers.add_parser("push", help="Upload protocol to FoodForThought")
|
|
263
|
+
protocol_push_parser.add_argument("file", nargs="?", help="Path to protocol.json (default: ./protocol.json)")
|
|
264
|
+
|
|
265
|
+
# protocol scan-serial
|
|
266
|
+
protocol_subparsers.add_parser("scan-serial", help="Scan for serial ports")
|
|
267
|
+
|
|
268
|
+
# protocol scan-ble
|
|
269
|
+
protocol_subparsers.add_parser("scan-ble", help="Scan for BLE devices")
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def handle(client, args):
|
|
273
|
+
"""Handle protocol commands."""
|
|
274
|
+
if args.protocol_action == "list":
|
|
275
|
+
protocol_list(client, args.robot, args.transport, args.verified, args.search)
|
|
276
|
+
elif args.protocol_action == "get":
|
|
277
|
+
protocol_get(client, args.protocol_id)
|
|
278
|
+
elif args.protocol_action == "init":
|
|
279
|
+
protocol_init(client, args.robot_model, args.transport, args.output)
|
|
280
|
+
elif args.protocol_action == "push":
|
|
281
|
+
protocol_push(client, args.file)
|
|
282
|
+
elif args.protocol_action == "scan-serial":
|
|
283
|
+
protocol_scan_serial()
|
|
284
|
+
elif args.protocol_action == "scan-ble":
|
|
285
|
+
protocol_scan_ble()
|
|
286
|
+
else:
|
|
287
|
+
print("Usage: ate protocol {list|get|init|push|scan-serial|scan-ble}")
|
|
288
|
+
sys.exit(1)
|