From 78a246726f8612eb3b30d9f765b5d86eda381434 Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Sun, 8 Nov 2026 23:59:53 +0000 Subject: [PATCH] Refactor cancelSubJob method in PrinterClient for improved error handling and state management - Updated the cancelSubJob method to accept a subJob object instead of an ID, enhancing clarity. - Added comprehensive error handling for various failure scenarios, including connection status and missing job IDs. - Implemented state checks to ensure only queued sub jobs can be canceled, improving robustness. - Enhanced logging for better visibility into the cancellation process and outcomes. - Updated PrinterManager to handle the new cancelSubJob action, integrating it into the existing command structure. --- src/printer/printerclient.js | 85 ++++++++++++++++++++++++++++------- src/printer/printermanager.js | 4 ++ 2 files changed, 72 insertions(+), 17 deletions(-) diff --git a/src/printer/printerclient.js b/src/printer/printerclient.js index a3bf1e1..d0206d3 100644 --- a/src/printer/printerclient.js +++ b/src/printer/printerclient.js @@ -1363,25 +1363,76 @@ export class PrinterClient { logger.info("Sub job deployed to printer:", this.id); } - async cancelSubJob(subJobId) { - try { - this.subJobCancelId = subJobId; - const result = await this.sendPrinterCommand({ - method: "server.job_queue.delete_job", - params: { - job_ids: [subJobId], - }, - }); + async cancelSubJob(subJob) { + const subJobId = refId(subJob); + logger.info(`Cancelling sub job ${subJobId} for ${this.printer.name}`); - if (!result) { - throw new Error("Failed to cancel sub job"); - } - this.subJobCancelId = null; - - logger.info("Sub job canceled:", subJobId); - } catch (error) { - logger.error(`Error canceling sub job ${subJobId}:`, error); + if (!this.isOnline) { + logger.error( + `Cannot cancel sub job: Not connected to Moonraker (${this.printer.name})`, + ); + return false; } + + if (!subJobId) { + logger.error("Cannot cancel sub job: missing sub job id"); + return false; + } + + return this._runQueueMutation(async () => { + try { + const record = await this.socketClient.getObject({ + objectType: "subJob", + _id: subJobId, + }); + const moonrakerJobId = record?.moonrakerJobId; + if (!moonrakerJobId) { + logger.error( + `Cannot cancel sub job ${subJobId}: missing moonraker job id`, + ); + return false; + } + if (record?.state?.type !== "queued") { + logger.error( + `Cannot cancel sub job ${subJobId}: state is ${record?.state?.type}`, + ); + return false; + } + + this.subJobCancelId = moonrakerJobId; + const result = await this.sendPrinterCommand({ + method: "server.job_queue.delete_job", + params: { + job_ids: [moonrakerJobId], + }, + }); + + if (!result) { + throw new Error("Failed to cancel sub job"); + } + + await this.database.updateSubJobState(subJobId, { type: "cancelled" }); + await this.database.setSubJobFinishedAt(subJobId, new Date()); + const jobId = refId(record.job); + if (jobId) { + await this.database.updateJobState(jobId); + } + + const printer = await this.database.getPrinter(); + const queue = Array.isArray(printer?.queue) ? printer.queue : []; + const remaining = queue.filter((item) => refId(item) !== subJobId); + await this.database.setQueuedSubJobs(remaining); + this.queue = remaining; + + logger.info(`Successfully cancelled sub job ${subJobId}`); + return true; + } catch (error) { + logger.error(`Error canceling sub job ${subJobId}:`, error); + return false; + } finally { + this.subJobCancelId = null; + } + }); } async setTemperature(temperature) { diff --git a/src/printer/printermanager.js b/src/printer/printermanager.js index 672be32..dcf1b3c 100644 --- a/src/printer/printermanager.js +++ b/src/printer/printermanager.js @@ -170,6 +170,10 @@ export class PrinterManager { const cancelJobResult = await printer.cancelJob(); callback(cancelJobResult); return; + case "cancelSubJob": + const cancelSubJobResult = await printer.cancelSubJob(action.data); + callback(cancelSubJobResult); + return; case "unloadFilamentStock": const unloadFilamentStockResult = await printer.unloadFilamentStock(); callback(unloadFilamentStockResult);