Routenplanung eingebaut

This commit is contained in:
2026-05-23 20:53:16 +02:00
parent b24996e67f
commit 91fb1fcf0b
6 changed files with 1877 additions and 0 deletions
@@ -18,6 +18,9 @@ SPEED_LIMIT_FILE = '/tmp/exomy_speed_limit.txt'
DRIVE_ESTIMATOR_CALIBRATION_FILE = os.path.join(
os.path.dirname(__file__), '..', 'config', 'drive_estimator_calibration.json'
)
ROUTE_LIBRARY_FILE = os.path.join(
os.path.dirname(__file__), '..', 'config', 'route_library.json'
)
MOTOR_TEST_LOCK = threading.Lock()
@@ -625,6 +628,156 @@ def save_drive_estimator_calibration(points):
}
def get_route_library():
data = {
'routes': [],
'updated_at': None,
}
try:
with open(ROUTE_LIBRARY_FILE, 'r', encoding='utf-8') as handle:
payload = json.load(handle)
routes = payload.get('routes')
if isinstance(routes, list):
cleaned_routes = []
for route in routes:
if not isinstance(route, dict):
continue
name = str(route.get('name', '')).strip()
waypoints = route.get('waypoints', [])
if not name or not isinstance(waypoints, list):
continue
cleaned_waypoints = []
for point in waypoints:
if not isinstance(point, dict):
continue
try:
lat = float(point.get('lat'))
lon = float(point.get('lon'))
except (TypeError, ValueError):
continue
cleaned_waypoints.append({
'lat': lat,
'lon': lon,
})
cleaned_routes.append({
'name': name,
'waypoints': cleaned_waypoints,
'updated_at': route.get('updated_at'),
})
data = {
'routes': cleaned_routes,
'updated_at': payload.get('updated_at'),
}
except FileNotFoundError:
pass
except (OSError, ValueError, TypeError, json.JSONDecodeError):
return 500, {'status': 'Routen konnten nicht geladen werden'}
return 200, {
'status': 'Bereit',
'library': data,
}
def save_named_route(name, waypoints):
route_name = str(name or '').strip()
if not route_name:
return 400, {'status': 'Routenname fehlt'}
if not isinstance(waypoints, list) or not waypoints:
return 400, {'status': 'Route hat keine Wegpunkte'}
cleaned_waypoints = []
for point in waypoints:
if not isinstance(point, dict):
return 400, {'status': 'Wegpunkte sind ungültig'}
try:
lat = float(point.get('lat'))
lon = float(point.get('lon'))
except (TypeError, ValueError):
return 400, {'status': 'Wegpunkte sind ungültig'}
cleaned_waypoints.append({
'lat': lat,
'lon': lon,
})
existing_status, existing_response = get_route_library()
if existing_status != 200:
return existing_status, existing_response
routes = existing_response['library']['routes']
now_text = time.strftime('%Y-%m-%d %H:%M:%S')
replaced = False
for route in routes:
if route['name'].lower() == route_name.lower():
route['name'] = route_name
route['waypoints'] = cleaned_waypoints
route['updated_at'] = now_text
replaced = True
break
if not replaced:
routes.append({
'name': route_name,
'waypoints': cleaned_waypoints,
'updated_at': now_text,
})
routes.sort(key=lambda item: item['name'].lower())
os.makedirs(os.path.dirname(ROUTE_LIBRARY_FILE), exist_ok=True)
payload = {
'updated_at': now_text,
'routes': routes,
}
try:
with open(ROUTE_LIBRARY_FILE, 'w', encoding='utf-8') as handle:
json.dump(payload, handle, ensure_ascii=False, indent=2)
handle.write('\n')
except OSError:
return 500, {'status': 'Route konnte nicht gespeichert werden'}
return 200, {
'status': 'Route gespeichert',
'route': {
'name': route_name,
'waypoints': cleaned_waypoints,
'updated_at': now_text,
},
'library': payload,
}
def delete_named_route(name):
route_name = str(name or '').strip()
if not route_name:
return 400, {'status': 'Routenname fehlt'}
existing_status, existing_response = get_route_library()
if existing_status != 200:
return existing_status, existing_response
routes = existing_response['library']['routes']
filtered_routes = [route for route in routes if route['name'].lower() != route_name.lower()]
if len(filtered_routes) == len(routes):
return 404, {'status': 'Route nicht gefunden'}
now_text = time.strftime('%Y-%m-%d %H:%M:%S')
payload = {
'updated_at': now_text,
'routes': filtered_routes,
}
try:
with open(ROUTE_LIBRARY_FILE, 'w', encoding='utf-8') as handle:
json.dump(payload, handle, ensure_ascii=False, indent=2)
handle.write('\n')
except OSError:
return 500, {'status': 'Route konnte nicht gelöscht werden'}
return 200, {
'status': 'Route gelöscht',
'library': payload,
}
def preview_drive_neutral_action(values):
from admin_motor_test import preview_drive_neutral_value
@@ -728,9 +881,21 @@ class Handler(http.server.BaseHTTPRequestHandler):
status_code, response = get_drive_estimator_calibration()
self.write_json(status_code, response)
return
if self.path == '/api/routes':
status_code, response = get_route_library()
self.write_json(status_code, response)
return
self.write_json(404, {'status': 'Unbekannt'})
def do_POST(self):
raw_body = b'{}'
if self.path in ('/api/routes/save', '/api/routes/delete'):
try:
content_length = int(self.headers.get('Content-Length', '0'))
except ValueError:
content_length = 0
raw_body = self.rfile.read(content_length) if content_length > 0 else b'{}'
if self.path == '/api/delay':
try:
content_length = int(self.headers.get('Content-Length', '0'))
@@ -782,6 +947,31 @@ class Handler(http.server.BaseHTTPRequestHandler):
self.write_json(status_code, response)
return
if self.path == '/api/routes/save':
try:
payload = json.loads(raw_body.decode('utf-8'))
except (UnicodeDecodeError, json.JSONDecodeError):
self.write_json(400, {'status': 'Ungültige Anfrage'})
return
status_code, response = save_named_route(
payload.get('name'),
payload.get('waypoints'),
)
self.write_json(status_code, response)
return
if self.path == '/api/routes/delete':
try:
payload = json.loads(raw_body.decode('utf-8'))
except (UnicodeDecodeError, json.JSONDecodeError):
self.write_json(400, {'status': 'Ungültige Anfrage'})
return
status_code, response = delete_named_route(payload.get('name'))
self.write_json(status_code, response)
return
if self.path == '/api/motor-test/drive-neutral/preview':
try:
content_length = int(self.headers.get('Content-Length', '0'))