UNPKG

@scalenc/nc-format

Version:

Library for handling TRUMPF NC file format.

337 lines 14.6 kB
"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