220 lines
6.3 KiB
JavaScript
220 lines
6.3 KiB
JavaScript
const DEFAULT_ALLOWED_USER_M_CODES = {
|
|
M428: {
|
|
code: "M428",
|
|
label: "TCP kinematics",
|
|
kinsType: "tcp",
|
|
switchkinsType: 1,
|
|
rtcpState: "on",
|
|
sourceRel: "configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc",
|
|
},
|
|
M429: {
|
|
code: "M429",
|
|
label: "Identity kinematics",
|
|
kinsType: "identity",
|
|
switchkinsType: 0,
|
|
rtcpState: "off",
|
|
sourceRel: "configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/429remap.ngc",
|
|
},
|
|
M430: {
|
|
code: "M430",
|
|
label: "User kinematics",
|
|
kinsType: "userk",
|
|
switchkinsType: 2,
|
|
rtcpState: "on",
|
|
sourceRel: "configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc",
|
|
},
|
|
M128: {
|
|
code: "M128",
|
|
label: "controlled millturn mill-mode user-M",
|
|
kinsType: "mill",
|
|
switchkinsType: 0,
|
|
rtcpState: "off",
|
|
sourceRel: "linuxcnc millturn user-M reference",
|
|
},
|
|
M129: {
|
|
code: "M129",
|
|
label: "controlled millturn turn-mode user-M",
|
|
kinsType: "turn",
|
|
switchkinsType: 1,
|
|
rtcpState: "off",
|
|
sourceRel: "linuxcnc millturn user-M reference",
|
|
},
|
|
};
|
|
|
|
export function createControlledUserMSimulation({ allowedCodes = DEFAULT_ALLOWED_USER_M_CODES } = {}) {
|
|
return {
|
|
apiName: "web-rtcp-5axis-controlled-user-m-simulation",
|
|
ready: true,
|
|
processReady: true,
|
|
processScope: "web_simulation_only",
|
|
hostProcessReady: false,
|
|
hostProcessExecution: false,
|
|
arbitraryUserMExecution: false,
|
|
allowedCodes: Object.fromEntries(
|
|
Object.entries(allowedCodes).map(([code, definition]) => [normalizeMCode(code), {
|
|
...definition,
|
|
code: normalizeMCode(definition.code || code),
|
|
}]),
|
|
),
|
|
events: [],
|
|
blockedEvents: [],
|
|
semanticBoundary: "controlled_user_m_web_simulation_whitelist_not_host_process",
|
|
linuxCncReferences: [
|
|
"linuxcnc/src/emc/rs274ngc",
|
|
"linuxcnc/src/emc/task/emctask.cc",
|
|
"linuxcnc/src/emc/task/emccanon.cc",
|
|
"linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc",
|
|
"linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/429remap.ngc",
|
|
"linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc",
|
|
],
|
|
};
|
|
}
|
|
|
|
export function runControlledUserM(simulation, code, context = {}) {
|
|
const state = cloneSimulation(simulation);
|
|
const normalizedCode = normalizeMCode(code);
|
|
const definition = state.allowedCodes[normalizedCode] || null;
|
|
if (!definition) {
|
|
const event = createUserMEvent({
|
|
code: normalizedCode,
|
|
allowed: false,
|
|
reason: "not_in_controlled_user_m_whitelist",
|
|
context,
|
|
});
|
|
state.blockedEvents.push(event);
|
|
return {
|
|
simulation: state,
|
|
event,
|
|
statePatch: {},
|
|
halPatch: {},
|
|
};
|
|
}
|
|
|
|
const profileKinsType = definition.kinsType === "tcp"
|
|
? tcpKinsTypeForProfile(context.profile)
|
|
: definition.kinsType;
|
|
const halPatch = {
|
|
"motion.switchkins-type": definition.switchkinsType,
|
|
"motion.analog-out-03": definition.switchkinsType,
|
|
};
|
|
const statePatch = {
|
|
kinsType: profileKinsType,
|
|
rtcpState: definition.rtcpState,
|
|
};
|
|
const event = createUserMEvent({
|
|
code: normalizedCode,
|
|
allowed: true,
|
|
definition,
|
|
context,
|
|
halPatch,
|
|
statePatch,
|
|
});
|
|
state.events.push(event);
|
|
return {
|
|
simulation: state,
|
|
event,
|
|
statePatch,
|
|
halPatch,
|
|
};
|
|
}
|
|
|
|
export function createControlledUserMReadiness(simulation) {
|
|
const ready = simulation?.processReady === true && simulation?.processScope === "web_simulation_only";
|
|
return {
|
|
apiName: "web-rtcp-5axis-controlled-user-m-readiness",
|
|
ready,
|
|
externalUserMProcessReady: ready,
|
|
externalUserMProcessScope: ready ? "web_simulation_only" : "not_ready",
|
|
hostExternalUserMProcessReady: false,
|
|
hostProcessExecution: false,
|
|
arbitraryUserMExecution: false,
|
|
allowedCodeCount: Object.keys(simulation?.allowedCodes || {}).length,
|
|
blockedEventCount: simulation?.blockedEvents?.length || 0,
|
|
semanticBoundary: "external_user_m_process_ready_for_web_simulation_only",
|
|
};
|
|
}
|
|
|
|
export function extractControlledUserMCodesFromProgram(programText = "") {
|
|
const matches = [];
|
|
const lines = String(programText).split(/\r?\n/);
|
|
lines.forEach((line, index) => {
|
|
const stripped = line.replace(/\([^)]*\)/g, " ");
|
|
for (const match of stripped.matchAll(/\bM\s*(\d{2,3})\b/gi)) {
|
|
const code = normalizeMCode(`M${match[1]}`);
|
|
if (Number(match[1]) >= 100 || ["M428", "M429", "M430"].includes(code)) {
|
|
matches.push({
|
|
code,
|
|
line: index + 1,
|
|
rawLine: line,
|
|
});
|
|
}
|
|
}
|
|
});
|
|
return matches;
|
|
}
|
|
|
|
export function runControlledUserMProgramScan(simulation, programText = "", context = {}) {
|
|
let state = simulation;
|
|
const events = [];
|
|
for (const occurrence of extractControlledUserMCodesFromProgram(programText)) {
|
|
const result = runControlledUserM(state, occurrence.code, {
|
|
...context,
|
|
line: occurrence.line,
|
|
rawLine: occurrence.rawLine,
|
|
});
|
|
state = result.simulation;
|
|
events.push(result.event);
|
|
}
|
|
return { simulation: state, events };
|
|
}
|
|
|
|
function createUserMEvent({
|
|
code,
|
|
allowed,
|
|
definition = null,
|
|
context = {},
|
|
halPatch = {},
|
|
statePatch = {},
|
|
reason = null,
|
|
}) {
|
|
return {
|
|
apiName: "web-rtcp-5axis-controlled-user-m-event",
|
|
code,
|
|
allowed,
|
|
reason,
|
|
label: definition?.label || null,
|
|
sourceRel: definition?.sourceRel || context.sourceRel || null,
|
|
line: context.line || null,
|
|
halPatch,
|
|
statePatch,
|
|
createdAt: new Date().toISOString(),
|
|
semanticBoundary: allowed
|
|
? "vendored_or_whitelisted_user_m_web_simulation_event"
|
|
: "arbitrary_external_user_m_blocked",
|
|
promotionScope: "web_simulation_only",
|
|
hostProcessExecution: false,
|
|
arbitraryUserMExecution: false,
|
|
};
|
|
}
|
|
|
|
function tcpKinsTypeForProfile(profile) {
|
|
if (profile?.id === "xyzbc-trt") return "tcp-xyzbc";
|
|
return "tcp-xyzac";
|
|
}
|
|
|
|
function normalizeMCode(code) {
|
|
const normalized = String(code || "").trim().toUpperCase().replace(/\s+/g, "");
|
|
const match = normalized.match(/^M0*(\d+)$/);
|
|
if (!match) return normalized;
|
|
return `M${Number(match[1])}`;
|
|
}
|
|
|
|
function cloneSimulation(simulation) {
|
|
return {
|
|
...simulation,
|
|
allowedCodes: { ...(simulation.allowedCodes || {}) },
|
|
events: [...(simulation.events || [])],
|
|
blockedEvents: [...(simulation.blockedEvents || [])],
|
|
};
|
|
}
|