adding validated services? patching forcad_local.py

This commit is contained in:
Your Name
2026-08-13 08:32:35 +07:00
parent d485f61169
commit e795614e45
1459 changed files with 420036 additions and 436 deletions

View File

@@ -0,0 +1,31 @@
create drone:
./client create --ip 10.80.1.2
you can also specify label and (or) flight plan
./client create --ip 10.80.1.2 --label asdf --flight-plan "1 BOOSTX [drone] 200\n55 BOOSTY [drone] 400\n"
connect:
./client connect --ip 10.80.1.2 --drone-id="676306da9da3a3b7141f3000" --control-key="c786e71f"
connect with setting position and (or) velocity:
./client connect --ip 10.80.1.2 --drone-id="676306da9da3a3b7141f3000" --control-key="c786e71f" --position 0 0 --velocity 1 2
by default, client keeps connection for 30 seconds. if you need more, add --duration:
./client connect --ip 10.80.1.2 --drone-id="676306da9da3a3b7141f3000" --control-key="c786e71f" --duration 9999
list drones (only ids):
./client list-drones --ip 10.80.1.2
list drones (detailed):
./client list-drones-details --ip 10.80.1.2
list details but only for one drone (by label):
./client list-drones-details --ip 10.80.1.2 --label p4MUiB5oEF5EHAnkMefO8
do not forget to use --help:
./client --help
./client create --help
./client connect --help
./client list-drones --help
./client list-drones-details --help

View File

@@ -0,0 +1,10 @@
sudo apt install python3.12-venv
python3.12 -m venv flysim_client
source flysim_client/bin/activate
pip3.12 install -r requirements.txt
pip3.12 install pyinstaller staticx
python3.12 -m PyInstaller --onefile client
staticx dist/client dist/client_static
# dist/client_static is the file you need

View File

@@ -0,0 +1,113 @@
#!/usr/bin/env python3
import click
import drone_client
import json
import time
from typing import Optional
class OrderedGroup(click.Group):
def list_commands(self, ctx):
# Return commands in the order they are defined
return self.commands.keys()
@click.group(cls=OrderedGroup)
def cli():
"""Drone Control CLI"""
pass
@cli.command()
@click.option("--ip", default="127.0.0.1", help="Server IP address")
@click.option("--label", help="Drone label")
@click.option("--secret-data", help="Secret data for the drone")
@click.option("--flight-plan", help="Flight plan for the drone")
def create(ip, label, secret_data, flight_plan):
"""Create a new drone"""
client = drone_client.DroneClient(ip)
if not client.create_drone(
label=label,
secret_data=secret_data,
flight_plan=flight_plan.replace(r"\n", "\n") if flight_plan else None,
):
click.echo("Failed to create drone")
return
credentials = {
"drone_id": client.drone_id,
"control_key": client.control_key,
"ip": ip,
}
click.echo(f"Drone created successfully:")
click.echo(f"Drone ID: {client.drone_id}")
click.echo(f"Control Key: {client.control_key}")
@cli.command()
@click.option("--ip", default="127.0.0.1", help="Server IP address")
@click.option("--drone-id", required=True, help="Drone ID")
@click.option("--control-key", required=True, help="Control key")
@click.option("--position", type=(float, float), help="Position coordinates (x, y)")
@click.option("--velocity", type=(float, float), help="Velocity vector (vx, vy)")
@click.option(
"--duration", default=30, help="Duration to keep connection alive (seconds)"
)
def connect(ip, drone_id, control_key, position, velocity, duration):
"""Connect to an existing drone"""
client = drone_client.DroneClient(ip)
client.drone_id = drone_id
client.control_key = control_key
if not client.connect_to_drone():
click.echo("Failed to connect to drone")
return
click.echo(f"Connected to drone {drone_id}")
if position:
client.update_position(list(position))
click.echo(f"Updated position to {position}")
if velocity:
client.update_velocity(list(velocity))
click.echo(f"Updated velocity to {velocity}")
click.echo(f"Keeping connection alive for {duration} seconds...")
time.sleep(duration)
client.disconnect()
click.echo("Disconnected from drone")
@cli.command()
@click.option("--ip", default="127.0.0.1", help="Server IP address")
def list_drones(ip):
"""List all available drones"""
client = drone_client.DroneClient(ip)
client.get_all_drones()
@cli.command()
@click.option("--ip", default="127.0.0.1", help="Server IP address")
@click.option(
"--label",
"label",
help="Target drone label. If label is not specified, all drones are shown.",
)
def list_drones_details(ip, label):
"""List drone(s) with detailed information"""
client = drone_client.DroneClient(ip)
if label:
print(
client.get_drones_with_details(json.dumps([{"$match": {"label": label}}]))
)
else:
print(client.get_drones_with_details(json.dumps([{"$match": {}}])))
if __name__ == "__main__":
cli()

