Complete sim config boundary coverage
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
export { createLinuxCncIniSdk } from "./linuxcnc-ini.js";
|
||||
export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js";
|
||||
export {
|
||||
analyzeIniRuntimeBoundaries,
|
||||
planIniFileContextStaging,
|
||||
planSimConfigStaging,
|
||||
} from "./sim-config-staging.js";
|
||||
|
||||
@@ -172,6 +172,197 @@ function remapNgcNames(iniValues) {
|
||||
return names;
|
||||
}
|
||||
|
||||
function sectionValues(iniValues, section) {
|
||||
const prefix = `${section.toUpperCase()}.`;
|
||||
const values = [];
|
||||
for (const [key, entries] of iniValues.entries()) {
|
||||
if (!key.startsWith(prefix)) {
|
||||
continue;
|
||||
}
|
||||
for (const value of entries) {
|
||||
values.push({ key: key.slice(prefix.length), value });
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
function valuesMatching(iniValues, section, keys) {
|
||||
const wanted = new Set(keys.map((key) => key.toUpperCase()));
|
||||
return sectionValues(iniValues, section)
|
||||
.filter((entry) => wanted.has(entry.key))
|
||||
.map((entry) => entry.value);
|
||||
}
|
||||
|
||||
function looksLikePythonReference(value) {
|
||||
return /(^|\s|=|:)["']?[^"'\s]*\.py(["'\s]|$)/i.test(value);
|
||||
}
|
||||
|
||||
function userMCodesInText(text) {
|
||||
const codes = new Set();
|
||||
for (const match of text.matchAll(/(?<![A-Za-z0-9_])M\s*(1\d\d)(?![0-9])/gi)) {
|
||||
codes.add(`M${match[1]}`);
|
||||
}
|
||||
return [...codes].sort();
|
||||
}
|
||||
|
||||
function vendoredUserMCodes({
|
||||
manifestEntries,
|
||||
sourceDir,
|
||||
normalizedSearchRoot,
|
||||
userMPathValues,
|
||||
}) {
|
||||
const manifestSet = new Set(manifestEntries);
|
||||
const executableCodes = new Set();
|
||||
for (const dirEntry of userMPathValues.flatMap(splitSearchPath)) {
|
||||
if (dirEntry.startsWith("/")) {
|
||||
continue;
|
||||
}
|
||||
const sourceUserMDir = normalizeRel(`${sourceDir}/${dirEntry}`);
|
||||
const prefix = sourceUserMDir ? `${sourceUserMDir}/` : "";
|
||||
for (const sourceRel of manifestEntries) {
|
||||
if (
|
||||
sourceRel.startsWith(prefix) &&
|
||||
!sourceRel.slice(prefix.length).includes("/") &&
|
||||
isUserMCodePath(sourceRel)
|
||||
) {
|
||||
executableCodes.add(basename(sourceRel).toUpperCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const dirEntry of userMPathValues.flatMap(splitSearchPath)) {
|
||||
if (dirEntry.startsWith("/")) {
|
||||
continue;
|
||||
}
|
||||
const sourceUserMDir = normalizeRel(`${sourceDir}/${dirEntry}`);
|
||||
for (let code = 100; code <= 199; code += 1) {
|
||||
const candidate = findUpwardByBasename(
|
||||
manifestSet,
|
||||
sourceUserMDir,
|
||||
`M${code}`,
|
||||
normalizedSearchRoot,
|
||||
);
|
||||
if (candidate) {
|
||||
executableCodes.add(`M${code}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...executableCodes].sort();
|
||||
}
|
||||
|
||||
export function analyzeIniRuntimeBoundaries({
|
||||
manifestText = "",
|
||||
sourceRootRel,
|
||||
sourceSearchRootRel = sourceRootRel,
|
||||
iniFile = "",
|
||||
iniText,
|
||||
executionTexts = [],
|
||||
}) {
|
||||
const manifestEntries = cleanManifest(manifestText);
|
||||
const iniValues = parseIni(iniText);
|
||||
const normalizedSourceRoot = normalizeRel(sourceRootRel);
|
||||
const normalizedSearchRoot = normalizeRel(sourceSearchRootRel);
|
||||
const sourceDir = normalizeRel(`${normalizedSourceRoot}/${dirname(iniFile)}`);
|
||||
const userMPathValues = allIniValues(iniValues, "RS274NGC", "USER_M_PATH");
|
||||
const halValues = valuesMatching(iniValues, "HAL", [
|
||||
"HALFILE",
|
||||
"HALCMD",
|
||||
"POSTGUI_HALFILE",
|
||||
"HALUI",
|
||||
]);
|
||||
const displayValues = valuesMatching(iniValues, "DISPLAY", [
|
||||
"DISPLAY",
|
||||
"PYVCP",
|
||||
"GLADEVCP",
|
||||
"EMBED_TAB_COMMAND",
|
||||
]);
|
||||
const halUiMdiCommands = allIniValues(iniValues, "HALUI", "MDI_COMMAND");
|
||||
const dbProgram = firstIniValue(iniValues, "EMCIO", "DB_PROGRAM");
|
||||
const remapValues = allIniValues(iniValues, "RS274NGC", "REMAP");
|
||||
const pythonRemapReferences = [
|
||||
...sectionValues(iniValues, "PYTHON").map((entry) => entry.value),
|
||||
...remapValues.filter((value) => /(?:^|\s)python=/i.test(value)),
|
||||
];
|
||||
const pythonUiReferences = [
|
||||
...displayValues.filter(looksLikePythonReference),
|
||||
...(dbProgram && looksLikePythonReference(dbProgram) ? [dbProgram] : []),
|
||||
];
|
||||
const pythonReferences = [...pythonRemapReferences, ...pythonUiReferences];
|
||||
const vendoredUserMCodeList = vendoredUserMCodes({
|
||||
manifestEntries,
|
||||
sourceDir,
|
||||
normalizedSearchRoot,
|
||||
userMPathValues,
|
||||
});
|
||||
const vendoredUserMCodeSet = new Set(vendoredUserMCodeList);
|
||||
const hasUserMPath = userMPathValues.length > 0;
|
||||
const executionUserMCodes = [...new Set(executionTexts.flatMap(userMCodesInText))].sort();
|
||||
const unstagedExecutionUserMCodes = executionUserMCodes
|
||||
.filter((code) => !vendoredUserMCodeSet.has(code));
|
||||
const hasExternalUserMUse = hasUserMPath && unstagedExecutionUserMCodes.length > 0;
|
||||
|
||||
const dependencies = [];
|
||||
if (dbProgram) {
|
||||
dependencies.push("tool_database_process");
|
||||
}
|
||||
if (halValues.length > 0) {
|
||||
dependencies.push("hal_process");
|
||||
}
|
||||
if (displayValues.some((value) => !/^axis$/i.test(value))) {
|
||||
dependencies.push("ui_process");
|
||||
}
|
||||
if (halUiMdiCommands.length > 0) {
|
||||
dependencies.push("halui_mdi_process");
|
||||
}
|
||||
if (pythonReferences.length > 0) {
|
||||
dependencies.push("python_runtime");
|
||||
}
|
||||
if (hasExternalUserMUse) {
|
||||
dependencies.push("external_user_m_process");
|
||||
}
|
||||
|
||||
let recommendedBlockedKind = "-";
|
||||
if (dbProgram) {
|
||||
recommendedBlockedKind = "L4-TOOL-DB";
|
||||
} else if (hasExternalUserMUse) {
|
||||
recommendedBlockedKind = "L4-USER-M-PROCESS";
|
||||
} else if (pythonRemapReferences.length > 0) {
|
||||
recommendedBlockedKind = "L4-PYTHON-REMAP";
|
||||
}
|
||||
|
||||
return {
|
||||
dependencies: [...new Set(dependencies)].sort(),
|
||||
recommendedBlockedKind,
|
||||
toolDatabaseProgram: dbProgram ?? "",
|
||||
halRuntime: {
|
||||
values: halValues,
|
||||
requiresProcess: halValues.length > 0,
|
||||
},
|
||||
uiRuntime: {
|
||||
values: displayValues,
|
||||
requiresProcess: displayValues.some((value) => !/^axis$/i.test(value)),
|
||||
},
|
||||
haluiRuntime: {
|
||||
mdiCommands: halUiMdiCommands,
|
||||
requiresProcess: halUiMdiCommands.length > 0,
|
||||
},
|
||||
userMRuntime: {
|
||||
paths: userMPathValues,
|
||||
vendoredExecutableCount: vendoredUserMCodeList.length,
|
||||
executionCodes: executionUserMCodes,
|
||||
unstagedExecutionCodes: unstagedExecutionUserMCodes,
|
||||
requiresExternalProcess: hasExternalUserMUse,
|
||||
},
|
||||
pythonRuntime: {
|
||||
references: pythonReferences,
|
||||
remapReferences: pythonRemapReferences,
|
||||
uiReferences: pythonUiReferences,
|
||||
requiresProcess: pythonReferences.length > 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function planSimConfigStaging({
|
||||
manifestText,
|
||||
machineRel,
|
||||
|
||||
Reference in New Issue
Block a user