重整cmake

This commit is contained in:
2026-08-11 15:11:56 +08:00
parent 5401a14708
commit 274dd4c044
12 changed files with 307 additions and 157 deletions
+42 -18
View File
@@ -161,6 +161,8 @@ class GallerySession {
this.telemetry = {};
this.controlValues = new Map();
this.lastSubmittedControls = new Map();
this.pendingStateRequests = [];
this.lastFeedbackAt = 0;
this.frameCount = 0;
this.framePending = false;
this.frameTimeout = null;
@@ -283,7 +285,8 @@ class GallerySession {
if (this.socket !== socket) return;
this.socketState = "ready";
this.socketText = "WS 已连接";
this.send("gallery_open", {case: this.definition.id, frame_mode: this.mode.id});
this.lastFeedbackAt = 0;
this.sendStateRequest("open", "gallery_open", {case: this.definition.id, frame_mode: this.mode.id});
this.resize();
});
socket.addEventListener("message", event => {
@@ -299,6 +302,9 @@ class GallerySession {
this.framePending = false;
clearTimeout(this.frameTimeout);
this.frameTimeout = null;
this.pendingStateRequests.length = 0;
this.lastSubmittedControls.clear();
this.lastFeedbackAt = 0;
this.socketState = "error";
this.socketText = this.displayVisible() ? "连接关闭 · 自动重连" : "连接关闭 · 等待可见";
this.updateMotionStatus();
@@ -315,6 +321,11 @@ class GallerySession {
send(type, payload = {}) {
if (this.socket?.readyState === WebSocket.OPEN) this.socket.send(message(type, payload));
}
sendStateRequest(kind, type, payload = {}) {
if (this.socket?.readyState !== WebSocket.OPEN) return;
this.pendingStateRequests.push(kind);
this.socket.send(message(type, payload));
}
receiveJson(raw) {
let data;
try {
@@ -324,19 +335,21 @@ class GallerySession {
return;
}
if (data.type === "error") {
this.pendingStateRequests.shift();
const detail = Object.values(data.field_errors || {})[0] || data.message || "后端拒绝操作";
this.lastSubmittedControls.clear();
notify(detail, true);
if (menuSession === this) {
menuStatus = detail;
renderMenu(this, true);
updateMenuData();
}
return;
}
if (data.type !== "case_state") return;
const requestKind = this.pendingStateRequests.shift();
this.controls = data.controls?.data || [];
this.actions = data.actions?.data || [];
this.telemetry = data.telemetry || {};
if (requestKind === "open" || requestKind === "refresh") this.telemetry = data.telemetry || {};
this.controlValues.clear();
for (const item of this.controls) this.controlValues.set(item.id, item.value);
this.lastSubmittedControls.clear();
@@ -467,10 +480,19 @@ class GallerySession {
}, 1500);
}
refreshBackendState() {
const time = performance.now();
if (!this.ready || this.socket?.readyState !== WebSocket.OPEN) return;
this.pushClientFeedback(performance.now());
this.sendStateRequest("refresh", "gallery_refresh");
}
pushClientFeedback(time) {
this.lastFeedbackAt = time;
this.transportFps = this.currentRate(this.transportTimes, time);
this.presentationFps = this.currentRate(this.presentationTimes, time);
this.send("gallery_refresh", {client_metrics: this.clientMetrics(time)});
this.send("gallery_feedback", {client_metrics: this.clientMetrics(time)});
}
sendClientFeedback(time) {
if (!this.ready || !this.displayVisible() || this.socket?.readyState !== WebSocket.OPEN || time - this.lastFeedbackAt < 650) return;
this.pushClientFeedback(time);
}
clientMetrics(time = performance.now()) {
return {
@@ -525,7 +547,7 @@ class GallerySession {
const value = normalizeEventValue(payload, id);
if (Object.is(this.lastSubmittedControls.get(id), value) || Object.is(this.controlValues.get(id), value)) return;
this.lastSubmittedControls.set(id, value);
this.send("gallery_patch", {patch: {[id]: value}});
this.sendStateRequest("patch", "gallery_patch", {patch: {[id]: value}});
menuStatus = `提交 ${item.api} · 等待后端回读`;
}
runAction(id, payload) {
@@ -536,7 +558,7 @@ class GallerySession {
const field = `action_${item.id}`;
request.argument = normalizeEventValue(payload, field);
}
this.send("gallery_action", request);
this.sendStateRequest("action", "gallery_action", request);
menuStatus = `执行 ${item.api}`;
if (["mode_render", "mode_dequeue", "mode_cycle"].includes(item.id)) setTimeout(() => this.requestFrame(performance.now(), true), 40);
}
@@ -555,7 +577,7 @@ class GallerySession {
const limitDurations = {frequency_limited: observer.target_interval_ns, paint_limited: observer.paint_duration_ns, render_limited: observer.render_duration_ns, consumer_limited: observer.consumer_interval_ns};
const limitName = limitNames[limit.current] || limit.current || "N/A";
const limitDuration = limitDurations[limit.current];
const client = this.clientMetrics();
const client = this.telemetry.client_performance || {};
const observerValues = {};
const clientValues = {};
observerValues.mode = observer.mode || this.mode.id;
@@ -571,10 +593,10 @@ class GallerySession {
motionText: this.motionText,
frameCount: formatInteger(this.frameCount),
backendFps: formatFps(performanceData.measured_fps),
transportFps: formatFps(performanceData.pixel_response_fps || this.transportFps),
presentationFps: formatFps(this.presentationFps),
roundTrip: Number(this.frameRoundTripMs || 0).toFixed(2),
overwritten: formatInteger(this.overwrittenPixelFrames),
transportFps: formatFps(performanceData.pixel_response_fps || client.transport_fps),
presentationFps: formatFps(client.presentation_fps),
roundTrip: Number(client.frame_round_trip_ms || 0).toFixed(2),
overwritten: formatInteger(client.overwritten_pixel_frames),
renderMs: Number(performanceData.last_render_ms || 0).toFixed(2),
encodeMs: Number(performanceData.last_pixel_encode_ms || 0).toFixed(2),
bandwidth: Number(performanceData.pixel_payload_megabytes_per_second || 0).toFixed(1),
@@ -583,8 +605,8 @@ class GallerySession {
points: `${formatInteger(dataShape.input_points)}${formatInteger(dataShape.rendered_elements)}`,
limit: limitDuration === undefined ? limitName : `${limitName} · ${formatNanoseconds(limitDuration)}`,
event: observer.last_event || "none",
timeouts: formatInteger(this.frameTimeoutCount),
pixelAge: this.lastPixelReceivedAt ? `${Math.max(0, performance.now() - this.lastPixelReceivedAt).toFixed(0)} ms` : "—",
timeouts: formatInteger(client.frame_request_timeout_count),
pixelAge: Number(client.last_pixel_receive_age_ms || 0) > 0 ? `${Number(client.last_pixel_receive_age_ms).toFixed(0)} ms` : "—",
limitFrequency: limitTile(observer.frequency_limit_enabled !== false, limit.current === "frequency_limited", observer.target_interval_ns),
limitPaint: limitTile(true, limit.current === "paint_limited", observer.paint_duration_ns),
limitRender: limitTile(true, limit.current === "render_limited", observer.render_duration_ns),
@@ -711,9 +733,10 @@ function controlEventAction(session, item, source) {
}
function controlSchema(session, item) {
const control = {...item.amis};
const formId = `control_${session.key}_${item.id}`.replace(/[^A-Za-z0-9_-]/g, "_");
const eventName = ["switch", "select", "input-color"].includes(control.type) ? "change" : "blur";
control.onEvent = {[eventName]: {actions: [controlEventAction(session, item, "context.data")]}};
return {type: "form", wrapWithPanel: false, className: "rv-control-form", body: [control], actions: []};
control.onEvent = {[eventName]: {actions: [{actionType: "submit", componentId: formId}]}};
return {type: "form", id: formId, wrapWithPanel: false, className: "rv-control-form", body: [control], actions: [], onEvent: {submit: {preventDefault: true, actions: [controlEventAction(session, item, "context.data")]}}};
}
function buildControlTab(session) {
return [...groupItems(session.controls)].map(([group, items]) => ({type: "panel", className: "rv-control-panel", title: group, body: items.map(item => controlSchema(session, item))}));
@@ -725,8 +748,8 @@ function actionSchema(session, item) {
const description = item.description || "";
const body = [{type: "tpl", tpl: `<div class="rv-action-head"><div><b>${escapeHtml(label)}</b><code>${escapeHtml(api)}</code></div>${description ? `<small>${escapeHtml(description)}</small>` : ""}</div>`}];
if (item.argument_input) body.push({...item.argument_amis, name: field});
body.push({type: "button", label: `执行 · ${label}`, level: "primary", onEvent: {click: {actions: [{actionType: "custom", script: `window.RenderiveGallery.runAction(${quote(session.key)},${quote(item.id)},context.data);`}]}}});
return {type: "form", wrapWithPanel: false, className: "rv-action-form", body, actions: []};
body.push({type: "button", actionType: "submit", label: `执行 · ${label}`, level: "primary"});
return {type: "form", wrapWithPanel: false, className: "rv-action-form", body, actions: [], onEvent: {submit: {preventDefault: true, actions: [{actionType: "custom", script: `window.RenderiveGallery.runAction(${quote(session.key)},${quote(item.id)},context.data);`}]}}};
}
function buildActionTab(session) {
return [...groupItems(session.actions)].map(([group, items]) => ({type: "panel", className: "rv-control-panel", title: group, body: items.map(item => actionSchema(session, item))}));
@@ -852,6 +875,7 @@ function loop(time) {
session.presentLatest(time);
session.updateMotionStatus(time);
if (session.mode.id !== "low_latency") session.requestFrame(time);
session.sendClientFeedback(time);
}
requestAnimationFrame(loop);
}