126 lines
5.6 KiB
Python
126 lines
5.6 KiB
Python
"""Durable manual-demonstration commands, sharing the automation transaction."""
|
||
|
||
import time
|
||
from uuid import uuid4
|
||
|
||
from artemis.exploration.store import ExplorationConflict, ExplorationStore
|
||
|
||
|
||
TERMINAL = {"completed", "failed", "cancelled", "timed_out"}
|
||
|
||
|
||
class Demonstrations:
|
||
def __init__(self, repository: ExplorationStore):
|
||
self.repository = repository
|
||
|
||
def get(self, demo_id: str) -> dict:
|
||
value = self.repository.database.get("exploration_demo", demo_id)
|
||
if value is None:
|
||
raise KeyError("人工示范不存在")
|
||
return value
|
||
|
||
def list(self, session_id: str) -> list[dict]:
|
||
self.repository.get(session_id)
|
||
with self.repository.database.connection() as connection:
|
||
rows = connection.execute(
|
||
"SELECT payload FROM automation_records WHERE kind='exploration_demo' "
|
||
"AND json_extract(payload,'$.exploration_id')=? ORDER BY updated_at",
|
||
(session_id,),
|
||
).fetchall()
|
||
import json
|
||
|
||
return [json.loads(row[0]) for row in rows]
|
||
|
||
def request(self, session_id: str, source: str, request_id: str) -> dict:
|
||
repo = self.repository
|
||
with repo._transaction() as connection:
|
||
session = repo._require(connection, session_id)
|
||
|
||
def create():
|
||
run_id = session["active_run_id"]
|
||
run = repo._get(connection, "exploration_run", run_id) if run_id else None
|
||
if (
|
||
not run
|
||
or run["kind"] != "exploration"
|
||
or run["status"] not in {"running", "waiting_user"}
|
||
):
|
||
raise ExplorationConflict("请等待探索开始执行,再请求人工示范")
|
||
if session["active_demonstration_id"]:
|
||
raise ExplorationConflict("已有进行中的人工示范")
|
||
value = {
|
||
"id": str(uuid4()),
|
||
"exploration_id": session_id,
|
||
"run_id": run_id,
|
||
"source": source,
|
||
"status": "requested",
|
||
"command": None,
|
||
"created_at": time.time(),
|
||
"deadline": None,
|
||
"media": [],
|
||
"error": None,
|
||
"timeout_seconds": session["config"]["demonstration_timeout_seconds"],
|
||
"warning_seconds": session["config"]["demonstration_warning_seconds"],
|
||
}
|
||
repo._put(connection, "exploration_demo", value["id"], value)
|
||
session["active_demonstration_id"] = value["id"]
|
||
repo._save_session(connection, session)
|
||
return value
|
||
|
||
return repo._operation(connection, f"{session_id}:demo", request_id, source, create)
|
||
|
||
def command(
|
||
self, session_id: str, demo_id: str, command: str, request_id: str, *, seconds: int = 0
|
||
) -> dict:
|
||
if command not in {"finish", "cancel", "extend"}:
|
||
raise ValueError("不支持的示范操作")
|
||
if command == "extend" and not 60 <= seconds <= 86400:
|
||
raise ValueError("延长时长必须在 60–86400 秒之间")
|
||
repo = self.repository
|
||
with repo._transaction() as connection:
|
||
session = repo._require(connection, session_id)
|
||
demo = repo._get(connection, "exploration_demo", demo_id)
|
||
if not demo or demo["exploration_id"] != session_id:
|
||
raise KeyError("人工示范不存在")
|
||
|
||
def update():
|
||
if session["active_demonstration_id"] != demo_id or demo["status"] not in {
|
||
"requested",
|
||
"recording",
|
||
}:
|
||
raise ExplorationConflict("示范已结束或正在分析")
|
||
if command != "cancel" and demo["status"] != "recording":
|
||
raise ExplorationConflict("请等待录像就绪")
|
||
if demo.get("command"):
|
||
raise ExplorationConflict("示范已请求结束")
|
||
if demo.get("deadline") and time.time() >= demo["deadline"]:
|
||
raise ExplorationConflict("示范已到时,请等待保存录像")
|
||
if command == "extend":
|
||
demo["deadline"] += seconds
|
||
else:
|
||
demo["command"] = command
|
||
return repo._put(connection, "exploration_demo", demo_id, demo)
|
||
|
||
return repo._operation(
|
||
connection, f"{demo_id}:command", request_id, [command, seconds], update
|
||
)
|
||
|
||
def transition(self, demo_id: str, status: str, **fields) -> dict:
|
||
repo = self.repository
|
||
with repo._transaction() as connection:
|
||
demo = repo._get(connection, "exploration_demo", demo_id)
|
||
if not demo:
|
||
raise KeyError("人工示范不存在")
|
||
session = repo._require(connection, demo["exploration_id"])
|
||
if demo["status"] in TERMINAL:
|
||
return demo
|
||
if session["active_run_id"] != demo["run_id"]:
|
||
raise ExplorationConflict("示范所属执行已结束")
|
||
demo.update(status=status, **fields)
|
||
if status == "recording":
|
||
demo.update(started_at=time.time(), deadline=time.time() + demo["timeout_seconds"])
|
||
if status in TERMINAL:
|
||
demo["finished_at"] = time.time()
|
||
session["active_demonstration_id"] = None
|
||
repo._save_session(connection, session)
|
||
return repo._put(connection, "exploration_demo", demo_id, demo)
|