103 lines
3.2 KiB
Python
103 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Dict, Tuple
|
|
import uuid
|
|
|
|
from .actions import ActionEngine
|
|
from .config import ServerSettings
|
|
from .models import (
|
|
ActionPayload,
|
|
ActionResult,
|
|
GridCellModel,
|
|
GridDescriptor,
|
|
GridInitRequest,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class _StoredCell:
|
|
model: GridCellModel
|
|
center: Tuple[int, int]
|
|
|
|
|
|
class VisionGrid:
|
|
def __init__(self, request: GridInitRequest, grid_id: str, rows: int, columns: int):
|
|
self.grid_id = grid_id
|
|
self.screenshot = request.screenshot_base64
|
|
self.memo = request.memo
|
|
self.rows = rows
|
|
self.columns = columns
|
|
self.width = request.width
|
|
self.height = request.height
|
|
self.cells: Dict[str, _StoredCell] = {}
|
|
self._engine = ActionEngine(self)
|
|
self._build_cells()
|
|
|
|
def _build_cells(self, margin: int = 4) -> None:
|
|
cell_width = max(1, self.width // self.columns)
|
|
cell_height = max(1, self.height // self.rows)
|
|
|
|
for row in range(self.rows):
|
|
for col in range(self.columns):
|
|
left = col * cell_width + margin
|
|
top = row * cell_height + margin
|
|
right = min(self.width - margin, (col + 1) * cell_width - margin)
|
|
bottom = min(self.height - margin, (row + 1) * cell_height - margin)
|
|
cell_id = f"{self.grid_id}-{row}-{col}"
|
|
bounds = (left, top, right, bottom)
|
|
center = ((left + right) // 2, (top + bottom) // 2)
|
|
cell = GridCellModel(
|
|
cell_id=cell_id,
|
|
row=row,
|
|
column=col,
|
|
bounds=bounds,
|
|
)
|
|
self.cells[cell_id] = _StoredCell(model=cell, center=center)
|
|
|
|
def describe(self) -> GridDescriptor:
|
|
return GridDescriptor(
|
|
grid_id=self.grid_id,
|
|
rows=self.rows,
|
|
columns=self.columns,
|
|
cells=[cell.model for cell in self.cells.values()],
|
|
metadata={
|
|
"memo": self.memo or "",
|
|
"width": self.width,
|
|
"height": self.height,
|
|
},
|
|
)
|
|
|
|
def resolve_cell_center(self, cell_id: str) -> Tuple[int, int]:
|
|
cell = self.cells.get(cell_id)
|
|
if not cell:
|
|
raise KeyError(f"Unknown cell {cell_id}")
|
|
return cell.center
|
|
|
|
def apply_action(self, payload: ActionPayload) -> ActionResult:
|
|
return self._engine.plan(payload)
|
|
|
|
|
|
class GridManager:
|
|
def __init__(self, settings: ServerSettings):
|
|
self.settings = settings
|
|
self._grids: Dict[str, VisionGrid] = {}
|
|
|
|
@property
|
|
def grid_count(self) -> int:
|
|
return len(self._grids)
|
|
|
|
def create_grid(self, request: GridInitRequest) -> VisionGrid:
|
|
rows = request.rows or self.settings.grid_rows
|
|
columns = request.columns or self.settings.grid_cols
|
|
grid_id = uuid.uuid4().hex
|
|
grid = VisionGrid(request, grid_id, rows, columns)
|
|
self._grids[grid_id] = grid
|
|
return grid
|
|
|
|
def get_grid(self, grid_id: str) -> VisionGrid:
|
|
try:
|
|
return self._grids[grid_id]
|
|
except KeyError as exc:
|
|
raise KeyError(f"Grid {grid_id} not found") from exc
|