from __future__ import annotations import json import time from django.views.decorators.csrf import csrf_exempt from django.http import HttpResponse, StreamingHttpResponse from rest_framework.decorators import api_view from rest_framework.response import Response from .services import gps_service, network_service, video_service @api_view(["GET"]) def dashboard_snapshot(request): return Response( { "gps": gps_service.get_latest(), "network": network_service.get_latest(), "video": video_service.get_status(), } ) @api_view(["GET"]) def gps_latest(request): return Response(gps_service.get_latest()) @api_view(["GET"]) def network_latest(request): return Response(network_service.get_latest()) @api_view(["GET"]) def clock_calibration(request): server_received_unix_ns = time.time_ns() server_sent_unix_ns = time.time_ns() response = Response( { "server_received_unix_ms": round(server_received_unix_ns / 1_000_000.0, 3), "server_sent_unix_ms": round(server_sent_unix_ns / 1_000_000.0, 3), } ) response["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0" return response @api_view(["GET"]) def video_status(request): return Response(video_service.get_status()) def video_frame(request): try: frame, headers = video_service.get_next_frame_with_headers() except (FileNotFoundError, RuntimeError) as error: status = video_service.get_status() return HttpResponse( status.get("source_detail") or str(error), status=503, content_type="text/plain; charset=utf-8", ) response = HttpResponse(frame, content_type="image/jpeg") response["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0" for key, value in headers.items(): response[key] = value return response def video_stream(request): status = video_service.get_status() if not status["available"]: return HttpResponse( status.get("source_detail") or f"JPEG frame directory not found: {status['frame_dir']}", status=503, content_type="text/plain; charset=utf-8", ) try: fps = float(request.GET.get("fps", status["fps"])) except (TypeError, ValueError): fps = float(status["fps"]) response = StreamingHttpResponse( video_service.iter_mjpeg(fps=fps), content_type="multipart/x-mixed-replace; boundary=frame", ) response["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0" return response @csrf_exempt @api_view(["POST"]) def video_display_probe(request): try: payload = json.loads(request.body.decode("utf-8")) except (UnicodeDecodeError, json.JSONDecodeError): return Response({"detail": "invalid json"}, status=400) if not isinstance(payload, dict): return Response({"detail": "expected json object"}, status=400) video_service.record_display_probe(payload) return Response({"ok": True})