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.`); } setTimeoutFn(() => { senders.forEach(s => { if (s.instance?.tSocket) robot.cmdReceivers.push(s.instance); }); }, 5000); 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 };