80 lines
3.5 KiB
JavaScript
80 lines
3.5 KiB
JavaScript
import {
|
|
clearPowerOffCheckpoint,
|
|
clearUiState,
|
|
loadPowerOffCheckpoint,
|
|
loadUiState,
|
|
savePowerOffCheckpoint,
|
|
saveUiState,
|
|
} from "./opfs-ui-state.mjs";
|
|
import { DEFAULT_MACHINE, machineDefinition } from "../workspace/machine-definitions.mjs";
|
|
|
|
const UI_COMMANDS = new Set(["UI_STATE_SAVE", "UI_STATE_LOAD", "UI_STATE_CLEAR"]);
|
|
|
|
function object(value) {
|
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
}
|
|
|
|
function resultEnvelope(input, payload) {
|
|
return {
|
|
schema_version: 1,
|
|
kind: "UI_STATE_RESULT",
|
|
context_id: input.context_id,
|
|
session_id: input.session_id,
|
|
seq: input.seq,
|
|
operation_id: input.operation_id ?? null,
|
|
payload: { ...payload, command: input.kind, operation_id: input.operation_id ?? null },
|
|
};
|
|
}
|
|
|
|
export function isUiStateCommand(input) {
|
|
return UI_COMMANDS.has(input?.kind);
|
|
}
|
|
|
|
export async function handleWorkerMessage(session, input) {
|
|
if (isUiStateCommand(input)) {
|
|
// UI cache is a Worker storage service, not a LinuxCNC command. It never
|
|
// consumes the WASM protocol sequence or changes task/motion authority.
|
|
const payload = object(input.payload);
|
|
const options = { contextId: input.context_id, key: payload.key ?? session.machineDefinition?.id ?? DEFAULT_MACHINE.id };
|
|
const result = input.kind === "UI_STATE_SAVE"
|
|
? await saveUiState({ ...options, state: payload.state })
|
|
: input.kind === "UI_STATE_LOAD"
|
|
? await loadUiState(options)
|
|
: await clearUiState(options);
|
|
return resultEnvelope(input, result);
|
|
}
|
|
|
|
let request = input;
|
|
let recoveryNotice = null;
|
|
const configuration = object(input.payload).configuration;
|
|
const requestedMachine = machineDefinition(configuration?.entrypoint);
|
|
const key = requestedMachine?.id ?? session.machineDefinition?.id ?? DEFAULT_MACHINE.id;
|
|
// Automatic restoration belongs to a new Worker session. Explicit INI
|
|
// changes and machine switches must not be overwritten by saved state.
|
|
if (input?.kind === "LOAD_WORKSPACE" && !session.machineDefinition && (!configuration?.entrypoint || requestedMachine) && !object(input.payload).recovery_checkpoint) {
|
|
const persisted = await loadPowerOffCheckpoint({ contextId: input.context_id, key });
|
|
if (persisted.status === "verified" && persisted.checkpoint) {
|
|
try {
|
|
if (configuration && session.core && typeof session.validateRecoveryGeometry === "function") session.validateRecoveryGeometry(persisted.checkpoint.state, configuration);
|
|
request = { ...input, payload: { ...object(input.payload), recovery_checkpoint: persisted.checkpoint } };
|
|
} catch (error) {
|
|
recoveryNotice = { status: "ignored", code: String(error?.message ?? error), reason: "saved_checkpoint_incompatible_with_current_configuration" };
|
|
}
|
|
}
|
|
}
|
|
const response = session.accept(request);
|
|
const payload = object(response?.payload);
|
|
if (recoveryNotice) payload.recovery_persistence = recoveryNotice;
|
|
if (input?.kind === "POWER" && response?.kind === "COMMAND_RESULT" && payload.status === "verified") {
|
|
if (payload.active === false && payload.recovery_checkpoint) {
|
|
const persisted = await savePowerOffCheckpoint({ contextId: input.context_id, checkpoint: payload.recovery_checkpoint, key });
|
|
payload.recovery_persistence = persisted;
|
|
} else if (payload.active === true) {
|
|
payload.recovery_persistence = await clearPowerOffCheckpoint({ contextId: input.context_id, key });
|
|
}
|
|
}
|
|
return response;
|
|
}
|
|
|
|
export { UI_COMMANDS };
|