Init
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
from .models import (
|
||||
AckStatus,
|
||||
CommandEnvelope,
|
||||
CommandRecord,
|
||||
CommandType,
|
||||
NodeRole,
|
||||
NodeStatus,
|
||||
OutputNode,
|
||||
PerformanceSample,
|
||||
PhysicalOutput,
|
||||
Scene,
|
||||
SceneState,
|
||||
Tile,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AckStatus",
|
||||
"CommandEnvelope",
|
||||
"CommandRecord",
|
||||
"CommandType",
|
||||
"NodeRole",
|
||||
"NodeStatus",
|
||||
"OutputNode",
|
||||
"PerformanceSample",
|
||||
"PhysicalOutput",
|
||||
"Scene",
|
||||
"SceneState",
|
||||
"Tile",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
from pydantic import BaseModel, Field, HttpUrl
|
||||
|
||||
from led_platform.core.clock import epoch_ms
|
||||
|
||||
|
||||
class CommandType(str, Enum):
|
||||
HELLO = "hello"
|
||||
STATE = "state"
|
||||
PREPARE_SCENE = "prepare_scene"
|
||||
COMMIT_SCENE = "commit_scene"
|
||||
COMPONENT_ACTION = "component_action"
|
||||
CLOCK_PING = "clock_ping"
|
||||
CLOCK_PONG = "clock_pong"
|
||||
TELEMETRY = "telemetry"
|
||||
HEARTBEAT = "heartbeat"
|
||||
ACK = "ack"
|
||||
ERROR = "error"
|
||||
|
||||
|
||||
class AckStatus(str, Enum):
|
||||
PREPARED = "prepared"
|
||||
COMMITTED = "committed"
|
||||
ACTION_COMMITTED = "action_committed"
|
||||
LATE = "late"
|
||||
ERROR = "error"
|
||||
|
||||
|
||||
class NodeRole(str, Enum):
|
||||
ADMIN = "admin"
|
||||
OUTPUT = "output"
|
||||
RENDERER = "renderer"
|
||||
|
||||
|
||||
class NodeStatus(str, Enum):
|
||||
STARTING = "starting"
|
||||
READY = "ready"
|
||||
DEGRADED = "degraded"
|
||||
ERROR = "error"
|
||||
|
||||
|
||||
class PhysicalOutput(BaseModel):
|
||||
id: str
|
||||
index: int
|
||||
x: int
|
||||
y: int
|
||||
width: int = 3840
|
||||
height: int = 2160
|
||||
connector: str | None = None
|
||||
|
||||
|
||||
class Tile(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
x: int
|
||||
y: int
|
||||
width: int
|
||||
height: int
|
||||
wall_width: int
|
||||
wall_height: int
|
||||
desktop_width: int
|
||||
desktop_height: int
|
||||
physical_outputs: list[PhysicalOutput] = Field(default_factory=list)
|
||||
|
||||
@property
|
||||
def right(self) -> int:
|
||||
return self.x + self.width
|
||||
|
||||
|
||||
class Scene(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
url: str | HttpUrl = "/wall-app"
|
||||
view: str
|
||||
description: str | None = None
|
||||
preload: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class SceneState(BaseModel):
|
||||
active_scene_id: str
|
||||
active_view: str
|
||||
active_url: str
|
||||
version: int
|
||||
scene_started_at_ms: int
|
||||
apply_at_ms: int | None = None
|
||||
action: str | None = None
|
||||
|
||||
|
||||
class CommandEnvelope(BaseModel):
|
||||
type: CommandType
|
||||
command_id: str = Field(default_factory=lambda: uuid4().hex)
|
||||
payload: dict[str, Any] = Field(default_factory=dict)
|
||||
sent_at_ms: int = Field(default_factory=epoch_ms)
|
||||
|
||||
|
||||
class OutputNode(BaseModel):
|
||||
node_id: str
|
||||
tile_id: str
|
||||
status: NodeStatus = NodeStatus.READY
|
||||
connected_at_ms: int = Field(default_factory=epoch_ms)
|
||||
last_seen_ms: int = Field(default_factory=epoch_ms)
|
||||
app_version: str | None = None
|
||||
user_agent: str | None = None
|
||||
clock_offset_ms: float | None = None
|
||||
rtt_ms: float | None = None
|
||||
fps: float | None = None
|
||||
frame_time_ms: float | None = None
|
||||
dropped_frames: int | None = None
|
||||
gpu_hint: str | None = None
|
||||
|
||||
|
||||
class PerformanceSample(BaseModel):
|
||||
node_id: str
|
||||
tile_id: str
|
||||
fps: float
|
||||
frame_time_ms: float
|
||||
dropped_frames: int = 0
|
||||
at_ms: int = Field(default_factory=epoch_ms)
|
||||
|
||||
|
||||
class CommandRecord(BaseModel):
|
||||
command_id: str
|
||||
command_type: CommandType
|
||||
target_tiles: list[str]
|
||||
apply_at_ms: int
|
||||
created_at_ms: int = Field(default_factory=epoch_ms)
|
||||
expires_at_ms: int
|
||||
payload: dict[str, Any] = Field(default_factory=dict)
|
||||
acks: list[dict[str, Any]] = Field(default_factory=list)
|
||||
|
||||
@property
|
||||
def complete(self) -> bool:
|
||||
completed = {
|
||||
ack.get("tile_id")
|
||||
for ack in self.acks
|
||||
if ack.get("status") in {AckStatus.COMMITTED, AckStatus.ACTION_COMMITTED}
|
||||
}
|
||||
return set(self.target_tiles).issubset({str(tile) for tile in completed if tile})
|
||||
Reference in New Issue
Block a user