Files
appRobotDriver/test/RobotConfig.test.js
2026-06-12 17:03:38 +02:00

180 lines
6.7 KiB
JavaScript

'use strict';
const { load, DEFAULTS } = require('../robot/RobotConfig');
function makeFs(content) {
return {
readFileSync: jest.fn(() => content)
};
}
function makeFailFs() {
return {
readFileSync: jest.fn(() => { throw new Error('ENOENT'); })
};
}
const log = { warn: jest.fn(), log: jest.fn(), error: jest.fn() };
const FULL_ROBOT_JSON = {
kinematics: { type: 'arm3segmentlinearx' },
motion: { defaultFeedrate: 2300, speedMode: 'legacy', speedModeOptions: ['legacy', 'correct'] },
controllers: {
base: { ip: 'fluidNcBase.local', port: 2300, protocol: 'telnet', axes: ['x', 'y', 'z'] },
elbow: { ip: 'fluidNcEllbow.local', port: 5000, protocol: 'telnet', axes: ['a', null, null] },
hand: { ip: 'fluidNcHand.local', port: 5000, protocol: 'telnet', axes: ['c', 'e', 'b'] }
},
links: {
Arm1: { skeleton: { from: [0,0,0], to: [0,-250,0] } },
Arm2: { skeleton: { from: [0,0,0], to: [0,-250,0] } },
Ellbow: { skeleton: { from: [0,0,0], to: [90,0,0] } }
}
};
describe('RobotConfig.load — Vollständige robot.json', () => {
let cfg;
beforeEach(() => {
cfg = load(makeFs(JSON.stringify(FULL_ROBOT_JSON)), {}, log);
});
test('kinematics.type aus robot.json', () => {
expect(cfg.kinematics.type).toBe('arm3segmentlinearx');
});
test('l1/l2 aus links.Arm1/Arm2.skeleton.to[1] (Betrag)', () => {
expect(cfg.kinematics.l1).toBe(250);
expect(cfg.kinematics.l2).toBe(250);
});
test('l3 aus links.Ellbow.skeleton.to[0]', () => {
expect(cfg.kinematics.l3).toBe(90);
});
test('motion.defaultFeedrate aus robot.json', () => {
expect(cfg.motion.defaultFeedrate).toBe(2300);
});
test('motion.speedMode aus robot.json', () => {
expect(cfg.motion.speedMode).toBe('legacy');
});
test('motion.useSpeedCalc false für legacy', () => {
expect(cfg.motion.useSpeedCalc).toBe(false);
});
test('controllers enthält alle drei Endpunkte', () => {
expect(cfg.controllers.base.ip).toBe('fluidNcBase.local');
expect(cfg.controllers.base.port).toBe(2300);
expect(cfg.controllers.base.protocol).toBe('telnet');
expect(cfg.controllers.elbow.port).toBe(5000);
expect(cfg.controllers.hand.ip).toBe('fluidNcHand.local');
});
test('heartbeatInterval Default wenn nicht in robot.json', () => {
expect(cfg.controllers.base.heartbeatInterval).toBe(DEFAULTS.controllers.base.heartbeatInterval);
expect(cfg.controllers.elbow.heartbeatInterval).toBe(DEFAULTS.controllers.elbow.heartbeatInterval);
expect(cfg.controllers.hand.heartbeatInterval).toBe(DEFAULTS.controllers.hand.heartbeatInterval);
});
test('axesByController gibt korrektes Array zurück', () => {
expect(cfg.axesByController('base')).toEqual(['x', 'y', 'z']);
expect(cfg.axesByController('elbow')).toEqual(['a', null, null]);
expect(cfg.axesByController('hand')).toEqual(['c', 'e', 'b']);
});
test('axesByController gibt [] für unbekannten Key zurück', () => {
expect(cfg.axesByController('unknown')).toEqual([]);
});
});
describe('RobotConfig.load — Fehlerbehandlung', () => {
test('fehlende robot.json → Defaults', () => {
const cfg = load(makeFailFs(), {}, log);
expect(cfg.kinematics.l1).toBe(DEFAULTS.kinematics.l1);
expect(cfg.motion.defaultFeedrate).toBe(DEFAULTS.motion.defaultFeedrate);
expect(cfg.controllers.base.ip).toBe(DEFAULTS.controllers.base.ip);
});
test('ungültiges JSON → Defaults', () => {
const cfg = load(makeFs('{ not valid json }'), {}, log);
expect(cfg.kinematics.type).toBe(DEFAULTS.kinematics.type);
});
test('fehlende links → kinematics-Defaults', () => {
const json = { ...FULL_ROBOT_JSON };
delete json.links;
const cfg = load(makeFs(JSON.stringify(json)), {}, log);
expect(cfg.kinematics.l1).toBe(DEFAULTS.kinematics.l1);
expect(cfg.kinematics.l3).toBe(DEFAULTS.kinematics.l3);
});
});
describe('RobotConfig.load — Env-Override', () => {
test('ROBOT_DEFAULT_FEEDRATE überschreibt robot.json', () => {
const env = { ROBOT_DEFAULT_FEEDRATE: '5000' };
const cfg = load(makeFs(JSON.stringify(FULL_ROBOT_JSON)), env, log);
expect(cfg.motion.defaultFeedrate).toBe(5000);
});
test('ROBOT_SPEED_MODE=correct setzt useSpeedCalc=true', () => {
const env = { ROBOT_SPEED_MODE: 'correct' };
const cfg = load(makeFs(JSON.stringify(FULL_ROBOT_JSON)), env, log);
expect(cfg.motion.speedMode).toBe('correct');
expect(cfg.motion.useSpeedCalc).toBe(true);
});
test('GRBL_BASE_IP überschreibt controller.base.ip', () => {
const env = { GRBL_BASE_IP: '192.168.1.10' };
const cfg = load(makeFs(JSON.stringify(FULL_ROBOT_JSON)), env, log);
expect(cfg.controllers.base.ip).toBe('192.168.1.10');
});
test('GRBL_ELLBOW_IP überschreibt controller.elbow.ip', () => {
const env = { GRBL_ELLBOW_IP: '192.168.1.11' };
const cfg = load(makeFs(JSON.stringify(FULL_ROBOT_JSON)), env, log);
expect(cfg.controllers.elbow.ip).toBe('192.168.1.11');
});
test('GRBL_HAND_IP überschreibt controller.hand.ip', () => {
const env = { GRBL_HAND_IP: '192.168.1.12' };
const cfg = load(makeFs(JSON.stringify(FULL_ROBOT_JSON)), env, log);
expect(cfg.controllers.hand.ip).toBe('192.168.1.12');
});
});
describe('RobotConfig.load — speedMode correct', () => {
test('speedMode correct aus robot.json setzt useSpeedCalc=true', () => {
const json = { ...FULL_ROBOT_JSON, motion: { ...FULL_ROBOT_JSON.motion, speedMode: 'correct' } };
const cfg = load(makeFs(JSON.stringify(json)), {}, log);
expect(cfg.motion.useSpeedCalc).toBe(true);
});
test('ROBOT_USE_SPEED_CALC=true setzt useSpeedCalc', () => {
const env = { ROBOT_USE_SPEED_CALC: 'true' };
const cfg = load(makeFs(JSON.stringify(FULL_ROBOT_JSON)), env, log);
expect(cfg.motion.useSpeedCalc).toBe(true);
});
});
describe('RobotConfig.load — heartbeatInterval', () => {
test('heartbeatInterval aus robot.json überschreibt Default', () => {
const json = {
...FULL_ROBOT_JSON,
controllers: {
...FULL_ROBOT_JSON.controllers,
base: { ...FULL_ROBOT_JSON.controllers.base, heartbeatInterval: 5000 },
elbow: { ...FULL_ROBOT_JSON.controllers.elbow, heartbeatInterval: 30000 },
}
};
const cfg = load(makeFs(JSON.stringify(json)), {}, log);
expect(cfg.controllers.base.heartbeatInterval).toBe(5000);
expect(cfg.controllers.elbow.heartbeatInterval).toBe(30000);
// hand nicht gesetzt → Default
expect(cfg.controllers.hand.heartbeatInterval).toBe(DEFAULTS.controllers.hand.heartbeatInterval);
});
test('fehlende heartbeatInterval → Default (10 000 ms)', () => {
const cfg = load(makeFailFs(), {}, log);
expect(cfg.controllers.base.heartbeatInterval).toBe(10000);
});
});