|
|
|
|
@@ -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";
|
|
|
|
|
}
|
|
|
|
|
|