Files
appRobotDriver/test/Sender.Telnet.speedMode.test.js
2026-06-09 15:51:30 +02:00

176 lines
6.3 KiB
JavaScript

const TelnetSender = require('../robot/TelnetSenderGRBL.js');
describe('TelnetSenderGRBL — ROBOT_SPEED_MODE (ToDo_6a)', () => {
beforeAll(() => {
jest.spyOn(console, 'log').mockImplementation(() => {});
});
afterAll(() => {
jest.restoreAllMocks();
});
function makeSender() {
const sender = new TelnetSender('test.test', 2300, 'x', 'y', 'z');
sender.tSocket = { written: '', write(txt) { this.written = txt; } };
return sender;
}
// Gemeinsame Bewegungsdaten: x, alpha(=90°), beta bleibt
function makeMotion(extra) {
const mOld = { x: 0, y: 0, z: 0, a: 0, b: 0, c: 0, e: 0 };
const mNew = {
x: 12.34, y: Math.PI / 2, z: 0, a: 0, b: 0, c: 0, e: 0,
xMotorChanged: true, yMotorChanged: true, zMotorChanged: true,
feedrate: 999,
...extra
};
return { mOld, mNew };
}
test('legacy (Default): kartesische Feedrate, exakt wie bisher', () => {
const sender = makeSender();
const { mOld, mNew } = makeMotion({ speedMode: 'legacy' });
sender.execCommand('G1', mOld, mNew);
// Achsenwerte wie gehabt, Feedrate = mNew.feedrate
expect(sender.tSocket.written).toBe('G90 G1 x12.34 y90.00 z-90.00 f999.00\r\n');
});
test('ohne speedMode-Feld → ebenfalls Legacy-Verhalten', () => {
const sender = makeSender();
const { mOld, mNew } = makeMotion(); // kein speedMode
sender.execCommand('G1', mOld, mNew);
expect(sender.tSocket.written).toContain('f999.00');
});
test('correct: koordinierte Feedrate ersetzt die kartesische', () => {
const sender = makeSender();
const { mOld, mNew } = makeMotion({ speedMode: 'correct', moveTime: 0.5 });
const expectedF = sender.computeCoordinatedFeedrate(mOld, mNew);
sender.execCommand('G1', mOld, mNew);
// Achsenwerte identisch zu Legacy — nur die Feedrate unterscheidet sich
expect(sender.tSocket.written).toContain('x12.34 y90.00 z-90.00');
expect(sender.tSocket.written).toContain('f' + expectedF.toFixed(2));
// und sie ist NICHT die kartesische 999
expect(sender.tSocket.written).not.toContain('f999.00');
});
test('correct ohne moveTime → Fallback auf Legacy-Feedrate', () => {
const sender = makeSender();
const { mOld, mNew } = makeMotion({ speedMode: 'correct' }); // kein moveTime
sender.execCommand('G1', mOld, mNew);
expect(sender.tSocket.written).toContain('f999.00');
});
test('correct ohne Bewegung (alt == neu) → null → Fallback', () => {
const sender = makeSender();
const { mNew } = makeMotion({ speedMode: 'correct', moveTime: 0.5 });
// gleiche Position als alt UND neu → Strecke 0
expect(sender.computeCoordinatedFeedrate(mNew, mNew)).toBeNull();
sender.execCommand('G1', mNew, mNew);
expect(sender.tSocket.written).toContain('f999.00');
});
test('computeCoordinatedFeedrate = Sender-Strecke / moveTime', () => {
const sender = makeSender();
const { mOld, mNew } = makeMotion({ speedMode: 'correct', moveTime: 0.5 });
// erwartete Strecke: x-Port=12.34, y-Port=90, z-Port=(0-90)=-90
const dist = Math.sqrt(12.34 ** 2 + 90 ** 2 + (-90) ** 2);
const expected = dist / 0.5;
expect(sender.computeCoordinatedFeedrate(mOld, mNew)).toBeCloseTo(expected, 4);
});
test('Hand-Sender (c,e,b) liefert im Korrekt-Modus koordinierte Feedrate', () => {
const hand = new TelnetSender('test.test', 5000, 'c', 'e', 'b');
hand.tSocket = { written: '', write(t) { this.written = t; } };
const mOld = { x: 0, y: 0, z: 0, a: 0, b: 0, c: 0, e: 0 };
const mNew = {
x: 0, y: 0, z: 0, a: 0, b: 0.5, c: 0.8, e: 0.3,
bMotorChanged: true, cMotorChanged: true, eMotorChanged: true,
feedrate: 1234, speedMode: 'correct', moveTime: 0.2
};
const expectedF = hand.computeCoordinatedFeedrate(mOld, mNew);
expect(expectedF).toBeGreaterThan(0);
hand.execCommand('G1', mOld, mNew);
expect(hand.tSocket.written).toContain('f' + expectedF.toFixed(2));
expect(hand.tSocket.written).not.toContain('f1234.00');
});
});
/**
* Kreuzprobe: portValue() muss exakt das liefern, was der echte Sende-Pfad
* (execCommand, Legacy) an den jeweiligen GRBL-Port schreibt. Schützt die in
* portValue dupliziert gehaltenen Formeln gegen Drift.
*/
describe('TelnetSenderGRBL.portValue ↔ Sende-Pfad (Kreuzprobe)', () => {
beforeAll(() => {
jest.spyOn(console, 'log').mockImplementation(() => {});
});
afterAll(() => {
jest.restoreAllMocks();
});
const POS = { x: 1.1, y: 0.3, z: 0.7, a: 0.9, b: 1.3, c: 0.5, e: 0.4 };
function mNewAllChanged() {
return {
...POS,
xMotorChanged: true, yMotorChanged: true, zMotorChanged: true,
aMotorChanged: true, bMotorChanged: true, cMotorChanged: true, eMotorChanged: true,
feedrate: 100
};
}
function axisVal(line, axis) {
const m = line.match(new RegExp(axis + '(-?\\d+(?:\\.\\d+)?)'));
return m ? parseFloat(m[1]) : undefined;
}
// Baut einen Sender, der genau EINEN GRBL-Port auf eine Roboter-Achse abbildet,
// führt execCommand aus und vergleicht den gesendeten Wert mit portValue().
function crossCheck(grblPort, robotAxis) {
const ctorArgs = ['test.test', 100, null, null, null, null, null];
// Position des Ports in der Konstruktor-Signatur: x=2,y=3,z=4,a=5,b=6
const idx = { x: 2, y: 3, z: 4, a: 5, b: 6 }[grblPort];
ctorArgs[idx] = robotAxis;
const sender = new TelnetSender(...ctorArgs);
sender.tSocket = { written: '', write(t) { this.written = t; } };
const mNew = mNewAllChanged();
const mOld = { x: 0, y: 0, z: 0, a: 0, b: 0, c: 0, e: 0 };
sender.execCommand('G1', mOld, mNew);
const emitted = axisVal(sender.tSocket.written, grblPort);
expect(emitted).toBeDefined();
expect(emitted).toBeCloseTo(sender.portValue(grblPort, robotAxis, mNew), 2);
}
// x-Port deckt alle sieben Roboter-Achsen ab (inkl. der kniffligen c/e-Formeln)
for (const r of ['x', 'y', 'z', 'a', 'b', 'c', 'e']) {
test(`x-Port ← robotAxis '${r}'`, () => crossCheck('x', r));
}
// Port-abhängige Formel-Varianten gezielt prüfen
test("y-Port ← 'c' (factorTurnLift-Variante)", () => crossCheck('y', 'c'));
test("z-Port ← 'c' (c+b+z-y-Variante)", () => crossCheck('z', 'c'));
test("z-Port ← 'b' (reines b, abweichend vom x-Port)", () => crossCheck('z', 'b'));
test("a-Port ← 'x' (Quirk: nutzt y)", () => crossCheck('a', 'x'));
});