View File

@@ -0,0 +1,135 @@
import socketio
import requests
import time
import json
from threading import Event
# def print(*args, **kwargs):
# pass # disable print
class DroneClient:
def __init__(self, ip="127.0.0.1"):
sio = socketio.Client()
self.base_url = f"http://{ip}:9000/"
self.sio = sio
self.drone_id = None
self.control_key = None
self.data_received_event = Event()
self.last_received_data = None
self.velocity_updated_event = Event()
self.last_received_veldata = None
self.position_updated_event = Event()
self.last_received_posdata = None
@sio.on("connect")
def on_connect():
print("Connected to server")
@sio.on("disconnect")
def on_disconnect():
print("Disconnected from server")
@sio.on("drone_connected")
def on_drone_connected(data):
print(f"Drone connection status: {data['data']}")
@sio.on("data_updated")
def on_data_updated(data):
print(data)
if isinstance(data, dict):
self.last_received_data = data
self.data_received_event.set()
@sio.on("velocity_updated")
def on_velocity_updated(data):
print(data)
if isinstance(data, dict):
self.last_received_veldata = data
self.velocity_updated_event.set()
@sio.on("position_updated")
def on_position_updated(data):
print(data)
if isinstance(data, dict):
self.last_received_posdata = data
self.position_updated_event.set()
@sio.on("error")
def on_error(data):
print(f"Error: {data['data']}")
def wait_for_data_msg(self, timeout=None):
self.data_received_event.clear()
if self.data_received_event.wait(timeout):
return self.last_received_data
return None
def wait_for_vel_msg(self, timeout=None):
self.velocity_updated_event.clear()
if self.velocity_updated_event.wait(timeout):
return self.last_received_veldata
return None
def wait_for_pos_msg(self, timeout=None):
self.position_updated_event.clear()
if self.position_updated_event.wait(timeout):
return self.last_received_posdata
return None
def create_drone(self, label=None, secret_data=None, flight_plan=None):
response = requests.post(
f"{self.base_url}/create_drone",
timeout=8000,
json={}
| ({"label": label} if label else {})
| ({"secret_data": secret_data} if secret_data else {})
| ({"flight_plan": flight_plan} if flight_plan else {}),
)
if response.status_code == 200:
data = response.json()
self.drone_id = data["id"]
self.control_key = data["control_key"]
print(
f"Created drone with ID: {self.drone_id}, control key: {self.control_key}"
)
return True
else:
print(f"Failed to create drone: {response.text}")
return False
def get_all_drones(self):
response = requests.get(f"{self.base_url}/get_drones")
print("All drones:", response.json())
def get_drones_with_details(self, with_what_details):
response = requests.get(f"{self.base_url}/get_drones?with={with_what_details}")
return response.json()
def connect_to_drone(self):
try:
self.sio.connect(self.base_url, socketio_path="/socket.io")
self.sio.emit(
"join_drone",
{"drone_id": self.drone_id, "control_key": self.control_key},
)
return True
except Exception as e:
print(f"Connection error: {e}")
return False
def update_position(self, position):
self.sio.emit("set_position", {"drone_id": self.drone_id, "position": position})
def update_velocity(self, velocity):
self.sio.emit("set_velocity", {"drone_id": self.drone_id, "velocity": velocity})
def disconnect(self):
try:
self.sio.disconnect()
except Exception as e:
print(f"Disconnection error: {e}")

View File

@@ -0,0 +1,4 @@
requests==2.32.3
python-socketio==5.11.4
websocket-client==1.8.0
click==8.1.7