// printer-manager.js - Manages multiple printer connections through MongoDB import { PrinterClient } from "./printerclient.js"; import { printerModel } from "../database/printer.schema.js"; // Import your printer model import { printSubJobModel } from "../database/printsubjob.schema.js"; // Import your subjob model import { loadConfig } from "../config.js"; import log4js from "log4js"; import { printJobModel } from "../database/printjob.schema.js"; // Load configuration const config = loadConfig(); const logger = log4js.getLogger("Printer Manager"); logger.level = config.server.logLevel; export class PrinterManager { constructor(config) { this.config = config; this.printerClientConnections = new Map(); this.statusCheckInterval = null; this.initializePrinterConnections(); } async initializePrinterConnections() { try { // Get all printers from the database const printers = await printerModel.find({}).populate({ path: "currentFilamentStock", populate: { path: "filament", },}); for (const printer of printers) { await this.connectToPrinter(printer); } logger.info(`Initialized connections to ${printers.length} printers`); } catch (error) { logger.error(`Error initializing printer connections: ${error.message}`); } } async connectToPrinter(printer) { // Create and store the connection const printerClientConnection = new PrinterClient( printer, this, this.socketManager, ); this.printerClientConnections.set(printer.id, printerClientConnection); // Connect to the printer await printerClientConnection.connect(); logger.info(`Connected to printer: ${printer.name} (${printer.id})`); return true; } getPrinterClient(printerId) { return this.printerClientConnections.get(printerId); } getAllPrinterClients() { return this.printerClientConnections.values(); } // Process command for a specific printer async processPrinterCommand(command) { const printerId = command.params.printerId; const printerClientConnection = this.printerClientConnections.get(printerId); if (!printerClientConnection) { return { success: false, error: `Printer with ID ${printerId} not found`, }; } return await printerClientConnection.sendPrinterCommand(command); } async updateSubscription(printerId, socketId, mergedSubscription) { const printerClientConnection = this.printerClientConnections.get(printerId); if (!printerClientConnection) { return { success: false, error: `Printer with ID ${printerId} not found`, }; } printerClientConnection.subscriptions.set(socketId, mergedSubscription); return await printerClientConnection.updateSubscriptions(); } // Close all printer connections closeAllConnections() { for (const printerClientConnection of this.printerClientConnections.values()) { if (printerClientConnection.socket) { printerClientConnection.socket.close(); } } } setSocketManager(socketManager) { this.socketManager = socketManager; } async downloadGCODE(gcodeFileId) { logger.info(`Downloading G-code file ${gcodeFileId}`); try { // Download the G-code file with authentication const url = `http://localhost:8080/gcodefiles/${gcodeFileId}/content/`; const response = await fetch(url, { headers: { Authorization: `Bearer ${this.socketManager.socketClientConnections.values().next().value.socket.handshake.auth.token}`, }, }); if (!response.ok) { throw new Error( `Failed to download G-code file: ${response.statusText}`, ); } const gcodeContent = await response.blob(); logger.info(`G-code file ${gcodeFileId} downloaded!`); return gcodeContent; } catch (error) { logger.error("Error in deployGcodeToAllPrinters:", error); return { success: false, error: error.message, }; } } async deployPrintJob(printJobId) { logger.info(`Deploying print job ${printJobId}`); const printJob = await printJobModel .findById(printJobId) .populate("printers") .populate("subJobs"); if (!printJob) { throw new Error("Print job not found"); } if (!printJob.gcodeFile) { throw new Error("No G-code file associated with this print job"); } const gcodeFileId = printJob.gcodeFile.toString(); const fileName = `${printJob.id}.gcode`; const gcodeFile = await this.downloadGCODE(gcodeFileId); for (const printer of printJob.printers) { const printerClient = this.getPrinterClient(printer.id); if (!printerClient) { throw new Error(`Printer with ID ${printer.id} not found`); return false; } await printerClient.uploadGcodeFile(gcodeFile, fileName); await printerClient.deploySubJobs(printJob.id); } printJob.state = { type: "queued" }; printJob.updatedAt = new Date(); await printJob.save(); this.socketManager.broadcast("notify_job_update", { id: printJob.id, state: { type: "queued" }, }); return true; } async cancelSubJob(subJobId) { logger.info(`Canceling sub job ${subJobId}`); const subJob = await printSubJobModel.findById(subJobId); if (!subJob) { throw new Error("Sub job not found"); } const printerClient = this.getPrinterClient(subJob.printer.toString()); if (!printerClient) { throw new Error(`Printer with ID ${printer.id} not found`); return false; } await printerClient.cancelSubJob(subJob.subJobId); return true; } }