@scalenc/nc-format
Version:
Library for handling TRUMPF NC file format.
337 lines • 14.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Processor = void 0;
const models_1 = require("../models");
const AssignmentProcessor_1 = require("./AssignmentProcessor");
const Constants_1 = require("./Constants");
const Errors_1 = require("./Errors");
const ExpressionEvaluator_1 = require("./ExpressionEvaluator");
const MachineDefinition_1 = require("./MachineDefinition");
const MotionMode_1 = require("./MotionMode");
const ProcessorException_1 = require("./ProcessorException");
const State_1 = require("./State");
const Variables_1 = require("./Variables");
class Processor {
callback;
programs;
assignmentProcessor;
nextBlockIndex;
stop = true;
wait = false;
waitDelay;
transformationSetter;
motionStartVariables;
motionEndVariables;
ignoreStack = [];
callStack = [];
state;
constructor(callback, machine = MachineDefinition_1.MachineDefinition.makeDefault(), programs = {}) {
this.callback = callback;
this.programs = programs;
this.state = new State_1.State(machine);
this.assignmentProcessor = new AssignmentProcessor_1.AssignmentProcessor(this.state);
}
processRelative(ncText) {
const absolute = this.state.absolute;
this.state.absolute = false;
try {
this.process(ncText);
}
finally {
this.state.absolute = absolute;
}
}
process(ncText) {
this.callStack.push({ ncText });
this.stop = false;
const blocks = ncText.blocks;
for (let blockIndex = 0; blockIndex >= 0 && blockIndex < blocks.length; ++blockIndex) {
// eslint-disable-next-line security/detect-object-injection
const block = blocks[blockIndex];
if (block.statements) {
this.callback.onEnterBlock?.(blockIndex, block, ncText, this);
block.statements?.forEach((statement) => !this.stop && statement.visit(this));
if (this.wait) {
this.callWait();
}
if (this.motionStartVariables) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.callMotion(this.motionStartVariables, this.motionEndVariables);
}
if (this.transformationSetter) {
this.state.transformation.applyReverseToVars(this.state.variables);
this.transformationSetter = undefined;
}
if (this.stop) {
break;
}
if (this.nextBlockIndex !== undefined) {
blockIndex = this.nextBlockIndex - 1; // -1 since increaesed at end of loop.
this.nextBlockIndex = undefined;
}
}
}
this.callStack.pop();
}
onDeclaration(declaration) {
if (this.ignoreStack.length) {
return;
}
if (declaration.type === models_1.VariableType.REAL || declaration.type === models_1.VariableType.INT) {
declaration.variables.forEach((v) => {
const assignment = new models_1.Assignment(v.name, undefined, v.initExpression ?? new models_1.Number(0, declaration.type === models_1.VariableType.INT));
this.assignmentProcessor.process(assignment);
this.assignmentProcessor.apply();
});
}
}
onAssignment(assignment) {
if (this.ignoreStack.length) {
return;
}
if (this.transformationSetter) {
this.transformationSetter.process(assignment);
this.transformationSetter.apply();
}
else {
this.assignmentProcessor.process(assignment);
if (this.wait && assignment.variable === Constants_1.Constants.VELOCITY) {
this.waitDelay = this.assignmentProcessor.value;
}
else if (this.state.machine.isTemporaryMotionVariable(assignment.variable)) {
this.saveMotionVariable(assignment.variable, this.assignmentProcessor.value);
}
else if (this.state.machine.isCoordinateOrCenter(assignment.variable)) {
const startValue = this.state.variables.getNumberOrDefault(assignment.variable);
this.saveMotionVariable(assignment.variable, startValue, this.assignmentProcessor.value);
}
else {
this.assignmentProcessor.apply();
}
}
}
onGCode(gCode) {
if (this.ignoreStack.length) {
return;
}
switch (gCode.id) {
case 0:
this.state.motionMode = MotionMode_1.MotionMode.QUICK;
break;
case 1:
this.state.motionMode = MotionMode_1.MotionMode.LINEAR;
break;
case 2:
this.state.motionMode = MotionMode_1.MotionMode.CLOCKWISE;
break;
case 3:
this.state.motionMode = MotionMode_1.MotionMode.COUNTER_CLOCKWISE;
break;
case 4:
this.wait = true;
break;
case 70:
this.state.metric = false;
break;
case 71:
this.state.metric = true;
break;
case 90:
this.state.absolute = true;
break;
case 91:
this.state.absolute = false;
break;
default:
this.callback.onUnhandledGCode?.(gCode, this);
break;
}
}
onGoto(gotoStatement) {
if (this.ignoreStack.length) {
return;
}
let targetName;
if (gotoStatement.target instanceof models_1.Variable) {
targetName = gotoStatement.target.name.toUpperCase();
if (/^N[0-9]+$/.test(targetName)) {
const number = +targetName.substring(1);
this.nextBlockIndex = this.findBlockIndexByBlockNumber(number);
}
else {
this.nextBlockIndex = this.findBlockIndexByLabel(targetName);
}
}
else {
if (gotoStatement.target instanceof models_1.Number && gotoStatement.target.isInteger) {
targetName = `${gotoStatement.target.value}`;
this.nextBlockIndex = this.findBlockIndexByBlockNumber(gotoStatement.target.value);
}
else {
throw new ProcessorException_1.ProcessorException(Errors_1.Errors.INVALID_GOTO_TARGET.replace(/\{0\}/g, JSON.stringify(gotoStatement.target)));
}
}
if (this.nextBlockIndex === undefined) {
throw new ProcessorException_1.ProcessorException(Errors_1.Errors.UNKNOWN_GOTO_TARGET.replace(/\{0\}/g, targetName));
}
}
onInstruction(instruction) {
if (this.ignoreStack.length) {
return;
}
if (Constants_1.isTransformationInstruction(instruction.name)) {
this.state.transformation.applyToVars(this.state.variables);
if (Constants_1.isAbsoluteTransformationInstruction(instruction.name)) {
this.state.transformation.variables.clearOwn();
}
const transformState = new State_1.State(this.state.machine);
transformState.variables = this.state.transformation.variables;
transformState.absolute = false;
transformState.metric = this.state.metric;
this.transformationSetter = new AssignmentProcessor_1.AssignmentProcessor(transformState);
}
else {
const subProgram = this.programs[instruction.name.toUpperCase()];
if (subProgram) {
if (instruction.expressions?.length) {
throw new ProcessorException_1.ProcessorException(Errors_1.Errors.UNEXPECTED_ARGS_IN_SUBPROGRAM_CALL.replace(/\{0\}/g, instruction.name));
}
this.callback.onEnterSubprogram?.(instruction.name, subProgram, this);
this.process(subProgram);
if (!this.stop) {
this.callback.onLeaveSubprogram?.(instruction.name, subProgram, this);
}
}
else {
this.callback.onInstruction?.(instruction, this);
}
}
}
onFlowControlInstruction(instruction) {
switch (instruction.type) {
case models_1.FlowControlInstructionType.IF: {
if (this.ignoreStack.length) {
this.ignoreStack.push(Constants_1.Constants.ENDIF); // Ignore until ENDIF
}
else {
if (!this.isConditionTrue(instruction.expressions?.[0])) {
this.ignoreStack.push(Constants_1.Constants.ELSE); // Ignore only until ELSE
}
}
break;
}
case models_1.FlowControlInstructionType.ELSE: {
const ignoredInstruction = this.ignoreStack.pop();
if (ignoredInstruction !== Constants_1.Constants.ELSE) {
if (ignoredInstruction && ignoredInstruction !== Constants_1.Constants.ENDIF) {
throw new ProcessorException_1.ProcessorException(`Non-matching IF/ELSE/ENDIF`);
}
this.ignoreStack.push(Constants_1.Constants.ENDIF);
}
break;
}
case models_1.FlowControlInstructionType.ENDIF: {
const ignoredInstruction = this.ignoreStack.pop();
if (ignoredInstruction && ignoredInstruction !== Constants_1.Constants.ENDIF && ignoredInstruction !== Constants_1.Constants.ELSE) {
throw new ProcessorException_1.ProcessorException(`Non-matching IF/ELSE/ENDIF`);
}
break;
}
default:
throw new ProcessorException_1.ProcessorException(Errors_1.Errors.UNSUPPORTED_FLOW_CONTROL_INSTRUCTION.replace(/\{0\}/g, `${instruction.type}`));
}
}
onMCode(mCode) {
if (this.ignoreStack.length) {
return;
}
switch (mCode.id) {
case 2:
case 30:
this.stop = true;
this.callback.onFinish?.(this);
break;
case 17:
this.nextBlockIndex = -1;
break;
default:
this.callback.onUnhandledMCode?.(mCode, this);
break;
}
}
isConditionTrue(expression) {
if (!expression) {
throw new ProcessorException_1.ProcessorException(Errors_1.Errors.INVALID_CONDITIONAL_EXPRESSION);
}
const condition = ExpressionEvaluator_1.evaluate(expression, this.state.variables);
if (typeof condition !== 'number') {
throw new ProcessorException_1.ProcessorException(Errors_1.Errors.INVALID_CONDITIONAL_EXPRESSION);
}
return !!condition;
}
saveMotionVariable(name, value, newValue) {
if (!this.motionStartVariables) {
this.motionStartVariables = new Variables_1.Variables(this.state.variables);
this.motionEndVariables = new Variables_1.Variables(this.state.variables);
}
this.motionStartVariables.setNumber(name, value);
if (newValue !== undefined) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.motionEndVariables.setNumber(name, newValue);
}
}
callWait() {
const waitDelay = this.waitDelay;
this.waitDelay = undefined;
this.wait = false;
this.callback.onWait?.(waitDelay, this);
}
callMotion(start, end) {
this.motionStartVariables = undefined;
this.motionEndVariables = undefined;
end.ownKeys.forEach((v) => this.state.variables.setNumber(v, end.getNumberOrDefault(v)));
this.trySetCenterOfFirstLinearAxisFromSignedRadius(start, end);
this.assignmentProcessor.setMissingCenterVariables(start);
this.state.transformation.applyToVars(start);
this.state.transformation.applyToVars(end);
this.callback.onMotion?.(start, end, this);
}
findBlockIndexByBlockNumber(blockNumber) {
const index = this.callStack[this.callStack.length - 1].ncText.blocks.findIndex((b) => b.blockNumber === blockNumber);
return index < 0 ? undefined : index;
}
findBlockIndexByLabel(label) {
const labelUpperCase = label.toUpperCase();
const index = this.callStack[this.callStack.length - 1].ncText.blocks.findIndex((b) => b.labels?.includes(labelUpperCase));
return index < 0 ? undefined : index;
}
trySetCenterOfFirstLinearAxisFromSignedRadius(start, end) {
const axes = this.state.machine.linearAxes[0];
if (axes.centerNames.length >= 2) {
Processor.trySetCenterFromSignedRadius(start, end, this.state.motionMode === MotionMode_1.MotionMode.CLOCKWISE, axes);
}
}
static trySetCenterFromSignedRadius(start, end, isClockwise, axes) {
const signedRadius = start.tryGetNumber(Constants_1.Constants.CENTER_RADIUS);
if (signedRadius !== undefined) {
const startX = start.getNumberOrDefault(axes.names[0]);
const startY = start.getNumberOrDefault(axes.names[1]);
const endX = end.getNumberOrDefault(axes.names[0]);
const endY = end.getNumberOrDefault(axes.names[1]);
const diffX = (endX - startX) / 2.0;
const diffY = (endY - startY) / 2.0;
const diffSquareLength = diffX * diffX + diffY * diffY;
const diffLength = Math.sqrt(diffSquareLength);
const orthOffset = Math.sqrt(signedRadius * signedRadius - diffSquareLength);
const sign = isClockwise ? Math.sign(signedRadius) : -Math.sign(signedRadius);
const orthX = (sign * orthOffset * diffY) / diffLength;
const orthY = (-sign * orthOffset * diffX) / diffLength;
const centerX = startX + diffX + orthX;
const centerY = startY + diffY + orthY;
start.setNumber(axes.centerNames[0], centerX);
start.setNumber(axes.centerNames[1], centerY);
}
}
}
exports.Processor = Processor;
//# sourceMappingURL=Processor.js.map