feat(n7web): enhance control workflows and status feedback

This commit is contained in:
Miayui
2026-09-16 12:52:07 +08:00
parent 84a3cd73bb
commit 0e4328f548
45 changed files with 7317 additions and 1001 deletions
@@ -10,7 +10,11 @@ ROOT = Path(__file__).resolve().parents[2]
def source(name: str) -> str:
return (ROOT / "apps" / "web-console" / name).read_text(encoding="utf-8")
web_root = ROOT / "apps" / "web-console"
content = (web_root / name).read_text(encoding="utf-8")
if name == "index.html":
content += "\n" + (web_root / "src" / "App.vue").read_text(encoding="utf-8")
return content
def test_frontend_only_exposes_user_orchestration_and_extension() -> None:
@@ -827,8 +831,8 @@ def test_parsed_command_delivery_is_independent_of_transport_and_target() -> Non
assert [commands[0]["deliveryLabel"] for commands in parsed] == [
"未下发",
"未下发",
"下发",
"下发",
"已下发 · 待状态确认",
"已下发 · 待状态确认",
"安全阻止",
]
assert [commands[0]["deliveryState"] for commands in parsed] == [
@@ -848,6 +852,137 @@ def test_parsed_command_delivery_is_independent_of_transport_and_target() -> Non
]
def test_delivery_status_distinguishes_blocked_failed_aborted_and_verified() -> None:
node = shutil.which("node")
if node is None:
return
console = source("console-v2.js")
assert 'class="v2-can-command-delivery-detail"' in console
assert "<b>状态说明</b>" in console
start = console.index(" const normalizedCanTarget")
end = console.index("\n\n function renderParsedCanCommands", start)
helpers = console[start:end]
def sample(
operation: dict,
*,
status: str,
message: str = "",
telemetry: dict | None = None,
) -> dict:
return {
"execution_mode": "hardware",
"control_transport": "wireless_mqtt",
"function_configs": {
"sample": {"spec": {"operations": [operation]}}
},
"execution_report": {
"spec": {
"results": [
{
"ref": operation["id"],
"status": status,
"message": message,
"telemetry": telemetry or {},
}
]
}
},
}
samples = [
sample(
{
"id": "window-blocked",
"capability": "window.set_position",
"target": {"zone": "front-right"},
"parameters": {"position_pct": 20},
},
status="failed",
message="未收到有效且新鲜的 0x388 车窗位置,拒绝启动机构",
telemetry={"verification_status": "failed_or_blocked"},
),
sample(
{
"id": "infrared-dispatch-failed",
"capability": "infrared.emit",
"target": {"device": "reading-light", "name": "阅读灯"},
"parameters": {"action": "开启", "slot": 2},
},
status="failed",
message="GSCU1B 尚未连接到本地 TCP 桥接",
telemetry={"delivery_status": "failed"},
),
sample(
{
"id": "infrared-aborted",
"capability": "infrared.emit",
"target": {"device": "reading-light", "name": "阅读灯"},
"parameters": {"action": "切换为暖光", "slot": 5},
},
status="aborted",
message="前序红外指令失败,未执行",
telemetry={"delivery_status": "not_submitted"},
),
sample(
{
"id": "climate-verified",
"capability": "climate.set_power",
"target": {"zone": "vehicle"},
"parameters": {"state": "on"},
},
status="succeeded",
telemetry={
"delivery_status": "delivered",
"physical_effect_confirmed": True,
},
),
sample(
{
"id": "climate-post-submit-failed",
"capability": "climate.set_power",
"target": {"zone": "vehicle"},
"parameters": {"state": "on"},
},
status="failed",
message="网关提交后执行失败",
telemetry={"delivery_status": "submitted"},
),
]
script = (
f"{helpers}\n"
f"const samples = {json.dumps(samples, ensure_ascii=False)};\n"
"process.stdout.write(JSON.stringify(samples.map((sample) => "
"parsedCanCommands(sample)[0])));"
)
completed = subprocess.run(
[node, "-"],
input=script,
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
commands = json.loads(completed.stdout)
assert [item["deliveryLabel"] for item in commands] == [
"安全阻止",
"下发失败",
"已中止",
"执行已确认",
"执行失败",
]
assert [item["deliveryState"] for item in commands] == [
"blocked",
"failed",
"aborted",
"submitted",
"failed",
]
assert "0x388 实时位置" in commands[0]["deliveryDetail"]
assert commands[1]["deliveryDetail"] == "GSCU1B 尚未连接到本地 TCP 桥接"
def test_mode_editor_renders_readable_commands_with_expandable_mqtt_sequences() -> None:
app = source("app.js")
console = source("console-v2.js")
@@ -870,6 +1005,12 @@ def test_mode_editor_renders_readable_commands_with_expandable_mqtt_sequences()
assert 'executionMode: "dry_run"' in app
assert 'clientType: "mode_editor_compile"' in app
assert "previewOnly: true" in app
compile_flow = app.split(
"async function executeModeEditorInstruction", 1
)[1].split("async function completeModeEditor", 1)[0]
assert "renderResult(" not in compile_flow
assert "cognitive-agent:validated-can-commands" not in compile_flow
assert "cognitive-agent:result" not in compile_flow
assert 'const nonDispatchTaskSources = new Set(["mode_editor_compile"])' in console
assert "if (isDispatchTask(task))" in console
assert "下发控制指令" in console