fix(scenes): restore cinema seat controls and ordered romantic lighting

This commit is contained in:
Miayui
2026-09-22 13:01:46 +08:00
parent 4d99702acd
commit 6b9ee0f349
24 changed files with 908 additions and 83 deletions
+20
View File
@@ -1,5 +1,25 @@
# Web Console
## 浪漫相关模式的外设灯光
浪漫、约会、纪念日及浪漫影院等模式使用当前持久授权的红外目录:打开灯笼、蜡烛和灯带,
所有具有红色指令的外设灯光统一设置为红色。每台设备均先发送开启指令,再发送红色指令;
开启下发失败时终止后续序列。缺少开启能力的设备不能只发送颜色指令,缺少红色能力时
不以其他颜色替代。新模式生成、车端卡片导入及已有卡片的执行入口均使用该规则,
原生座椅、空调等场景动作保持原有定义。
## 车端影院模式的副驾姿态
从 `from_n7/scenes` 同步的 `local_scene.seat_preset` 会在执行时按 N7 的 M0–M3
标定转换为副驾前后、坐盆、靠背、腿托四项位置控制,沿用现有座椅安全校验和执行器。
例如 `[246, 112, 405, 342]` 脉冲转换为当前百分比协议的 `[51, 30, 44, 53]`。
原始脉冲保留在卡片中,已保存的影院模式及继承该预设的本地模式无需重新上传。
不完整、越界或错误座位的预设会被拒绝;带延时的预设继续交由车端处理。
视频启动等车端专属动作仍走 `transto_n7/ctl`,不会重复转发已由 PC 处理的四轴姿态。
模式卡片区分完整成功、部分处理、指令已提交待车端确认及模拟校验,消息入队不会显示为
车辆执行完成。
## 座椅固件开发工作台
“开发指令”支持输入“增加动态木马功能”,也可点击位于位置控制、座椅通风、座椅加热之后的
+40 -14
View File
@@ -7049,12 +7049,16 @@ function setModeExecutionState(modeProfileId, modeState = "idle") {
? "已停止"
: active && modeState === "succeeded"
? "已完成"
: active && modeState === "submitted"
? "待车端确认"
: active && modeState === "partial"
? "部分处理"
: active && modeState === "failed"
? "执行失败"
: "点击执行";
}
});
if (modeProfileId && ["succeeded", "failed", "started", "ended", "stopped"].includes(modeState)) {
if (modeProfileId && ["succeeded", "failed", "started", "ended", "stopped", "submitted", "partial"].includes(modeState)) {
state.modeRunTimer = window.setTimeout(() => setModeExecutionState(null), 2800);
}
}
@@ -7073,6 +7077,35 @@ function hasExecutableModeCommandFlow(mode) {
|| (plans && typeof plans === "object" && Object.keys(plans).length > 0);
}
function savedModeExecutionView(mode, result) {
const name = mode?.name || "功能模式";
if (modeScheduleExecutionView(result)?.state === "processing") {
return { state: "started", title: `${name}时间轴已启动`, toast: "时间轴已启动,可输入“停止动态模式”提前结束" };
}
if (!["succeeded", "partially_succeeded"].includes(result.status)) {
return { state: "failed", title: `${name}执行未完成`, toast: `${name}未完成,请查看执行结果` };
}
const preview = result.execution_mode === "dry_run";
const waiting = !preview && executionVerificationStatus(result) === "submitted_unverified";
if (result.status === "partially_succeeded") {
return {
state: "partial",
title: `${name}${preview ? "模拟校验部分通过" : waiting ? "部分指令已提交,待车端确认" : "部分完成"}`,
toast: preview
? `${name}模拟校验部分通过,仍有未由 PC 执行的动作`
: `${name}仅部分处理成功,请查看各项指令和车端转发状态`,
};
}
if (waiting) {
return { state: "submitted", title: `${name}指令已提交,待车端确认`, toast: "指令已提交,尚未确认车辆执行完成" };
}
return {
state: "succeeded",
title: `${name}${preview ? "模拟校验完成" : "执行完成"}`,
toast: `${name}${preview ? "模拟校验完成" : "已完成保存的控制指令流"}`,
};
}
async function executeSavedModeProfile(mode, { command = "" } = {}) {
if (!hasExecutableModeCommandFlow(mode)) return false;
const modeCommand = String(command || `进入${mode.name || mode.id}`).trim();
@@ -7125,14 +7158,9 @@ async function executeSavedModeProfile(mode, { command = "" } = {}) {
);
if (!payload) throw new Error("模式卡片未返回执行结果");
renderResult(payload);
const scheduleRunning = modeScheduleExecutionView(payload)?.state === "processing";
const completed = ["succeeded", "partially_succeeded"].includes(payload.status);
$("#resultTitle").textContent = scheduleRunning
? `${mode.name}时间轴已启动`
: completed
? `${mode.name}执行完成`
: `${mode.name}执行未完成`;
setModeExecutionState(mode.id, scheduleRunning ? "started" : completed ? "succeeded" : "failed");
const executionView = savedModeExecutionView(mode, payload);
$("#resultTitle").textContent = executionView.title;
setModeExecutionState(mode.id, executionView.state);
if (assistantModeHandle && assistant?.completeModeExecution) {
assistant.completeModeExecution(assistantModeHandle, mode, payload, {
command: modeCommand,
@@ -7141,11 +7169,7 @@ async function executeSavedModeProfile(mode, { command = "" } = {}) {
assistant?.announceModeExecution?.(mode, payload, { command: modeCommand });
}
if (state.executionMode === "hardware") await synchronizePhysicalSeatState();
showToast(scheduleRunning
? "时间轴已启动,可输入“停止动态模式”提前结束"
: completed
? `${mode.name}已直接执行保存的控制指令流`
: `${mode.name}未完成,请查看执行结果`);
showToast(executionView.toast);
} catch (error) {
setModeExecutionState(mode.id, "failed");
setRunStatus("failed", "MODE FAILED");
@@ -8926,6 +8950,8 @@ function renderResult(result) {
? `${result.mode_profile?.name ? `${result.mode_profile.name} · ` : ""}${scheduleExecution.title}`
: peripheralRoute && executionSucceeded
? `${result.peripheral?.name || "第三方外设"} · ${result.peripheral?.action || "动作已执行"}`
: savedModeExecution && result.mode_profile
? savedModeExecutionView(result.mode_profile, result).title
: partiallySucceeded
? `${domains.length} 域场景部分完成`
: executionSucceeded
+1 -1
View File
@@ -39,7 +39,7 @@
<script src="/vendor/three/0.147.0/fflate.min.js" defer></script>
<script src="/vendor/three/0.147.0/FBXLoader.js" defer></script>
<script src="/network.js?v=android-pad-2" defer></script>
<script src="/app.js?v=dual-seat-147-video-media" defer></script>
<script src="/app.js?v=dual-seat-148-seat-preset" defer></script>
<script src="/speech-input.js?v=sensevoice-13" defer></script>
<script type="module" src="/gesture-control.js?v=android-pad-9"></script>
<script src="/pwa.js?v=android-pad-3" defer></script>
+2 -2
View File
@@ -1,4 +1,4 @@
const CACHE_VERSION = "cognitive-agent-pad-v335-firmware-rocking-horse";
const CACHE_VERSION = "cognitive-agent-pad-v336-seat-preset";
const SHELL_CACHE = `${CACHE_VERSION}-shell`;
const RUNTIME_CACHE = `${CACHE_VERSION}-runtime`;
const APP_SHELL = [
@@ -6,7 +6,7 @@ const APP_SHELL = [
"/index.html",
"/styles.css?v=dual-seat-24-dialog-size",
"/network.js?v=android-pad-2",
"/app.js?v=dual-seat-147-video-media",
"/app.js?v=dual-seat-148-seat-preset",
"/gesture-control.js?v=android-pad-9",
"/pwa.js?v=android-pad-3",
"/console-v2.css?v=delivery-state-69",