From d5fe97836122f663a4b57645060ac6194e0a9b14 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Tue, 1 Sep 2026 12:34:37 +0100 Subject: [PATCH] Enhance connection handling in DocumentPrinterClient - Introduced connection state management with `isConnecting` to prevent multiple concurrent connection attempts. - Added `scheduleReconnect` method to handle reconnection logic with a delay. - Improved error handling and logging during connection and reconnection processes. - Updated `updateDocumentPrinterState` to optimize state updates and prevent unnecessary changes. --- src/documentprinter/documentprinterclient.js | 162 +++++++++++++------ src/printer/printerclient.js | 32 ++-- 2 files changed, 123 insertions(+), 71 deletions(-) diff --git a/src/documentprinter/documentprinterclient.js b/src/documentprinter/documentprinterclient.js index a6ffd89..a909df1 100644 --- a/src/documentprinter/documentprinterclient.js +++ b/src/documentprinter/documentprinterclient.js @@ -31,6 +31,8 @@ export class DocumentPrinterClient { this.state = { type: this.active == true ? "offline" : "inactive" }; this.isOnline = documentPrinter.online || false; this.shouldReconnect = true; + this.isConnecting = false; + this.reconnectTimeout = null; this.isProcessingQueue = false; this.eventUpdateInterval = null; this.initializeInterface(); @@ -138,7 +140,37 @@ export class DocumentPrinterClient { }); } + statesEqual(a, b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return ( + a.type === b.type && a.message === b.message && a.progress === b.progress + ); + } + + scheduleReconnect(delay = 30000) { + if (!this.shouldReconnect || this.active === false) { + return; + } + clearTimeout(this.reconnectTimeout); + this.reconnectTimeout = setTimeout(() => { + this.reconnectTimeout = null; + this.reconnect(); + }, delay); + } + async connect() { + if (this.isConnecting) { + logger.debug( + `Already connecting to document printer ${this.documentPrinter.name}, skipping`, + ); + return false; + } + if (this.active == false) { this.shouldReconnect = false; this.isOnline = false; @@ -146,44 +178,58 @@ export class DocumentPrinterClient { await this.updateDocumentPrinterState(); return false; } - logger.info( - `Connecting to document printer ${this.id} (${this.interface})`, - ); - clearTimeout(this.reconnectTimeout); - // Always stop the event update interval when connecting - clearInterval(this.eventUpdateInterval); - this.eventUpdateInterval = null; - this.state = { type: "connecting", message: null }; - this.isOnline = false; - await this.updateDocumentPrinterState(); - - if (!this.printerInterface) { - logger.error( - `Cannot connect: No interface initialized for ${this.interface}`, + this.isConnecting = true; + try { + logger.info( + `Connecting to document printer ${this.id} (${this.interface})`, ); - return false; - } - const result = await this.printerInterface.connect(); - - if (result.error) { - logger.error( - `Error connecting to document printer ${this.documentPrinter.name}:`, - result.error, - ); + clearTimeout(this.reconnectTimeout); + this.reconnectTimeout = null; + // Always stop the event update interval when connecting + clearInterval(this.eventUpdateInterval); + this.eventUpdateInterval = null; + this.state = { type: "connecting", message: null }; this.isOnline = false; - this.state = { type: "offline", message: result.error }; await this.updateDocumentPrinterState(); - return false; + + if (!this.printerInterface) { + logger.error( + `Cannot connect: No interface initialized for ${this.interface}`, + ); + return false; + } + + const result = await this.printerInterface.connect(); + + if (result.error) { + logger.error( + `Error connecting to document printer ${this.documentPrinter.name}:`, + result.error, + ); + this.isOnline = false; + this.state = { type: "offline", message: result.error }; + await this.updateDocumentPrinterState(); + return false; + } + logger.info( + `Connected to document printer ${this.documentPrinter.name} (${this.interface})`, + ); + return true; + } finally { + this.isConnecting = false; } - logger.info( - `Connected to document printer ${this.documentPrinter.name} (${this.interface})`, - ); - return true; } async reconnect() { + if (this.isConnecting) { + logger.debug( + `Document printer ${this.documentPrinter.name} is already connecting, skipping reconnect`, + ); + return false; + } + if (this.active == false) { logger.info( `Document printer ${this.documentPrinter.name} is inactive, skipping reconnect`, @@ -194,6 +240,10 @@ export class DocumentPrinterClient { await this.updateDocumentPrinterState(); return false; } + + clearTimeout(this.reconnectTimeout); + this.reconnectTimeout = null; + if (this.isOnline == true) { logger.info( `Disconnecting from document printer ${this.documentPrinter.name} before reconnecting...`, @@ -207,25 +257,17 @@ export class DocumentPrinterClient { const connectResult = await this.connect(); if (connectResult == false) { logger.error( - `Error reconnecting to document printer ${this.documentPrinter.name}:`, - connectResult.error, + `Error reconnecting to document printer ${this.documentPrinter.name}`, ); - if (this.shouldReconnect) { - // Attempt to reconnect after delay - setTimeout(() => this.reconnect(), 30000); - } + this.scheduleReconnect(); return false; } const initializeResult = await this.initialize(); if (initializeResult == false) { logger.error( - `Error initializing document printer ${this.documentPrinter.name}:`, - initializeResult.error, + `Error initializing document printer ${this.documentPrinter.name}`, ); - if (this.shouldReconnect) { - // Attempt to reconnect after delay - setTimeout(() => this.reconnect(), 30000); - } + this.scheduleReconnect(); return false; } @@ -281,17 +323,33 @@ export class DocumentPrinterClient { } async updateDocumentPrinterState() { + const updateData = { + online: this.isOnline, + state: this.state, + connectedAt: this.connectedAt ?? null, + }; + + const currentConnectedAt = this.documentPrinter?.connectedAt; + const connectedAtUnchanged = + (currentConnectedAt?.toString?.() ?? null) === + (updateData.connectedAt?.toString?.() ?? null); + + if ( + this.documentPrinter?.online === updateData.online && + this.statesEqual(this.documentPrinter?.state, updateData.state) && + connectedAtUnchanged + ) { + return; + } + + this.documentPrinter = { ...this.documentPrinter, ...updateData }; + try { - // Update state in database or via socket client - // This can be implemented based on your database structure - this.socketClient.editObject({ + await this.socketClient.editObject({ _id: this.id, objectType: "documentPrinter", - updateData: { - online: this.isOnline, - state: this.state, - connectedAt: this.connectedAt, - }, + updateData, + auditLog: true, }); } catch (error) { logger.error(`Failed to update document printer state:`, error); @@ -308,8 +366,7 @@ export class DocumentPrinterClient { async updateJobState(jobId, state) { logger.info(`Updating job state for ${jobId}`); - const notify = - state?.type !== "deploying" && state?.type !== "queued"; + const notify = state?.type !== "deploying" && state?.type !== "queued"; await this.socketClient.editObject({ _id: jobId, objectType: "documentJob", @@ -571,6 +628,8 @@ export class DocumentPrinterClient { async disconnect() { logger.info(`Disconnecting from ${this.documentPrinter.name}`); this.shouldReconnect = false; + clearTimeout(this.reconnectTimeout); + this.reconnectTimeout = null; // Always stop the event update interval when disconnecting clearInterval(this.eventUpdateInterval); this.eventUpdateInterval = null; @@ -583,7 +642,6 @@ export class DocumentPrinterClient { this.queue = []; // Clear queue on disconnect this.jobQuantities.clear(); await this.updateDocumentPrinterState(); - clearTimeout(this.reconnectTimeout); logger.info(`Successfully disconnected from ${this.documentPrinter.name}`); return true; } diff --git a/src/printer/printerclient.js b/src/printer/printerclient.js index 91b5858..54fe94d 100644 --- a/src/printer/printerclient.js +++ b/src/printer/printerclient.js @@ -774,8 +774,6 @@ export class PrinterClient { ); this.filamentDetected = newFilamentDetected; - console.log(this.currentFilamentStock); - if (newFilamentDetected == false && this.currentFilamentStock == null) { await this.database.removeAlertsByCode("noFilamentLoaded"); await this.database.addAlert({ @@ -1175,9 +1173,12 @@ export class PrinterClient { const remainingTime = this.deploySubJobTargetTime - now; clearTimeout(this.deploySubJobTimer); - this.deploySubJobTimer = setTimeout(() => { - this.processDeploySubJobQueue(); - }, Math.max(remainingTime, 0)); + this.deploySubJobTimer = setTimeout( + () => { + this.processDeploySubJobQueue(); + }, + Math.max(remainingTime, 0), + ); await this.database.updateSubJobState(subJob._id, { type: "deploying", @@ -1227,7 +1228,6 @@ export class PrinterClient { "with gcode file:", `${subJob.gcodeFile._id}`, ); - console.log("subJob", subJob); const gcodeFile = await this.socketClient.getObject({ objectType: "gcodeFile", _id: subJob.gcodeFile._id, @@ -1237,8 +1237,6 @@ export class PrinterClient { throw new Error("G-code file not found"); } - console.log("gcodeFile", gcodeFile); - const fileId = (gcodeFile.file?._id || gcodeFile.file).toString(); let deploymentActive = true; @@ -1517,9 +1515,7 @@ export class PrinterClient { try { if (speedFactor === undefined || speedFactor === null) { - logger.warn( - `No speed factor provided for ${this.printer.name}`, - ); + logger.warn(`No speed factor provided for ${this.printer.name}`); return this.printerActionResult(false, "No speed factor provided"); } @@ -1550,7 +1546,10 @@ export class PrinterClient { } async setExtrudeFactor({ extrudeFactor }) { - logger.info(`Setting extrude factor for ${this.printer.name}:`, extrudeFactor); + logger.info( + `Setting extrude factor for ${this.printer.name}:`, + extrudeFactor, + ); if (!this.isOnline) { logger.error( @@ -1564,9 +1563,7 @@ export class PrinterClient { try { if (extrudeFactor === undefined || extrudeFactor === null) { - logger.warn( - `No extrude factor provided for ${this.printer.name}`, - ); + logger.warn(`No extrude factor provided for ${this.printer.name}`); return this.printerActionResult(false, "No extrude factor provided"); } @@ -1710,10 +1707,7 @@ export class PrinterClient { } try { - if ( - squareCornerVelocity === undefined || - squareCornerVelocity === null - ) { + if (squareCornerVelocity === undefined || squareCornerVelocity === null) { logger.warn( `No square corner velocity provided for ${this.printer.name}`, );