142 lines
6.2 KiB
JavaScript
142 lines
6.2 KiB
JavaScript
'use strict';
|
||
// Tests für ToDo_9 Paket 4: Hardware-Sync-Command (M114 R).
|
||
// - syncFromHardware liest MPos aller 3 Controller → Motorwerte → Pose
|
||
// - bewegt den Roboter NICHT (kein sendCommand)
|
||
// - robust gegen fehlende Rolle / ungültige/fehlende MPos
|
||
|
||
const RobotController = require('../robot/RobotController');
|
||
const { motorStateFromPorts, D } = require('../robot/portInverse');
|
||
const Robot = require('../robot/kinematics/Arm3SegmentLinearX');
|
||
|
||
// ── Fake-Sender mit Rolle und vorgegebenem MPos-Snapshot ─────────────────────
|
||
function fakeSender(role, machinePosition, { fail } = {}) {
|
||
return {
|
||
controllerRole: role,
|
||
requestStatusReport: jest.fn(() =>
|
||
fail
|
||
? Promise.reject(new Error(`${role}: timeout`))
|
||
: Promise.resolve({ grblState: 'Idle', machinePosition, machinePositionType: 'MPos' })
|
||
),
|
||
};
|
||
}
|
||
|
||
/** Roboter mit den drei Sendern, deren MPos die gegebenen Motorwerte kodieren. */
|
||
function robotWithMotors(m) {
|
||
const robot = new Robot(250, 264, 100);
|
||
// Produktiv-Verkabelung: base[x,y,z]=[xMotor, alpha·D, (beta−alpha)·D],
|
||
// elbow[x]=a·D, hand[x,y,z]=[(c−b)·D, eMotor·D, b·D]
|
||
const base = [m.xMotor, m.alpha * D, (m.beta - m.alpha) * D];
|
||
const elbow = [m.a * D];
|
||
const hand = [(m.c - m.b) * D, m.eMotor * D, m.b * D];
|
||
robot.cmdReceivers = [
|
||
fakeSender('base', base),
|
||
fakeSender('elbow', elbow),
|
||
fakeSender('hand', hand),
|
||
];
|
||
return robot;
|
||
}
|
||
|
||
describe('RobotController.syncFromHardware (ToDo_9 Paket 4)', () => {
|
||
|
||
beforeAll(() => { jest.spyOn(console, 'log').mockImplementation(() => {}); });
|
||
afterAll(() => { jest.restoreAllMocks(); });
|
||
|
||
test('rekonstruiert Motorwerte aus MPos und übernimmt sie', async () => {
|
||
const motors = { xMotor: 100, alpha: 0.30, beta: 0.50, a: 0.20, b: 0.40, c: 0.90, eMotor: 1.0 };
|
||
const robot = robotWithMotors(motors);
|
||
|
||
await RobotController.syncFromHardware(robot);
|
||
|
||
expect(robot.xMotor).toBeCloseTo(motors.xMotor, 6);
|
||
expect(robot.alpha).toBeCloseTo(motors.alpha, 6);
|
||
expect(robot.beta).toBeCloseTo(motors.beta, 6);
|
||
expect(robot.a).toBeCloseTo(motors.a, 6);
|
||
expect(robot.b).toBeCloseTo(motors.b, 6);
|
||
expect(robot.c).toBeCloseTo(motors.c, 6);
|
||
expect(robot.eMotor).toBeCloseTo(motors.eMotor, 6);
|
||
});
|
||
|
||
test('rechnet die Pose vor und gibt sie zurück (gleiche wie Referenz-Roboter)', async () => {
|
||
const motors = { xMotor: 42, alpha: 0.10, beta: 0.10, a: 0.05, b: 1.20, c: 1.25, eMotor: 0.0 };
|
||
const robot = robotWithMotors(motors);
|
||
|
||
const pose = await RobotController.syncFromHardware(robot);
|
||
|
||
const ref = new Robot(250, 264, 100);
|
||
Object.assign(ref, motors);
|
||
ref.calculatePositionFromMotorAngles();
|
||
|
||
for (const k of ['x', 'y', 'z', 'phi', 'theta', 'psi']) {
|
||
expect(pose[k]).toBeCloseTo(ref[k], 6);
|
||
}
|
||
});
|
||
|
||
test('bewegt den Roboter NICHT (kein execCommand auf den Sendern)', async () => {
|
||
const robot = robotWithMotors({ xMotor: 10, alpha: 0.1, beta: 0.2, a: 0.1, b: 0.2, c: 0.3, eMotor: 0 });
|
||
// execCommand-Spy auf alle Sender; darf nie aufgerufen werden
|
||
robot.cmdReceivers.forEach(s => { s.execCommand = jest.fn(); });
|
||
|
||
await RobotController.syncFromHardware(robot);
|
||
|
||
robot.cmdReceivers.forEach(s => expect(s.execCommand).not.toHaveBeenCalled());
|
||
});
|
||
|
||
test('setzt motorPosition zurück (nächster Move rechnet von echter Position)', async () => {
|
||
const robot = robotWithMotors({ xMotor: 5, alpha: 0, beta: 0, a: 0, b: 0, c: 0, eMotor: 0 });
|
||
robot.motorPosition = { dummy: true };
|
||
robot.motorPositionOld = { dummy: true };
|
||
|
||
await RobotController.syncFromHardware(robot);
|
||
|
||
expect(robot.motorPosition).toBeNull();
|
||
expect(robot.motorPositionOld).toBeNull();
|
||
});
|
||
|
||
test('fragt alle drei Controller aktiv ab (requestStatusReport)', async () => {
|
||
const robot = robotWithMotors({ xMotor: 0, alpha: 0, beta: 0, a: 0, b: 0, c: 0, eMotor: 0 });
|
||
await RobotController.syncFromHardware(robot);
|
||
robot.cmdReceivers.forEach(s => expect(s.requestStatusReport).toHaveBeenCalledTimes(1));
|
||
});
|
||
|
||
test('wirft, wenn eine Controller-Rolle fehlt', async () => {
|
||
const robot = new Robot(250, 264, 100);
|
||
robot.cmdReceivers = [fakeSender('base', [0, 0, 0]), fakeSender('hand', [0, 0, 0])]; // elbow fehlt
|
||
await expect(RobotController.syncFromHardware(robot)).rejects.toThrow(/elbow/);
|
||
});
|
||
|
||
test('wirft bei ungültiger MPos (zu wenige Achsen)', async () => {
|
||
const robot = new Robot(250, 264, 100);
|
||
robot.cmdReceivers = [
|
||
fakeSender('base', [1, 2]), // nur 2 statt 3 Werte
|
||
fakeSender('elbow', [0]),
|
||
fakeSender('hand', [0, 0, 0]),
|
||
];
|
||
await expect(RobotController.syncFromHardware(robot)).rejects.toThrow(/base/);
|
||
});
|
||
|
||
test('wirft, wenn ein Controller nicht antwortet (Timeout propagiert)', async () => {
|
||
const robot = new Robot(250, 264, 100);
|
||
robot.cmdReceivers = [
|
||
fakeSender('base', [0, 0, 0]),
|
||
fakeSender('elbow', [0], { fail: true }),
|
||
fakeSender('hand', [0, 0, 0]),
|
||
];
|
||
await expect(RobotController.syncFromHardware(robot)).rejects.toThrow(/timeout/);
|
||
});
|
||
|
||
test('Dispatch: applyCommand(M114 R) liefert ein Promise', () => {
|
||
const robot = robotWithMotors({ xMotor: 0, alpha: 0, beta: 0, a: 0, b: 0, c: 0, eMotor: 0 });
|
||
const result = RobotController.applyCommand(robot, { command: 'M114', params: { R: true } });
|
||
expect(result && typeof result.then).toBe('function');
|
||
return result; // sauber auflösen lassen
|
||
});
|
||
|
||
test('receive(): bare M114 ohne R löst keinen Sync aus (synchron, undefined)', () => {
|
||
const robot = robotWithMotors({ xMotor: 0, alpha: 0, beta: 0, a: 0, b: 0, c: 0, eMotor: 0 });
|
||
robot.cmdReceivers.forEach(s => s.requestStatusReport.mockClear());
|
||
const result = RobotController.receive(robot, 'M114');
|
||
expect(result).toBeUndefined();
|
||
robot.cmdReceivers.forEach(s => expect(s.requestStatusReport).not.toHaveBeenCalled());
|
||
});
|
||
});
|