Enhance connection handling in DocumentPrinterClient
All checks were successful
farmcontrol/farmcontrol-server/pipeline/head This commit looks good

- 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.
This commit is contained in:
Tom Butcher 2026-09-01 12:34:37 +01:00
parent bb9b472559
commit d5fe978361
2 changed files with 123 additions and 71 deletions

View File

@ -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,11 +178,15 @@ export class DocumentPrinterClient {
await this.updateDocumentPrinterState();
return false;
}
this.isConnecting = true;
try {
logger.info(
`Connecting to document printer ${this.id} (${this.interface})`,
);
clearTimeout(this.reconnectTimeout);
this.reconnectTimeout = null;
// Always stop the event update interval when connecting
clearInterval(this.eventUpdateInterval);
this.eventUpdateInterval = null;
@ -181,9 +217,19 @@ export class DocumentPrinterClient {
`Connected to document printer ${this.documentPrinter.name} (${this.interface})`,
);
return true;
} finally {
this.isConnecting = false;
}
}
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() {
try {
// Update state in database or via socket client
// This can be implemented based on your database structure
this.socketClient.editObject({
_id: this.id,
objectType: "documentPrinter",
updateData: {
const updateData = {
online: this.isOnline,
state: this.state,
connectedAt: this.connectedAt,
},
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 {
await this.socketClient.editObject({
_id: this.id,
objectType: "documentPrinter",
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;
}

View File

@ -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.deploySubJobTimer = setTimeout(
() => {
this.processDeploySubJobQueue();
}, Math.max(remainingTime, 0));
},
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}`,
);