feat: ui program to cb connection
ref: N25B-198
This commit is contained in:
52
src/control_backend/api/v1/endpoints/program.py
Normal file
52
src/control_backend/api/v1/endpoints/program.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from fastapi import APIRouter, HTTPException, Request
|
||||||
|
|
||||||
|
from control_backend.schemas.message import Message
|
||||||
|
from control_backend.schemas.program import Phase
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/program", status_code=202)
|
||||||
|
async def receive_message(program: Message, request: Request):
|
||||||
|
"""
|
||||||
|
Receives a BehaviorProgram as a stringified JSON list inside `message`.
|
||||||
|
Converts it into real Phase objects.
|
||||||
|
"""
|
||||||
|
logger.info("Received raw program: ")
|
||||||
|
logger.debug("%s", program)
|
||||||
|
raw_str = program.message # This is the JSON string
|
||||||
|
|
||||||
|
# Convert Json into dict.
|
||||||
|
try:
|
||||||
|
program_list = json.loads(raw_str)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logger.error("Failed to decode program JSON: %s", e)
|
||||||
|
raise HTTPException(status_code=400, detail="Undecodeable Json string") from None
|
||||||
|
|
||||||
|
# Validate Phases
|
||||||
|
try:
|
||||||
|
phases: list[Phase] = [Phase(**phase) for phase in program_list]
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("❌ Failed to convert to Phase objects: %s", e)
|
||||||
|
raise HTTPException(status_code=400, detail="Non-Phase String") from None
|
||||||
|
|
||||||
|
logger.info(f"Succesfully recieved {len(phases)} Phase(s).")
|
||||||
|
for p in phases:
|
||||||
|
logger.info(
|
||||||
|
f"Phase {p.id}: "
|
||||||
|
f"{len(p.phaseData.norms)} norms, "
|
||||||
|
f"{len(p.phaseData.goals)} goals, "
|
||||||
|
f"{len(p.phaseData.triggers) if hasattr(p.phaseData, 'triggers') else 0} triggers"
|
||||||
|
)
|
||||||
|
|
||||||
|
# send away
|
||||||
|
topic = b"program"
|
||||||
|
body = json.dumps([p.model_dump() for p in phases]).encode("utf-8")
|
||||||
|
pub_socket = request.app.state.endpoints_pub_socket
|
||||||
|
await pub_socket.send_multipart([topic, body])
|
||||||
|
|
||||||
|
return {"status": "Program parsed", "phase_count": len(phases)}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
from fastapi.routing import APIRouter
|
from fastapi.routing import APIRouter
|
||||||
|
|
||||||
from control_backend.api.v1.endpoints import command, logs, message, sse
|
from control_backend.api.v1.endpoints import command, logs, message, program, sse
|
||||||
|
|
||||||
api_router = APIRouter()
|
api_router = APIRouter()
|
||||||
|
|
||||||
@@ -11,3 +11,5 @@ api_router.include_router(sse.router, tags=["SSE"])
|
|||||||
api_router.include_router(command.router, tags=["Commands"])
|
api_router.include_router(command.router, tags=["Commands"])
|
||||||
|
|
||||||
api_router.include_router(logs.router, tags=["Logs"])
|
api_router.include_router(logs.router, tags=["Logs"])
|
||||||
|
|
||||||
|
api_router.include_router(program.router, tags=["Program"])
|
||||||
|
|||||||
38
src/control_backend/schemas/program.py
Normal file
38
src/control_backend/schemas/program.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class Norm(BaseModel):
|
||||||
|
id: str
|
||||||
|
name: str
|
||||||
|
value: str
|
||||||
|
|
||||||
|
|
||||||
|
class Goal(BaseModel):
|
||||||
|
id: str
|
||||||
|
name: str
|
||||||
|
description: str
|
||||||
|
achieved: bool
|
||||||
|
|
||||||
|
|
||||||
|
class Trigger(BaseModel):
|
||||||
|
id: str
|
||||||
|
label: str
|
||||||
|
type: str
|
||||||
|
value: list[str]
|
||||||
|
|
||||||
|
|
||||||
|
class PhaseData(BaseModel):
|
||||||
|
norms: list[Norm]
|
||||||
|
goals: list[Goal]
|
||||||
|
triggers: list[Trigger]
|
||||||
|
|
||||||
|
|
||||||
|
class Phase(BaseModel):
|
||||||
|
id: str
|
||||||
|
name: str
|
||||||
|
nextPhaseId: str
|
||||||
|
phaseData: PhaseData
|
||||||
|
|
||||||
|
|
||||||
|
class Program(BaseModel):
|
||||||
|
phases: list[Phase]
|
||||||
Reference in New Issue
Block a user