// printer-manager.js - Manages multiple printer connections through MongoDB import { PrinterClient } from "./printerclient.js"; import { loadConfig } from "../config.js"; import log4js from "log4js"; import { sendIPC } from "../desktop/notify.js"; import { startWaiting, stopWaiting } from "../spinner.js"; // Load configuration const config = loadConfig(); const logger = log4js.getLogger("Printer Manager"); logger.level = config.logLevel; export class PrinterManager { constructor(socketClient) { this.socketClient = socketClient; this.printerClients = new Map(); this.printers = []; } async reloadPrinters() { startWaiting("Reloading printers...", logger); try { this.printers = await this.socketClient.listObjects({ objectType: "printer", filter: { host: this.socketClient.id }, populate: [ "currentFilamentStock", "currentJob", "currentSubJob", "queue", ], }); sendIPC("setPrinters", this.printers); var removedPrintersCount = 0; // Remove printer clients that are no longer in the printers list const printerIds = this.printers.map((printer) => printer._id); for (const [printerId, printerClient] of this.printerClients.entries()) { if (!printerIds.includes(printerId)) { // Close the connection before removing if (printerClient.socket) { printerClient.shouldReconnect = false; printerClient.socket.close(); } this.printerClients.delete(printerId); logger.info(`Removed printer client for printer ID: ${printerId}`); removedPrintersCount++; } } var addedPrintersCount = 0; // Add new printer clients for printers not in the printerClients map for (const printer of this.printers) { const printerId = printer._id; if (!this.printerClients.has(printerId)) { const printerClient = new PrinterClient(printer, this); await printerClient.connect(); this.printerClients.set(printerId, printerClient); logger.info(`Added printer client for printer ID: ${printerId}`); addedPrintersCount++; } } await stopWaiting(); logger.debug("Printers added:", addedPrintersCount); logger.debug("Printers removed:", removedPrintersCount); } catch (error) { await stopWaiting(); logger.error("Failed to update printers:", error); this.printers = []; } } async handlePrinterAction(id, action, callback) { logger.debug("Running printer action...", action); const printer = this.getPrinterClient(id); switch (action.type) { case "setTemperature": const setTempResult = await printer.setTemperature(action.data); callback(setTempResult); return; case "homeAxis": const homeAxisResult = await printer.homeAxis(action.data); callback(homeAxisResult); return; case "moveAxis": const moveAxisResult = await printer.moveAxis(action.data); callback(moveAxisResult); return; case "setSpeedFactor": const setSpeedFactorResult = await printer.setSpeedFactor(action.data); callback(setSpeedFactorResult); return; case "setExtrudeFactor": const setExtrudeFactorResult = await printer.setExtrudeFactor( action.data, ); callback(setExtrudeFactorResult); return; case "setMaxVelocity": const setMaxVelocityResult = await printer.setMaxVelocity(action.data); callback(setMaxVelocityResult); return; case "setMaxAcceleration": const setMaxAccelerationResult = await printer.setMaxAcceleration( action.data, ); callback(setMaxAccelerationResult); return; case "setSquareCornerVelocity": const setSquareCornerVelocityResult = await printer.setSquareCornerVelocity(action.data); callback(setSquareCornerVelocityResult); return; case "setMinCruiseRatio": const setMinCruiseRatioResult = await printer.setMinCruiseRatio( action.data, ); callback(setMinCruiseRatioResult); return; case "gcodeScript": const gcodeScriptResult = await printer.gcodeScript(action.data); callback(gcodeScriptResult); return; case "restartPrinterFirmware": const restartPrinterFirmwareResult = await printer.restartPrinterFirmware(); callback(restartPrinterFirmwareResult); return; case "restartPrinter": const restartPrinterResult = await printer.restartPrinter(); callback(restartPrinterResult); return; case "restartMoonraker": const restartMoonrakerResult = await printer.restartMoonraker(); callback(restartMoonrakerResult); return; case "deploy": const deployResult = await printer.deploySubJob(action.data); callback(deployResult); return; case "startQueue": const startQueueResult = await printer.startQueue(); callback(startQueueResult); return; case "pauseJob": const pauseJobResult = await printer.pauseJob(); callback(pauseJobResult); return; case "resumeJob": const resumeJobResult = await printer.resumeJob(); callback(resumeJobResult); return; case "cancelJob": const cancelJobResult = await printer.cancelJob(); callback(cancelJobResult); return; case "unloadFilamentStock": const unloadFilamentStockResult = await printer.unloadFilamentStock(); callback(unloadFilamentStockResult); return; case "loadFilamentStock": const loadFilamentStockResult = await printer.loadFilamentStock( action.data.filamentStock, ); callback(loadFilamentStockResult); return; } callback({ error: "Unknown command." }); } async handlePrinterUpdate(id, data) { logger.debug("Handling printer update for id:", id); const printer = this.getPrinterClient(id); if (printer) { await printer.updatePrinter(data.object); } } getPrinterClient(printerId) { return this.printerClients.get(printerId); } getAllPrinterClients() { return this.printerClients.values(); } // Close all printer connections async closeAllConnections() { startWaiting( `Closing all printer connections... current count: ${this.printerClients.size}`, logger, ); try { const clients = Array.from(this.printerClients.values()); for (const printerClient of clients) { try { // Ensure we never auto-reconnect after a manual close-all printerClient.shouldReconnect = false; await printerClient.disconnect(); logger.info( `Disconnected printer client ${printerClient?.id || "unknown"}`, ); } catch (error) { logger.error( `Failed to disconnect printer client ${ printerClient?.id || "unknown" }:`, error, ); } } console.log("Printer clients:", this.printerClients); // Clear local references so no stale clients remain this.printerClients.clear(); this.printers = []; logger.info( `All printer connections closed. Remaining clients: ${this.printerClients.size}`, ); await stopWaiting(); } catch (error) { await stopWaiting(); throw error; } } }