Kaera umschaltbar

This commit is contained in:
2026-05-25 15:30:56 +02:00
parent 1855a2446b
commit 8c8bfd9e5b
7 changed files with 452 additions and 111 deletions
@@ -21,7 +21,38 @@ DRIVE_ESTIMATOR_CALIBRATION_FILE = os.path.join(
ROUTE_LIBRARY_FILE = os.path.join(
os.path.dirname(__file__), '..', 'config', 'route_library.json'
)
CAMERA_SETTINGS_FILE = os.path.join(
os.path.dirname(__file__), '..', 'config', 'camera_settings.json'
)
MOTOR_TEST_LOCK = threading.Lock()
CAMERA_PROFILES = [
{
'id': '640x480',
'label': '640 x 480',
'description': '4:3, klein und sparsam',
'width': 640,
'height': 480,
'fps': 10,
},
{
'id': '1024x768',
'label': '1024 x 768',
'description': '4:3, großes Sichtfeld mit mehr Details',
'width': 1024,
'height': 768,
'fps': 10,
},
{
'id': '1296x972',
'label': '1296 x 972',
'description': '4:3, großes Sichtfeld mit noch mehr Details',
'width': 1296,
'height': 972,
'fps': 10,
},
]
CAMERA_PROFILE_MAP = {profile['id']: profile for profile in CAMERA_PROFILES}
DEFAULT_CAMERA_PROFILE_ID = '1296x972'
class ReusableThreadingTCPServer(socketserver.ThreadingTCPServer):
@@ -285,6 +316,77 @@ def set_speed_limit(percent):
return percent
def get_default_camera_profile():
return dict(CAMERA_PROFILE_MAP[DEFAULT_CAMERA_PROFILE_ID])
def normalize_camera_profile(profile_id):
profile = CAMERA_PROFILE_MAP.get(str(profile_id or '').strip())
if profile is None:
raise ValueError('Unbekanntes Kamera-Profil')
return dict(profile)
def read_camera_settings():
default_profile = get_default_camera_profile()
try:
with open(CAMERA_SETTINGS_FILE, 'r', encoding='utf-8') as handle:
payload = json.load(handle)
except (FileNotFoundError, json.JSONDecodeError, OSError, ValueError, TypeError):
return default_profile
selected_profile_id = str(payload.get('profile', '')).strip()
if selected_profile_id in CAMERA_PROFILE_MAP:
return dict(CAMERA_PROFILE_MAP[selected_profile_id])
try:
width = int(payload.get('width', default_profile['width']))
height = int(payload.get('height', default_profile['height']))
fps = int(payload.get('fps', default_profile['fps']))
except (TypeError, ValueError):
return default_profile
for profile in CAMERA_PROFILES:
if profile['width'] == width and profile['height'] == height and profile['fps'] == fps:
return dict(profile)
return default_profile
def write_camera_settings(profile):
profile = dict(profile)
os.makedirs(os.path.dirname(CAMERA_SETTINGS_FILE), exist_ok=True)
payload = {
'profile': profile['id'],
'width': profile['width'],
'height': profile['height'],
'fps': profile['fps'],
}
with open(CAMERA_SETTINGS_FILE, 'w', encoding='utf-8') as handle:
json.dump(payload, handle, indent=2, sort_keys=True)
handle.write('\n')
def get_camera_config():
profile = read_camera_settings()
return {
'status': 'Bereit',
'camera': {
'selected_profile': profile['id'],
'width': profile['width'],
'height': profile['height'],
'fps': profile['fps'],
'profiles': [dict(item) for item in CAMERA_PROFILES],
}
}
def set_camera_profile(profile_id):
profile = normalize_camera_profile(profile_id)
write_camera_settings(profile)
restart_camera_service()
return profile
def rumble_controller(duration_ms=400):
# Linux FF constants
EV_FF = 0x15
@@ -856,6 +958,9 @@ class Handler(http.server.BaseHTTPRequestHandler):
if self.path == '/api/speed-limit':
self.write_json(200, {'speed_limit_percent': get_speed_limit()})
return
if self.path == '/api/camera-config':
self.write_json(200, get_camera_config())
return
if self.path == '/api/status':
self.write_json(200, collect_status())
return
@@ -932,6 +1037,35 @@ class Handler(http.server.BaseHTTPRequestHandler):
self.write_json(500, {'status': str(exc)})
return
if self.path == '/api/camera-config':
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'{}'
try:
payload = json.loads(raw_body.decode('utf-8'))
except (UnicodeDecodeError, json.JSONDecodeError):
self.write_json(400, {'status': 'Ungültige Anfrage'})
return
try:
profile = set_camera_profile(payload.get('profile'))
self.write_json(200, {
'status': 'Kameraauflösung gespeichert, Kameradienst startet neu',
'camera': {
'selected_profile': profile['id'],
'width': profile['width'],
'height': profile['height'],
'fps': profile['fps'],
'profiles': [dict(item) for item in CAMERA_PROFILES],
}
})
except ValueError as exc:
self.write_json(400, {'status': str(exc)})
except Exception as exc:
self.write_json(500, {'status': str(exc)})
return
if self.path == '/api/drive-estimator/calibration':
try:
content_length = int(self.headers.get('Content-Length', '0'))