接续上一轮,先做 L4-USER-M-PROCESS,完全接入仿真系统
结论:已将 L4-USER-M-PROCESS 的 millturn M128/M129 受控状态转换接入 virtual HAL、真实 browser simulation、OPFS 会话 payload、SDK API 和 release diagnostics validation;外部 user-M process execution 与 promotion 仍保持 locked,不伪装为 native process 解锁。
This commit is contained in:
@@ -32,6 +32,84 @@ export const VIRTUAL_HAL_SOURCE_FILES = Object.freeze({
|
||||
vendoredMotionHeader: "wasm-port/vendor/linuxcnc/src/emc/motion/motion.h",
|
||||
});
|
||||
|
||||
export const VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY = Object.freeze({
|
||||
id: "millturn-user-m-process",
|
||||
label: "Millturn M128/M129 user-M process boundary",
|
||||
boundaryClass: "L4-USER-M-PROCESS",
|
||||
path: "axis/vismach/millturn/example.ngc",
|
||||
ini: "axis/vismach/millturn/millturn.ini",
|
||||
requiredRuntime: "linuxcnc_task_hal_tcl_user_m_process",
|
||||
sourceFiles: Object.freeze([
|
||||
"linuxcnc/configs/sim/axis/vismach/millturn/millturn.ini",
|
||||
"linuxcnc/configs/sim/axis/vismach/millturn/example.ngc",
|
||||
"linuxcnc/configs/sim/axis/vismach/millturn/mcodes/M128",
|
||||
"linuxcnc/configs/sim/axis/vismach/millturn/mcodes/M129",
|
||||
"linuxcnc/configs/sim/axis/vismach/millturn/remap_subs/428remap.ngc",
|
||||
"linuxcnc/configs/sim/axis/vismach/millturn/remap_subs/429remap.ngc",
|
||||
]),
|
||||
manifestSourceFiles: Object.freeze([
|
||||
"linuxcnc/configs/sim/axis/vismach/millturn/millturn.ini",
|
||||
"linuxcnc/configs/sim/axis/vismach/millturn/example.ngc",
|
||||
"linuxcnc/configs/sim/axis/vismach/millturn/remap_subs/428remap.ngc",
|
||||
"linuxcnc/configs/sim/axis/vismach/millturn/remap_subs/429remap.ngc",
|
||||
]),
|
||||
nativeProbe: "ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh",
|
||||
cases: Object.freeze([
|
||||
Object.freeze({
|
||||
userMCode: "M128",
|
||||
remapCode: "M428",
|
||||
stateMode: "mill",
|
||||
guardPin: "kinstype.is-0",
|
||||
switchkinsOutputPin: "motion.analog-out-03",
|
||||
switchkinsTarget: 0,
|
||||
activeG5x: "G59.1",
|
||||
workOffsetPocket: "P7",
|
||||
workOffsetWords: "X-290 Y0 Z-160 A0",
|
||||
sourceField: "MIN_LIMIT/MAX_LIMIT",
|
||||
stateTargets: Object.freeze([
|
||||
Object.freeze({ pin: "ini.x.min_limit", value: -300, sourceSection: "AXIS_X", sourceField: "MIN_LIMIT" }),
|
||||
Object.freeze({ pin: "ini.x.max_limit", value: 300, sourceSection: "AXIS_X", sourceField: "MAX_LIMIT" }),
|
||||
Object.freeze({ pin: "ini.x.min_velocity", value: 60.0, sourceSection: "AXIS_X", sourceField: "MAX_VELOCITY" }),
|
||||
Object.freeze({ pin: "ini.x.max_acceleration", value: 400.0, sourceSection: "AXIS_X", sourceField: "MAX_ACCELERATION" }),
|
||||
Object.freeze({ pin: "ini.y.min_limit", value: -100, sourceSection: "AXIS_Y", sourceField: "MIN_LIMIT" }),
|
||||
Object.freeze({ pin: "ini.y.max_limit", value: 100, sourceSection: "AXIS_Y", sourceField: "MAX_LIMIT" }),
|
||||
Object.freeze({ pin: "ini.y.min_velocity", value: 60.0, sourceSection: "AXIS_Y", sourceField: "MAX_VELOCITY" }),
|
||||
Object.freeze({ pin: "ini.y.max_acceleration", value: 400.0, sourceSection: "AXIS_Y", sourceField: "MAX_ACCELERATION" }),
|
||||
Object.freeze({ pin: "ini.z.min_limit", value: -240, sourceSection: "AXIS_Z", sourceField: "MIN_LIMIT" }),
|
||||
Object.freeze({ pin: "ini.z.max_limit", value: 0, sourceSection: "AXIS_Z", sourceField: "MAX_LIMIT" }),
|
||||
Object.freeze({ pin: "ini.z.min_velocity", value: 60.0, sourceSection: "AXIS_Z", sourceField: "MAX_VELOCITY" }),
|
||||
Object.freeze({ pin: "ini.z.max_acceleration", value: 400.0, sourceSection: "AXIS_Z", sourceField: "MAX_ACCELERATION" }),
|
||||
]),
|
||||
}),
|
||||
Object.freeze({
|
||||
userMCode: "M129",
|
||||
remapCode: "M429",
|
||||
stateMode: "turn",
|
||||
guardPin: "kinstype.is-1",
|
||||
switchkinsOutputPin: "motion.analog-out-03",
|
||||
switchkinsTarget: 1,
|
||||
activeG5x: "G59.2",
|
||||
workOffsetPocket: "P8",
|
||||
workOffsetWords: "X-160 Y0 Z-290 A0",
|
||||
sourceField: "MIN_LIMIT_TURN/MAX_LIMIT_TURN",
|
||||
stateTargets: Object.freeze([
|
||||
Object.freeze({ pin: "ini.x.min_limit", value: -240, sourceSection: "AXIS_X", sourceField: "MIN_LIMIT_TURN" }),
|
||||
Object.freeze({ pin: "ini.x.max_limit", value: 0, sourceSection: "AXIS_X", sourceField: "MAX_LIMIT_TURN" }),
|
||||
Object.freeze({ pin: "ini.x.min_velocity", value: 60.0, sourceSection: "AXIS_X", sourceField: "MAX_VELOCITY" }),
|
||||
Object.freeze({ pin: "ini.x.max_acceleration", value: 400.0, sourceSection: "AXIS_X", sourceField: "MAX_ACCELERATION" }),
|
||||
Object.freeze({ pin: "ini.y.min_limit", value: -100, sourceSection: "AXIS_Y", sourceField: "MIN_LIMIT_TURN" }),
|
||||
Object.freeze({ pin: "ini.y.max_limit", value: 100, sourceSection: "AXIS_Y", sourceField: "MAX_LIMIT_TURN" }),
|
||||
Object.freeze({ pin: "ini.y.min_velocity", value: 60.0, sourceSection: "AXIS_Y", sourceField: "MAX_VELOCITY" }),
|
||||
Object.freeze({ pin: "ini.y.max_acceleration", value: 400.0, sourceSection: "AXIS_Y", sourceField: "MAX_ACCELERATION" }),
|
||||
Object.freeze({ pin: "ini.z.min_limit", value: -300, sourceSection: "AXIS_Z", sourceField: "MIN_LIMIT_TURN" }),
|
||||
Object.freeze({ pin: "ini.z.max_limit", value: 300, sourceSection: "AXIS_Z", sourceField: "MAX_LIMIT_TURN" }),
|
||||
Object.freeze({ pin: "ini.z.min_velocity", value: 60.0, sourceSection: "AXIS_Z", sourceField: "MAX_VELOCITY" }),
|
||||
Object.freeze({ pin: "ini.z.max_acceleration", value: 400.0, sourceSection: "AXIS_Z", sourceField: "MAX_ACCELERATION" }),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
});
|
||||
|
||||
export const VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS = Object.freeze([
|
||||
Object.freeze({
|
||||
id: "axis-foam",
|
||||
@@ -740,6 +818,31 @@ export const VIRTUAL_HAL_SYSTEM_PIN_FAMILIES = Object.freeze([
|
||||
{ name: "motion.switchkins-type", type: "HAL_FLOAT", direction: "HAL_IN", conditional: "switchable kinematics" },
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: "millturn-user-m-process",
|
||||
component: "millturn",
|
||||
label: "Millturn user-M process boundary pins",
|
||||
sourceFiles: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.sourceFiles,
|
||||
sourceEvidence: "LinuxCNC millturn M128/M129 Tcl scripts and remap callers set switchkins guard state and ini.[xyz] HAL pins.",
|
||||
coverage: VIRTUAL_HAL_COVERAGE_STATES.runtimeBoundary,
|
||||
pins: Object.freeze([
|
||||
{ name: "motion.analog-out-03", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
{ name: "kinstype.is-0", type: "HAL_BIT", direction: "HAL_OUT" },
|
||||
{ name: "kinstype.is-1", type: "HAL_BIT", direction: "HAL_OUT" },
|
||||
{ name: "ini.x.min_limit", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
{ name: "ini.x.max_limit", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
{ name: "ini.x.min_velocity", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
{ name: "ini.x.max_acceleration", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
{ name: "ini.y.min_limit", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
{ name: "ini.y.max_limit", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
{ name: "ini.y.min_velocity", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
{ name: "ini.y.max_acceleration", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
{ name: "ini.z.min_limit", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
{ name: "ini.z.max_limit", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
{ name: "ini.z.min_velocity", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
{ name: "ini.z.max_acceleration", type: "HAL_FLOAT", direction: "HAL_OUT" },
|
||||
]),
|
||||
},
|
||||
{
|
||||
id: "axis-motion",
|
||||
component: "axis",
|
||||
@@ -866,6 +969,11 @@ export const VIRTUAL_HAL_PROJECT_PIN_GROUPS = Object.freeze([
|
||||
label: "Tool pins",
|
||||
description: "Tool status, tool change, and motion tool-offset pins from HALUI, iocontrol, and motion.",
|
||||
},
|
||||
{
|
||||
id: "user-m-process",
|
||||
label: "User-M process boundary pins",
|
||||
description: "Controlled millturn M128/M129 state pins sourced from LinuxCNC Tcl user-M scripts and remap callers.",
|
||||
},
|
||||
]);
|
||||
|
||||
export const VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES = Object.freeze([
|
||||
@@ -879,6 +987,7 @@ export const VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES = Object.freeze([
|
||||
"axis-joint-position-feedback",
|
||||
"spindle-coolant-tool-status",
|
||||
"interpreter-hal-bridge-snapshot",
|
||||
"controlled-user-m-process-state-boundary",
|
||||
]);
|
||||
|
||||
export const VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS = Object.freeze([
|
||||
@@ -887,6 +996,24 @@ export const VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS = Object.freeze([
|
||||
"motion-controller",
|
||||
]);
|
||||
|
||||
const VIRTUAL_HAL_DEFAULT_MILLTURN_USER_M_EXTERNAL_PINS = Object.freeze({
|
||||
"motion.analog-out-03": Object.freeze({ kind: "pin", name: "motion.analog-out-03", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
"kinstype.is-0": Object.freeze({ kind: "pin", name: "kinstype.is-0", type: "HAL_BIT", value: 0, connected: true }),
|
||||
"kinstype.is-1": Object.freeze({ kind: "pin", name: "kinstype.is-1", type: "HAL_BIT", value: 0, connected: true }),
|
||||
"ini.x.min_limit": Object.freeze({ kind: "pin", name: "ini.x.min_limit", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
"ini.x.max_limit": Object.freeze({ kind: "pin", name: "ini.x.max_limit", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
"ini.x.min_velocity": Object.freeze({ kind: "pin", name: "ini.x.min_velocity", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
"ini.x.max_acceleration": Object.freeze({ kind: "pin", name: "ini.x.max_acceleration", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
"ini.y.min_limit": Object.freeze({ kind: "pin", name: "ini.y.min_limit", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
"ini.y.max_limit": Object.freeze({ kind: "pin", name: "ini.y.max_limit", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
"ini.y.min_velocity": Object.freeze({ kind: "pin", name: "ini.y.min_velocity", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
"ini.y.max_acceleration": Object.freeze({ kind: "pin", name: "ini.y.max_acceleration", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
"ini.z.min_limit": Object.freeze({ kind: "pin", name: "ini.z.min_limit", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
"ini.z.max_limit": Object.freeze({ kind: "pin", name: "ini.z.max_limit", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
"ini.z.min_velocity": Object.freeze({ kind: "pin", name: "ini.z.min_velocity", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
"ini.z.max_acceleration": Object.freeze({ kind: "pin", name: "ini.z.max_acceleration", type: "HAL_FLOAT", value: 0, connected: true }),
|
||||
});
|
||||
|
||||
export const VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES = Object.freeze([
|
||||
Object.freeze({
|
||||
id: "pin-signal-net-show",
|
||||
@@ -1130,6 +1257,11 @@ export const VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES = Object.freeze({
|
||||
]),
|
||||
evidence: "Bridge snapshots only serialize source-derived virtual HAL pins and values into the LinuxCNC-backed interpreter HAL adapter.",
|
||||
}),
|
||||
"controlled-user-m-process-state-boundary": Object.freeze({
|
||||
label: "Controlled user-M process state boundary",
|
||||
sourceFiles: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.sourceFiles,
|
||||
evidence: "Millturn M128/M129 simulation exposes LinuxCNC source-derived Tcl/HAL state targets while keeping external process execution and promotion locked.",
|
||||
}),
|
||||
});
|
||||
|
||||
export function createVirtualHalState(overrides = {}) {
|
||||
@@ -1150,7 +1282,7 @@ export function createVirtualHalState(overrides = {}) {
|
||||
mode: "manual",
|
||||
},
|
||||
pins: Object.fromEntries(VIRTUAL_HAL_AXISUI_PINS.pins.map((pin) => [pin.name, pin.type === "HAL_FLOAT" ? 0 : false])),
|
||||
externalPins: {},
|
||||
externalPins: Object.fromEntries(Object.entries(VIRTUAL_HAL_DEFAULT_MILLTURN_USER_M_EXTERNAL_PINS).map(([name, entry]) => [name, { ...entry }])),
|
||||
position: Object.fromEntries(VIRTUAL_HAL_AXES.map((axis) => [axis, 0])),
|
||||
homed: Object.fromEntries(VIRTUAL_HAL_AXES.map((axis) => [axis, false])),
|
||||
limits: Object.fromEntries(VIRTUAL_HAL_AXES.map((axis) => [
|
||||
@@ -2210,6 +2342,12 @@ export function createVirtualHalBridgeReadiness(options = {}) {
|
||||
"halui.estop.is-activated",
|
||||
"spindle.0.speed-out",
|
||||
"iocontrol.0.coolant-flood",
|
||||
"motion.switchkins-type",
|
||||
"motion.analog-out-03",
|
||||
"kinstype.is-0",
|
||||
"kinstype.is-1",
|
||||
"ini.x.min_limit",
|
||||
"ini.z.max_limit",
|
||||
];
|
||||
const snapshotPins = new Set((snapshot.values ?? []).map(({ name }) => name));
|
||||
const missingPins = requiredPins.filter((name) => !snapshotPins.has(name));
|
||||
@@ -2337,6 +2475,7 @@ export function createVirtualHalProjectReport(options = {}) {
|
||||
registry: pinRegistry,
|
||||
systemCoverage,
|
||||
});
|
||||
const userMProcessBoundary = createVirtualHalMillturnUserMProcessBoundaryReport(options);
|
||||
return {
|
||||
apiName: "linuxcnc-wasm-virtual-hal-project-report",
|
||||
reportVersion: 1,
|
||||
@@ -2353,6 +2492,7 @@ export function createVirtualHalProjectReport(options = {}) {
|
||||
integrity,
|
||||
systemCoverage,
|
||||
simulationRuntime: createVirtualHalSimulationRuntimeReport(state),
|
||||
userMProcessBoundary,
|
||||
wasmBridgeSnapshot: snapshot,
|
||||
bridgeReadiness: readiness,
|
||||
bridgeActionPlan: actionPlan,
|
||||
@@ -2387,6 +2527,11 @@ export function createVirtualHalProjectReport(options = {}) {
|
||||
label: "Simulation HAL runtime",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "user-m-process-boundary",
|
||||
label: "User-M process boundary",
|
||||
value: userMProcessBoundary.phase,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -3407,6 +3552,177 @@ export function createVirtualHalSimConfigMacroLoadFixtureReport(options = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
function virtualHalMillturnUserMCaseForCode(code) {
|
||||
const normalized = String(code ?? "").trim().toUpperCase();
|
||||
return VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.cases
|
||||
.find((entry) => entry.userMCode === normalized || entry.remapCode === normalized) ?? null;
|
||||
}
|
||||
|
||||
function createMillturnUserMStateTargetRows(boundaryCase) {
|
||||
return boundaryCase.stateTargets.map((target) => ({
|
||||
path: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.path,
|
||||
ini: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.ini,
|
||||
blocked: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.boundaryClass,
|
||||
userMCode: boundaryCase.userMCode,
|
||||
targetPin: target.pin,
|
||||
sourceSection: target.sourceSection,
|
||||
sourceField: target.sourceField,
|
||||
expectedValue: target.value,
|
||||
executionEnabled: false,
|
||||
promotionAllowed: false,
|
||||
proofStatus: "pending_native_runtime_probe",
|
||||
notes: "source_derived_virtual_hal_state_target_no_external_process_execution",
|
||||
}));
|
||||
}
|
||||
|
||||
export function createVirtualHalMillturnUserMProcessBoundaryReport(options = {}) {
|
||||
const boundary = VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY;
|
||||
const manifestProvided = typeof options.manifestText === "string" || Array.isArray(options.manifestEntries);
|
||||
const manifestSet = new Set([
|
||||
...sourceManifestSetFromText(options.manifestText ?? ""),
|
||||
...(options.manifestEntries ?? []).map(normalizeLinuxCncSourcePath),
|
||||
]);
|
||||
const sourceFiles = [...boundary.sourceFiles];
|
||||
const manifestSourceFiles = [...boundary.manifestSourceFiles];
|
||||
const missingManifestFiles = manifestProvided
|
||||
? manifestSourceFiles.filter((file) => !manifestSet.has(normalizeLinuxCncSourcePath(file)))
|
||||
: [];
|
||||
const rows = boundary.cases.map((entry) => {
|
||||
const stateTargetRows = createMillturnUserMStateTargetRows(entry);
|
||||
return {
|
||||
path: boundary.path,
|
||||
ini: boundary.ini,
|
||||
blocked: boundary.boundaryClass,
|
||||
remapCode: entry.remapCode,
|
||||
userMCode: entry.userMCode,
|
||||
stateMode: entry.stateMode,
|
||||
guardPin: entry.guardPin,
|
||||
switchkinsOutputPin: entry.switchkinsOutputPin,
|
||||
switchkinsTarget: entry.switchkinsTarget,
|
||||
activeG5x: entry.activeG5x,
|
||||
workOffsetPocket: entry.workOffsetPocket,
|
||||
workOffsetWords: entry.workOffsetWords,
|
||||
requiredRuntime: boundary.requiredRuntime,
|
||||
expectedStatePins: stateTargetRows.map((row) => row.targetPin),
|
||||
expectedStateValues: stateTargetRows.map((row) => `${row.targetPin}=${row.expectedValue}`),
|
||||
stateTargetCount: stateTargetRows.length,
|
||||
stateTargetRows,
|
||||
sourceFiles,
|
||||
manifestSourceFiles,
|
||||
sourceDerived: sourceFiles.length > 0,
|
||||
executionEnabled: false,
|
||||
promotionAllowed: false,
|
||||
proofStatus: "pending_native_runtime_probe",
|
||||
nativeProbe: boundary.nativeProbe,
|
||||
complete: sourceFiles.length > 0 && missingManifestFiles.length === 0,
|
||||
notes: "controlled_virtual_hal_state_boundary_keep_user_m_process_blocked",
|
||||
};
|
||||
});
|
||||
const complete = rows.length > 0 &&
|
||||
rows.every((row) => row.complete) &&
|
||||
missingManifestFiles.length === 0;
|
||||
return {
|
||||
apiName: "linuxcnc-wasm-virtual-hal-millturn-user-m-process-boundary-report",
|
||||
reportVersion: 1,
|
||||
source: "linuxcnc-configs-sim-source-derived-user-m-process-boundary",
|
||||
phase: complete ? "simulation-boundary-ready" : "blocked",
|
||||
complete,
|
||||
webSimulationSatisfied: complete,
|
||||
boundaryClass: boundary.boundaryClass,
|
||||
path: boundary.path,
|
||||
ini: boundary.ini,
|
||||
requiredRuntime: boundary.requiredRuntime,
|
||||
nativeProbe: boundary.nativeProbe,
|
||||
nativeRuntimeRequired: true,
|
||||
processExecutionReady: false,
|
||||
executionEnabled: false,
|
||||
promotionAllowed: false,
|
||||
inventoryBaselineUnchanged: true,
|
||||
jsCncSemantics: false,
|
||||
manifestChecked: manifestProvided,
|
||||
missingManifestFiles,
|
||||
sourceFiles,
|
||||
manifestSourceFiles,
|
||||
caseCount: rows.length,
|
||||
stateTargetCount: rows.reduce((sum, row) => sum + row.stateTargetCount, 0),
|
||||
rows,
|
||||
summaryRows: [
|
||||
{ id: "boundary", label: "User-M boundary", value: boundary.boundaryClass },
|
||||
{ id: "cases", label: "M-code cases", value: rows.map((row) => `${row.remapCode}->${row.userMCode}`).join(", ") },
|
||||
{ id: "state-targets", label: "HAL state targets", value: `${rows.reduce((sum, row) => sum + row.stateTargetCount, 0)}` },
|
||||
{ id: "execution", label: "External process execution", value: "locked" },
|
||||
{ id: "promotion", label: "Promotion allowed", value: "0" },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function applyVirtualHalMillturnUserMProcessState(halState = createVirtualHalState(), codeOrOptions = "M128", options = {}) {
|
||||
const code = typeof codeOrOptions === "object" && codeOrOptions !== null
|
||||
? codeOrOptions.code ?? codeOrOptions.userMCode ?? codeOrOptions.remapCode
|
||||
: codeOrOptions;
|
||||
const boundaryCase = virtualHalMillturnUserMCaseForCode(code);
|
||||
if (!boundaryCase) {
|
||||
throw new Error(`unknown millturn user-M process code: ${code}`);
|
||||
}
|
||||
const report = createVirtualHalMillturnUserMProcessBoundaryReport(options);
|
||||
const row = report.rows.find((entry) => entry.userMCode === boundaryCase.userMCode);
|
||||
if (!row?.complete) {
|
||||
throw new Error(`millturn user-M process boundary is not source-complete for ${boundaryCase.userMCode}`);
|
||||
}
|
||||
|
||||
const guardUpdates = VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.cases.map((entry) => ({
|
||||
name: entry.guardPin,
|
||||
type: "HAL_BIT",
|
||||
value: entry.userMCode === boundaryCase.userMCode ? 1 : 0,
|
||||
}));
|
||||
const updates = [
|
||||
{ name: "motion.switchkins-type", type: "HAL_FLOAT", value: boundaryCase.switchkinsTarget },
|
||||
{ name: boundaryCase.switchkinsOutputPin, type: "HAL_FLOAT", value: boundaryCase.switchkinsTarget },
|
||||
...guardUpdates,
|
||||
...boundaryCase.stateTargets.map((target) => ({
|
||||
name: target.pin,
|
||||
type: "HAL_FLOAT",
|
||||
value: target.value,
|
||||
})),
|
||||
];
|
||||
const next = applyVirtualHalPinUpdates(halState, updates, { allowExternal: true });
|
||||
next.hal.commandLog = [
|
||||
...(next.hal.commandLog ?? []),
|
||||
{
|
||||
source: "virtual-user-m-process-boundary",
|
||||
action: "millturn-user-m-state",
|
||||
userMCode: boundaryCase.userMCode,
|
||||
remapCode: boundaryCase.remapCode,
|
||||
stateMode: boundaryCase.stateMode,
|
||||
executionEnabled: false,
|
||||
promotionAllowed: false,
|
||||
ok: true,
|
||||
},
|
||||
].slice(-200);
|
||||
next.revision += 1;
|
||||
return {
|
||||
apiName: "linuxcnc-wasm-virtual-hal-millturn-user-m-process-state-result",
|
||||
resultVersion: 1,
|
||||
source: "linuxcnc-configs-sim-source-derived-user-m-process-state",
|
||||
ok: true,
|
||||
boundaryClass: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.boundaryClass,
|
||||
path: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.path,
|
||||
ini: VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.ini,
|
||||
userMCode: boundaryCase.userMCode,
|
||||
remapCode: boundaryCase.remapCode,
|
||||
stateMode: boundaryCase.stateMode,
|
||||
switchkinsTarget: boundaryCase.switchkinsTarget,
|
||||
activeG5x: boundaryCase.activeG5x,
|
||||
workOffsetPocket: boundaryCase.workOffsetPocket,
|
||||
appliedPins: updates.map((update) => update.name),
|
||||
executionEnabled: false,
|
||||
promotionAllowed: false,
|
||||
processExecutionReady: false,
|
||||
state: normalizeVirtualHalState(next),
|
||||
report,
|
||||
};
|
||||
}
|
||||
|
||||
export function createVirtualHalSourceComplianceReport(options = {}) {
|
||||
const halState = options.halState ?? createVirtualHalState();
|
||||
const snapshot = options.snapshot ?? createVirtualHalWasmBridgeSnapshot(halState);
|
||||
@@ -3422,6 +3738,7 @@ export function createVirtualHalSourceComplianceReport(options = {}) {
|
||||
...VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles),
|
||||
...VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES.flatMap((fixture) => fixture.sourceFiles),
|
||||
...VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES.flatMap((fixture) => fixture.sourceFiles),
|
||||
...VIRTUAL_HAL_MILLTURN_USER_M_PROCESS_BOUNDARY.sourceFiles,
|
||||
...(options.allowedSourceFiles ?? []),
|
||||
]);
|
||||
const requiredCapabilities = options.requiredCapabilities ?? VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES;
|
||||
@@ -3629,6 +3946,14 @@ export function createLinuxCncVirtualHalRuntime(initialState = createVirtualHalS
|
||||
getSimConfigMacroLoadFixtureReport(options = {}) {
|
||||
return createVirtualHalSimConfigMacroLoadFixtureReport(options);
|
||||
},
|
||||
getMillturnUserMProcessBoundaryReport(options = {}) {
|
||||
return createVirtualHalMillturnUserMProcessBoundaryReport(options);
|
||||
},
|
||||
applyMillturnUserMProcessState(codeOrOptions = "M128", options = {}) {
|
||||
const result = applyVirtualHalMillturnUserMProcessState(state, codeOrOptions, options);
|
||||
state = result.state;
|
||||
return result;
|
||||
},
|
||||
getCommandScriptFixtureReport(options = {}) {
|
||||
return createVirtualHalCommandScriptFixtureReport({ ...options, halState: state });
|
||||
},
|
||||
@@ -3826,6 +4151,9 @@ function isVirtualHalPinWritable(name) {
|
||||
if (/^(axis|joint)\./.test(name) || name.startsWith("spindle.")) {
|
||||
return true;
|
||||
}
|
||||
if (name.startsWith("ini.") || name.startsWith("kinstype.")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4014,6 +4342,9 @@ function applyAxisPinWrite(state, name, numeric, enabled) {
|
||||
}
|
||||
|
||||
function classifyVirtualHalPinGroup(name) {
|
||||
if (name.startsWith("ini.") || name.startsWith("kinstype.") || name === "motion.analog-out-03") {
|
||||
return "user-m-process";
|
||||
}
|
||||
if (name.startsWith("axisui.") || name.startsWith("jog.") || name.startsWith("notifications-") || name === "error" || name === "abort") {
|
||||
return "axisui";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user