Files
appRobotDriver/startRobot.js
2026-06-08 18:17:53 +02:00

142 lines
4.1 KiB
JavaScript
Executable File

const fs = require('fs');
const https = require('https');
const Robot = require('./robot/Robot');
const GCode = require('./robot/GCode');
const TelnetSender = require('./robot/TelnetSenderGRBL');
const initInputWS = require('./server/InputWS');
const createInfoServer = require('./server/InfoServer');
function loadHttpsOptions(fsModule) {
try {
return {
enable: true,
key: fsModule.readFileSync('https/localhost.key'),
cert: fsModule.readFileSync('https/localhost.pem'),
passphrase: 'abcd'
};
} catch (err) {
throw new Error(`Failed to load HTTPS certificate/key: ${err.message}`);
}
}
function getSenderConnectionStatus(senderInfo) {
const { name, instance } = senderInfo;
let status = 'disconnected';
let reason = 'no active socket connection';
if (instance?.getStatus) {
const senderStatus = instance.getStatus();
status = senderStatus.state || status;
reason = senderStatus.state === 'disconnected' ? senderStatus.error || reason : undefined;
} else if (instance?.isTestMode || instance?.tSocket) {
status = 'connected';
reason = undefined;
}
return { name, status, reason };
}
function createApp(options = {}) {
const {
fsModule = fs,
httpsModule = https,
processEnv = process.env,
RobotClass = Robot,
GCodeModule = GCode,
TelnetSenderClass = TelnetSender,
initInputWSFn = initInputWS,
createInfoServerFn = createInfoServer,
setTimeoutFn = setTimeout,
consoleObj = console
} = options;
const startupStatus = {
https: { ok: false, error: null },
senders: []
};
let httpsOptions;
try {
httpsOptions = loadHttpsOptions(fsModule);
startupStatus.https = { ok: true };
} catch (err) {
startupStatus.https = { ok: false, error: err.message };
consoleObj.error(startupStatus.https.error);
return { startupStatus };
}
const httpsServer = httpsModule.createServer(httpsOptions);
const robot = new RobotClass(250, 264, 100);
const sharedState = {
connectedClients: [],
lastCommands: [],
lastPings: []
};
initInputWSFn(httpsServer, robot, GCodeModule, sharedState);
const baseIP = processEnv.GRBL_BASE_IP ?? 'fluidNcBase.local';
const elbowIP = processEnv.GRBL_ELLBOW_IP ?? 'fluidNcEllbow.local';
const handIP = processEnv.GRBL_HAND_IP ?? 'fluidNcHand.local';
const telnetSender1 = new TelnetSenderClass(baseIP, 2300, 'x', 'y', 'z');
const telnetSender2 = new TelnetSenderClass(elbowIP, 5000, 'a', null, null);
const telnetSender3 = new TelnetSenderClass(handIP, 5000, 'c', 'e', 'b');
const senders = [
{ name: 'Base', instance: telnetSender1 },
{ name: 'Elbow', instance: telnetSender2 },
{ name: 'Hand', instance: telnetSender3 }
];
startupStatus.senders = senders.map(getSenderConnectionStatus);
const disconnectedSenders = startupStatus.senders.filter(s => s.status === 'disconnected');
if (disconnectedSenders.length > 0) {
consoleObj.warn(`Startup warning: ${disconnectedSenders.length} sender(s) disconnected at startup.`);
}
// Register all senders as command receivers immediately and permanently.
// Each sender's execCommand() guards internally against sending while it is
// disconnected, and resumes automatically once it (re)connects — so there is
// no need to wait for a socket or to re-register after a reconnect. The old
// 5s one-shot registration silently dropped senders that connected later
// (e.g. after EHOSTUNREACH backoff) and never registered WebSocket senders.
senders.forEach(s => robot.cmdReceivers.push(s.instance));
const port = Number(processEnv.PORT) || 2095;
httpsServer.listen(port);
consoleObj.log(`Input HTTPS/WebSocket on https://localhost:${port}`);
const infoServer = createInfoServerFn(
httpsOptions,
sharedState,
robot,
GCodeModule,
senders
);
const infoPort = 2098;
infoServer.listen(infoPort);
consoleObj.log(`Info server on https://localhost:${infoPort}`);
return {
httpsServer,
infoServer,
robot,
senders: senders.map(s => s.instance),
sharedState,
httpsOptions,
startupStatus
};
}
if (require.main === module) {
createApp();
}
module.exports = { createApp };