Refactored and fixed printer stated system. Added filament loading and stock.
This commit is contained in:
parent
81196897fa
commit
2ac65ec717
26
src/database/filament.schema.js
Normal file
26
src/database/filament.schema.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const filamentSchema = new mongoose.Schema({
|
||||||
|
name: { required: true, type: String },
|
||||||
|
barcode: { required: false, type: String },
|
||||||
|
url: { required: false, type: String },
|
||||||
|
image: { required: false, type: Buffer },
|
||||||
|
color: { required: true, type: String },
|
||||||
|
vendor: { type: Schema.Types.ObjectId, ref: "Vendor", required: true },
|
||||||
|
type: { required: true, type: String },
|
||||||
|
cost: { required: true, type: Number },
|
||||||
|
diameter: { required: true, type: Number },
|
||||||
|
density: { required: true, type: Number },
|
||||||
|
createdAt: { required: true, type: Date },
|
||||||
|
updatedAt: { required: true, type: Date },
|
||||||
|
emptySpoolWeight: { required: true, type: Number },
|
||||||
|
});
|
||||||
|
|
||||||
|
filamentSchema.virtual("id").get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
filamentSchema.set("toJSON", { virtuals: true });
|
||||||
|
|
||||||
|
export const filamentModel = mongoose.model("Filament", filamentSchema);
|
||||||
39
src/database/filamentstock.schema.js
Normal file
39
src/database/filamentstock.schema.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
// Define the main filamentStock schema
|
||||||
|
const filamentStockSchema = new Schema(
|
||||||
|
{
|
||||||
|
state: {
|
||||||
|
type: { type: String, required: true },
|
||||||
|
percent: { type: String, required: true },
|
||||||
|
},
|
||||||
|
startingGrossWeight: { type: Number, required: true },
|
||||||
|
startingNetWeight: { type: Number, required: true },
|
||||||
|
currentGrossWeight: { type: Number, required: true },
|
||||||
|
currentNetWeight: { type: Number, required: true },
|
||||||
|
filament: { type: mongoose.Schema.Types.ObjectId, ref: "Filament" },
|
||||||
|
stockEvents: [{
|
||||||
|
type: { type: String, required: true },
|
||||||
|
value: { type: Number, required: true },
|
||||||
|
subJob: { type: mongoose.Schema.Types.ObjectId, ref: "PrintSubJob", required: false },
|
||||||
|
job: { type: mongoose.Schema.Types.ObjectId, ref: "PrintJob", required: false },
|
||||||
|
timestamp: { type: Date, default: Date.now }
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
{ timestamps: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
filamentStockSchema.virtual("id").get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
filamentStockSchema.set("toJSON", { virtuals: true });
|
||||||
|
|
||||||
|
// Create and export the model
|
||||||
|
export const filamentStockModel = mongoose.model(
|
||||||
|
"FilamentStock",
|
||||||
|
filamentStockSchema,
|
||||||
|
);
|
||||||
24
src/database/gcodefile.schema.js
Normal file
24
src/database/gcodefile.schema.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const gcodeFileSchema = new mongoose.Schema({
|
||||||
|
name: { required: true, type: String },
|
||||||
|
gcodeFileName: { required: false, type: String },
|
||||||
|
gcodeFileInfo: { required: true, type: Object },
|
||||||
|
size: { type: Number, required: false },
|
||||||
|
filament: { type: Schema.Types.ObjectId, ref: "Filament", required: true },
|
||||||
|
parts: [{ type: Schema.Types.ObjectId, ref: "Part", required: true }],
|
||||||
|
cost: { type: Number, required: false },
|
||||||
|
createdAt: { type: Date },
|
||||||
|
updatedAt: { type: Date },
|
||||||
|
});
|
||||||
|
|
||||||
|
gcodeFileSchema.index({ name: "text", brand: "text" });
|
||||||
|
|
||||||
|
gcodeFileSchema.virtual("id").get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
gcodeFileSchema.set("toJSON", { virtuals: true });
|
||||||
|
|
||||||
|
export const gcodeFileModel = mongoose.model("GCodeFile", gcodeFileSchema);
|
||||||
@ -12,10 +12,19 @@ const moonrakerSchema = new Schema(
|
|||||||
{ _id: false },
|
{ _id: false },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Define the alert schema
|
||||||
|
const alertSchema = new Schema(
|
||||||
|
{
|
||||||
|
priority: { type: String, required: true }, // order to show
|
||||||
|
type: { type: String, required: true }, // selectFilament, error, info, message,
|
||||||
|
},
|
||||||
|
{ timestamps: true, _id: false }
|
||||||
|
);
|
||||||
|
|
||||||
// Define the main printer schema
|
// Define the main printer schema
|
||||||
const printerSchema = new Schema(
|
const printerSchema = new Schema(
|
||||||
{
|
{
|
||||||
printerName: { type: String, required: true },
|
name: { type: String, required: true },
|
||||||
online: { type: Boolean, required: true, default: false },
|
online: { type: Boolean, required: true, default: false },
|
||||||
state: {
|
state: {
|
||||||
type: { type: String, required: true, default: "Offline" },
|
type: { type: String, required: true, default: "Offline" },
|
||||||
@ -32,7 +41,9 @@ const printerSchema = new Schema(
|
|||||||
firmware: { type: String },
|
firmware: { type: String },
|
||||||
currentJob: { type: Schema.Types.ObjectId, ref: "PrintJob" },
|
currentJob: { type: Schema.Types.ObjectId, ref: "PrintJob" },
|
||||||
currentSubJob: { type: Schema.Types.ObjectId, ref: "PrintSubJob" },
|
currentSubJob: { type: Schema.Types.ObjectId, ref: "PrintSubJob" },
|
||||||
|
currentFilamentStock: { type: Schema.Types.ObjectId, ref: "FilamentStock" },
|
||||||
subJobs: [{ type: Schema.Types.ObjectId, ref: "PrintSubJob" }],
|
subJobs: [{ type: Schema.Types.ObjectId, ref: "PrintSubJob" }],
|
||||||
|
alerts: [alertSchema],
|
||||||
},
|
},
|
||||||
{ timestamps: true },
|
{ timestamps: true },
|
||||||
);
|
);
|
||||||
|
|||||||
@ -6,10 +6,13 @@ const printJobSchema = new mongoose.Schema({
|
|||||||
type: { required: true, type: String },
|
type: { required: true, type: String },
|
||||||
progress: { required: false, type: Number, default: 0 },
|
progress: { required: false, type: Number, default: 0 },
|
||||||
},
|
},
|
||||||
|
subJobStats : {
|
||||||
|
required: false, type: Object
|
||||||
|
},
|
||||||
printers: [{ type: Schema.Types.ObjectId, ref: "Printer", required: false }],
|
printers: [{ type: Schema.Types.ObjectId, ref: "Printer", required: false }],
|
||||||
createdAt: { required: true, type: Date },
|
createdAt: { required: true, type: Date },
|
||||||
updatedAt: { required: true, type: Date },
|
updatedAt: { required: true, type: Date },
|
||||||
startedAt: { required: true, type: Date },
|
startedAt: { required: false, type: Date },
|
||||||
gcodeFile: {
|
gcodeFile: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: "GCodeFile",
|
ref: "GCodeFile",
|
||||||
|
|||||||
680
src/printer/database.js
Normal file
680
src/printer/database.js
Normal file
@ -0,0 +1,680 @@
|
|||||||
|
import { printerModel } from "../database/printer.schema.js";
|
||||||
|
import { printJobModel } from "../database/printjob.schema.js";
|
||||||
|
import { printSubJobModel } from "../database/printsubjob.schema.js";
|
||||||
|
import { gcodeFileModel } from "../database/gcodefile.schema.js"
|
||||||
|
import { filamentStockModel } from "../database/filamentstock.schema.js";
|
||||||
|
import { filamentModel } from "../database/filament.schema.js";
|
||||||
|
import log4js from "log4js";
|
||||||
|
import { loadConfig } from "../config.js";
|
||||||
|
|
||||||
|
const config = loadConfig();
|
||||||
|
const logger = log4js.getLogger("Printer Database");
|
||||||
|
logger.level = config.server.logLevel;
|
||||||
|
|
||||||
|
export class PrinterDatabase {
|
||||||
|
constructor(socketManager) {
|
||||||
|
this.socketManager = socketManager;
|
||||||
|
logger.info("Initialized PrinterDatabase with socket manager");
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPrinterConfig(printerId) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Getting printer config for ${printerId}`);
|
||||||
|
|
||||||
|
const printer = await printerModel.findById(printerId);
|
||||||
|
if (!printer) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found when getting config`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`Retrieved printer config for ${printerId}:`, printer.moonraker);
|
||||||
|
return printer.moonraker;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to get printer config for ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePrinterState(printerId, state, online) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Updating printer state for ${printerId}:`, { state, online });
|
||||||
|
|
||||||
|
if (state.type === "printing" && state.progress === undefined) {
|
||||||
|
logger.debug(`Setting default progress for printing state on printer ${printerId}`);
|
||||||
|
state.progress = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
||||||
|
printerId,
|
||||||
|
{ state, online },
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!updatedPrinter) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found when updating status`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Updated printer ${printerId} state:`, {
|
||||||
|
type: state.type,
|
||||||
|
progress: state.progress,
|
||||||
|
online,
|
||||||
|
previousState: updatedPrinter.state
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
|
id: printerId,
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedPrinter;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to update printer state for ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearCurrentJob(printerId) {
|
||||||
|
try {
|
||||||
|
logger.warn(`Clearing current job for printer ${printerId}`);
|
||||||
|
|
||||||
|
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
||||||
|
printerId,
|
||||||
|
{
|
||||||
|
currentSubJob: null,
|
||||||
|
currentJob: null,
|
||||||
|
},
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!updatedPrinter) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found when clearing current job`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast the update through websocket
|
||||||
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
|
id: printerId,
|
||||||
|
currentSubJob: null,
|
||||||
|
currentJob: null
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentSubJob: null,
|
||||||
|
currentJob: null
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to clear current job for printer ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setCurrentJobForPrinting(printerId, queuedJobIds) {
|
||||||
|
try {
|
||||||
|
logger.error(`Setting current job for printer ${printerId} as printing starts`);
|
||||||
|
|
||||||
|
const printer = await printerModel.findById(printerId).populate('subJobs');
|
||||||
|
if (!printer) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const subJobs = printer.subJobs
|
||||||
|
for (const subJob of subJobs) {
|
||||||
|
if (!queuedJobIds.includes(subJob.subJobId) && ['printing', 'queued', 'paused'].includes(subJob.state.type)) {
|
||||||
|
logger.info(`Setting current job and subjob for printer ${printerId} as printing starts`, {
|
||||||
|
subJobId: subJob.id,
|
||||||
|
jobId: subJob.printJob
|
||||||
|
});
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
// Update printer with current job and startedAt
|
||||||
|
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
||||||
|
printerId,
|
||||||
|
{
|
||||||
|
currentSubJob: subJob.id,
|
||||||
|
currentJob: subJob.printJob,
|
||||||
|
startedAt: now
|
||||||
|
},
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!updatedPrinter) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found when setting job and subjob`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update subjob with startedAt
|
||||||
|
await printSubJobModel.findByIdAndUpdate(
|
||||||
|
subJob.id,
|
||||||
|
{ startedAt: now }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the full job object and update its startedAt if null
|
||||||
|
const job = await printJobModel.findById(subJob.printJob).populate('gcodeFile');
|
||||||
|
if (!job) {
|
||||||
|
logger.error(`Job with ID ${subJob.printJob} not found`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!job.startedAt) {
|
||||||
|
await printJobModel.findByIdAndUpdate(
|
||||||
|
subJob.printJob,
|
||||||
|
{ startedAt: now }
|
||||||
|
);
|
||||||
|
job.startedAt = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast the update through websocket
|
||||||
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
|
id: printerId,
|
||||||
|
currentSubJob: subJob,
|
||||||
|
currentJob: job
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentSubJob: subJob,
|
||||||
|
currentJob: job
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to set current job for printer ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateSubJobState(subJobId, state) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (state.type == "standby" || state.type == "error" || state.type == "offline") {
|
||||||
|
state.type = 'failed'
|
||||||
|
logger.warn(`Updating subjob state for ${subJobId}:`, state);
|
||||||
|
} else {
|
||||||
|
logger.debug(`Updating subjob state for ${subJobId}:`, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const updatedSubJob = await printSubJobModel.findByIdAndUpdate(
|
||||||
|
subJobId,
|
||||||
|
{ state },
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!updatedSubJob) {
|
||||||
|
logger.error(`Sub job with ID ${subJobId} not found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Updated subjob ${subJobId} state:`, {
|
||||||
|
type: state.type,
|
||||||
|
progress: state.progress,
|
||||||
|
previousState: updatedSubJob.state,
|
||||||
|
printJob: updatedSubJob.printJob
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update parent job state
|
||||||
|
await this.updateJobState(updatedSubJob.printJob);
|
||||||
|
|
||||||
|
this.socketManager.broadcast("notify_subjob_update", {
|
||||||
|
id: subJobId,
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedSubJob;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to update sub job state for ${subJobId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateJobState(jobId) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Updating job state for ${jobId}`);
|
||||||
|
|
||||||
|
const job = await printJobModel.findById(jobId).populate("subJobs");
|
||||||
|
if (!job) {
|
||||||
|
logger.error(`Job with ID ${jobId} not found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subJobStates = job.subJobs.map(subJob => subJob.state);
|
||||||
|
const stateCounts = {
|
||||||
|
printing: 0,
|
||||||
|
paused: 0,
|
||||||
|
complete: 0,
|
||||||
|
failed: 0,
|
||||||
|
queued: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
let progress = 0;
|
||||||
|
subJobStates.forEach(state => {
|
||||||
|
stateCounts[state.type]++;
|
||||||
|
progress += state.progress || 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.debug(`Job ${jobId} state counts:`, stateCounts);
|
||||||
|
|
||||||
|
const jobState = {
|
||||||
|
type: this.determineJobState(stateCounts),
|
||||||
|
progress: progress / job.subJobs.length
|
||||||
|
};
|
||||||
|
|
||||||
|
logger.debug(`Calculated job state for ${jobId}:`, {
|
||||||
|
type: jobState.type,
|
||||||
|
progress: jobState.progress,
|
||||||
|
subJobCount: job.subJobs.length
|
||||||
|
});
|
||||||
|
|
||||||
|
job.state = jobState;
|
||||||
|
job.subJobStats = stateCounts;
|
||||||
|
|
||||||
|
await job.save();
|
||||||
|
|
||||||
|
logger.info(`Updated job ${jobId} state:`, {
|
||||||
|
id: jobId,
|
||||||
|
state: jobState,
|
||||||
|
subJobStats: stateCounts
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socketManager.broadcast("notify_job_update", {
|
||||||
|
id: jobId,
|
||||||
|
state: jobState,
|
||||||
|
subJobStats: stateCounts
|
||||||
|
});
|
||||||
|
|
||||||
|
return job;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to update job state for ${jobId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
determineJobState(stateCounts) {
|
||||||
|
logger.debug("Determining job state from counts:", stateCounts);
|
||||||
|
|
||||||
|
// If any subjob is printing, the overall state should be printing
|
||||||
|
if (stateCounts.printing > 0) {
|
||||||
|
logger.debug("Job state determined as 'printing' due to active printing subjobs");
|
||||||
|
return "printing";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateCounts.failed > 0 || stateCounts.cancelled > 0) {
|
||||||
|
logger.debug("Job state determined as 'failed' due to failed or cancelled subjobs");
|
||||||
|
return "failed";
|
||||||
|
}
|
||||||
|
if (stateCounts.paused > 0) {
|
||||||
|
logger.debug("Job state determined as 'paused'");
|
||||||
|
return "paused";
|
||||||
|
}
|
||||||
|
if (stateCounts.complete === Object.values(stateCounts).reduce((a, b) => a + b, 0)) {
|
||||||
|
logger.debug("Job state determined as 'complete'");
|
||||||
|
return "complete";
|
||||||
|
}
|
||||||
|
logger.debug("Job state determined as 'queued'");
|
||||||
|
return "queued";
|
||||||
|
}
|
||||||
|
|
||||||
|
async addSubJobToPrinter(printerId, subJobId) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Adding subjob ${subJobId} to printer ${printerId}`);
|
||||||
|
|
||||||
|
const printer = await printerModel.findById(printerId);
|
||||||
|
if (!printer) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.subJobs.push(subJobId);
|
||||||
|
await printer.save();
|
||||||
|
|
||||||
|
logger.info(`Added subjob ${subJobId} to printer ${printerId}`, {
|
||||||
|
currentSubJobs: printer.subJobs.length,
|
||||||
|
printerState: printer.state
|
||||||
|
});
|
||||||
|
|
||||||
|
return printer;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to add subjob ${subJobId} to printer ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async removePrinterSubJob(printerId, subJobId) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Removing subjob ${subJobId} from printer ${printerId}`);
|
||||||
|
|
||||||
|
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
||||||
|
printerId,
|
||||||
|
{ $pull: { subJobs: subJobId } },
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!updatedPrinter) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Removed subjob ${subJobId} from printer ${printerId}`, {
|
||||||
|
remainingSubJobs: updatedPrinter.subJobs.length,
|
||||||
|
printerState: updatedPrinter.state
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedPrinter;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to remove subjob ${subJobId} from printer ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateDisplayStatus(printerId, message) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Updating display status for printer ${printerId}:`, { message });
|
||||||
|
|
||||||
|
this.socketManager.broadcast("notify_display_status", {
|
||||||
|
printerId,
|
||||||
|
message
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Broadcast display status for printer ${printerId}:`, { message });
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to broadcast display status for printer ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updatePrinterFirmware(printerId, firmwareVersion) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Updating firmware version for printer ${printerId}:`, { firmwareVersion });
|
||||||
|
|
||||||
|
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
||||||
|
printerId,
|
||||||
|
{ version: firmwareVersion },
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!updatedPrinter) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found when updating firmware version`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Updated firmware version for printer ${printerId}:`, {
|
||||||
|
newVersion: firmwareVersion,
|
||||||
|
previousVersion: updatedPrinter.version
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedPrinter;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to update firmware version for printer ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async addAlert(printerId, alert) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Adding alert to printer ${printerId}:`, alert);
|
||||||
|
|
||||||
|
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
||||||
|
printerId,
|
||||||
|
{ $push: { alerts: alert } },
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!updatedPrinter) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found when adding alert`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Added alert to printer ${printerId}:`, {
|
||||||
|
type: alert.type,
|
||||||
|
priority: alert.priority
|
||||||
|
});
|
||||||
|
|
||||||
|
// Broadcast the alert through websocket
|
||||||
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
|
id: printerId,
|
||||||
|
alerts: updatedPrinter.alerts
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedPrinter;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to add alert to printer ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeAlerts(printerId, options = {}) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Clearing alerts for printer ${printerId}:`, options);
|
||||||
|
|
||||||
|
let query = {};
|
||||||
|
if (options.type) {
|
||||||
|
query.type = options.type;
|
||||||
|
}
|
||||||
|
if (options.priority) {
|
||||||
|
query.priority = options.priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
||||||
|
printerId,
|
||||||
|
{ $pull: { alerts: query } },
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!updatedPrinter) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found when clearing alerts`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Cleared alerts for printer ${printerId}:`, {
|
||||||
|
options,
|
||||||
|
remainingAlerts: updatedPrinter.alerts.length
|
||||||
|
});
|
||||||
|
|
||||||
|
// Broadcast the alert clear through websocket
|
||||||
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
|
id: printerId,
|
||||||
|
alerts: updatedPrinter.alerts
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedPrinter;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to clear alerts for printer ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAlerts(printerId, options = {}) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Getting alerts for printer ${printerId}:`, options);
|
||||||
|
|
||||||
|
const printer = await printerModel.findById(printerId);
|
||||||
|
if (!printer) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found when getting alerts`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let alerts = printer.alerts;
|
||||||
|
|
||||||
|
// Filter alerts based on options
|
||||||
|
if (options.type) {
|
||||||
|
alerts = alerts.filter(alert => alert.type === options.type);
|
||||||
|
}
|
||||||
|
if (options.priority) {
|
||||||
|
alerts = alerts.filter(alert => alert.priority === options.priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort alerts by priority
|
||||||
|
alerts.sort((a, b) => a.priority.localeCompare(b.priority));
|
||||||
|
|
||||||
|
logger.info(`Retrieved ${alerts.length} alerts for printer ${printerId}`);
|
||||||
|
return alerts;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to get alerts for printer ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setCurrentFilamentStock(printerId, filamentStockId) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Setting current filament stock for printer ${printerId}:`, { filamentStockId });
|
||||||
|
|
||||||
|
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
||||||
|
printerId,
|
||||||
|
{ currentFilamentStock: filamentStockId },
|
||||||
|
{ new: true }
|
||||||
|
).populate({ path: "currentFilamentStock",
|
||||||
|
populate: {
|
||||||
|
path: "filament",
|
||||||
|
}});
|
||||||
|
|
||||||
|
if (!updatedPrinter) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found when setting current filament stock`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Updated current filament stock for printer ${printerId}:`, {
|
||||||
|
filamentStock: updatedPrinter.currentFilamentStock,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Broadcast the update through websocket
|
||||||
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
|
id: printerId,
|
||||||
|
currentFilamentStock: updatedPrinter.currentFilamentStock
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedPrinter.currentFilamentStock;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to set current filament stock for printer ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateFilamentStockWeight(filamentStockId, weight, subJobId = null, jobId = null) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Updating filament stock weight for ${filamentStockId}:`, { weight, subJobId, jobId });
|
||||||
|
|
||||||
|
const filamentStock = await filamentStockModel.findById(filamentStockId);
|
||||||
|
if (!filamentStock) {
|
||||||
|
logger.error(`Filament stock with ID ${filamentStockId} not found`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a stock event already exists for this subJobId and jobId
|
||||||
|
const existingEventIndex = filamentStock.stockEvents.findIndex(
|
||||||
|
event => event.subJob?.toString() === subJobId.toString() &&
|
||||||
|
event.job?.toString() === jobId.toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
let updatedFilamentStock;
|
||||||
|
if (existingEventIndex !== -1) {
|
||||||
|
// Update existing event
|
||||||
|
logger.debug(`Updating existing stock event for subJobId ${subJobId} and jobId ${jobId}`);
|
||||||
|
updatedFilamentStock = await filamentStockModel.findOneAndUpdate(
|
||||||
|
{
|
||||||
|
_id: filamentStockId,
|
||||||
|
'stockEvents.subJob': subJobId,
|
||||||
|
'stockEvents.job': jobId
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
'stockEvents.$.value': weight,
|
||||||
|
'stockEvents.$.timestamp': new Date()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Create new stock event
|
||||||
|
logger.debug(`Creating new stock event for subJobId ${subJobId} and jobId ${jobId}`);
|
||||||
|
const stockEvent = {
|
||||||
|
type: 'subJob',
|
||||||
|
value: weight,
|
||||||
|
subJob: subJobId,
|
||||||
|
job: jobId,
|
||||||
|
timestamp: new Date()
|
||||||
|
};
|
||||||
|
|
||||||
|
updatedFilamentStock = await filamentStockModel.findByIdAndUpdate(
|
||||||
|
filamentStockId,
|
||||||
|
{
|
||||||
|
$push: { stockEvents: stockEvent }
|
||||||
|
},
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!updatedFilamentStock) {
|
||||||
|
logger.error(`Failed to update filament stock ${filamentStockId}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate new net weight based on all stock events
|
||||||
|
const totalEventWeight = updatedFilamentStock.stockEvents.reduce((sum, event) => sum + event.value, 0);
|
||||||
|
const newNetWeight = updatedFilamentStock.startingNetWeight + totalEventWeight;
|
||||||
|
const newGrossWeight = updatedFilamentStock.startingGrossWeight + totalEventWeight;
|
||||||
|
|
||||||
|
|
||||||
|
// Update the net weight
|
||||||
|
const finalFilamentStock = await filamentStockModel.findByIdAndUpdate(
|
||||||
|
filamentStockId,
|
||||||
|
{ currentNetWeight: newNetWeight, currentGrossWeight: newGrossWeight },
|
||||||
|
{
|
||||||
|
new: true,
|
||||||
|
populate: {
|
||||||
|
path: 'stockEvents',
|
||||||
|
populate: [
|
||||||
|
{
|
||||||
|
path: 'subJob',
|
||||||
|
select: 'number'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'job',
|
||||||
|
select: 'startedAt'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate remaining percentage and update state
|
||||||
|
const remainingPercent = newNetWeight / finalFilamentStock.startingNetWeight;
|
||||||
|
const state = {
|
||||||
|
type: 'partiallyconsumed',
|
||||||
|
percent: (1 - remainingPercent).toFixed(2)
|
||||||
|
};
|
||||||
|
|
||||||
|
const filamentStockWithState = await filamentStockModel.findByIdAndUpdate(
|
||||||
|
filamentStockId,
|
||||||
|
{ state },
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info(`Updated filament stock ${filamentStockId}:`, {
|
||||||
|
newGrossWeight: newGrossWeight,
|
||||||
|
newNetWeight: newNetWeight,
|
||||||
|
eventCount: finalFilamentStock.stockEvents.length,
|
||||||
|
updatedExistingEvent: existingEventIndex !== -1,
|
||||||
|
remainingPercent: remainingPercent.toFixed(2),
|
||||||
|
consumedPercent: state.percent,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Broadcast the update through websocket
|
||||||
|
this.socketManager.broadcast("notify_filamentstock_update", {
|
||||||
|
id: filamentStockId,
|
||||||
|
currentNetWeight: newNetWeight,
|
||||||
|
currentGrossWeight: newGrossWeight,
|
||||||
|
stockEvents: finalFilamentStock.stockEvents,
|
||||||
|
state
|
||||||
|
});
|
||||||
|
|
||||||
|
return filamentStockWithState;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to update filament stock weight for ${filamentStockId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,9 +2,9 @@
|
|||||||
import { JsonRPC } from "./jsonrpc.js";
|
import { JsonRPC } from "./jsonrpc.js";
|
||||||
import { WebSocket } from "ws";
|
import { WebSocket } from "ws";
|
||||||
import { loadConfig } from "../config.js";
|
import { loadConfig } from "../config.js";
|
||||||
import { printerModel } from "../database/printer.schema.js"; // Import your printer model
|
import { printerModel } from "../database/printer.schema.js";
|
||||||
import { printJobModel } from "../database/printjob.schema.js";
|
|
||||||
import { printSubJobModel } from "../database/printsubjob.schema.js";
|
import { printSubJobModel } from "../database/printsubjob.schema.js";
|
||||||
|
import { PrinterDatabase } from "./database.js";
|
||||||
import log4js from "log4js";
|
import log4js from "log4js";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import FormData from "form-data";
|
import FormData from "form-data";
|
||||||
@ -18,30 +18,36 @@ logger.level = config.server.logLevel;
|
|||||||
export class PrinterClient {
|
export class PrinterClient {
|
||||||
constructor(printer, printerManager, socketManager) {
|
constructor(printer, printerManager, socketManager) {
|
||||||
this.id = printer.id;
|
this.id = printer.id;
|
||||||
this.name = printer.printerName;
|
this.name = printer.name;
|
||||||
this.printerManager = printerManager;
|
this.printerManager = printerManager;
|
||||||
this.socketManager = socketManager;
|
this.socketManager = socketManager;
|
||||||
this.state = printer.state;
|
this.state = { type: 'offline '};
|
||||||
this.klippyState = null;
|
this.klippyState = { type: 'offline '};
|
||||||
this.config = printer.moonraker;
|
this.config = printer.moonraker;
|
||||||
this.version = printer.version;
|
this.version = printer.version;
|
||||||
this.jsonRpc = new JsonRPC();
|
this.jsonRpc = new JsonRPC();
|
||||||
this.socket = null;
|
this.socket = null;
|
||||||
this.connectionId = null;
|
this.connectionId = null;
|
||||||
this.currentJobId = printer.currentJob;
|
this.currentJobId = null;
|
||||||
this.currentJobState = { type: "unknown", progress: 0 };
|
this.currentJobState = { type: "unknown", progress: 0 };
|
||||||
this.currentSubJobId = printer.currentSubJob;
|
this.currentSubJobId = null;
|
||||||
this.currentSubJobState = { type: "unknown", progress: 0 };
|
this.currentSubJobState = null;
|
||||||
|
this.currentFilamentStockId = printer.currentFilamentStock?._id.toString() || null;
|
||||||
|
this.currentFilamentStockDensity = printer.currentFilamentStock?.filament?.density || null
|
||||||
this.registerEventHandlers();
|
this.registerEventHandlers();
|
||||||
this.baseSubscription = {
|
this.baseSubscription = {
|
||||||
print_stats: null,
|
print_stats: null,
|
||||||
display_status: null,
|
display_status: null,
|
||||||
|
'filament_switch_sensor fsensor': null,
|
||||||
|
output_pin: null
|
||||||
};
|
};
|
||||||
this.subscriptions = new Map();
|
this.subscriptions = new Map();
|
||||||
this.queuedJobIds = [];
|
this.queuedJobIds = [];
|
||||||
this.isOnline = printer.online;
|
this.isOnline = printer.online;
|
||||||
this.subJobIsCancelling = false;
|
this.subJobIsCancelling = false;
|
||||||
this.subJobCancelId = null;
|
this.subJobCancelId = null;
|
||||||
|
this.database = new PrinterDatabase(socketManager);
|
||||||
|
this.filamentDetected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
registerEventHandlers() {
|
registerEventHandlers() {
|
||||||
@ -78,10 +84,12 @@ export class PrinterClient {
|
|||||||
|
|
||||||
async getPrinterConnectionConfig() {
|
async getPrinterConnectionConfig() {
|
||||||
try {
|
try {
|
||||||
const printer = await printerModel.findOne({ _id: this.id });
|
const config = await this.database.getPrinterConfig(this.id);
|
||||||
this.config = printer.moonraker;
|
if (config) {
|
||||||
logger.info(`Reloaded connection config! (${this.name})`);
|
this.config = config;
|
||||||
logger.debug(this.config);
|
logger.info(`Reloaded connection config! (${this.name})`);
|
||||||
|
logger.debug(this.config);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`Failed to get printer connection config! (${this.name}):`,
|
`Failed to get printer connection config! (${this.name}):`,
|
||||||
@ -181,21 +189,10 @@ export class PrinterClient {
|
|||||||
`Klippy info for ${this.name}: ${klippyResult.hostname}, ${klippyResult.software_version}`,
|
`Klippy info for ${this.name}: ${klippyResult.hostname}, ${klippyResult.software_version}`,
|
||||||
);
|
);
|
||||||
// Update firmware version in database
|
// Update firmware version in database
|
||||||
try {
|
await this.database.updatePrinterFirmware(this.id, klippyResult.software_version);
|
||||||
await printerModel.findByIdAndUpdate(
|
logger.info(
|
||||||
this.id,
|
`Updated firmware version for ${this.name} to ${klippyResult.software_version}`,
|
||||||
{ firmware: klippyResult.software_version },
|
);
|
||||||
{ new: true },
|
|
||||||
);
|
|
||||||
logger.info(
|
|
||||||
`Updated firmware version for ${this.name} to ${klippyResult.software_version}`,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(
|
|
||||||
`Failed to update firmware version in database (${this.name}):`,
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (klippyResult.state === "error") {
|
if (klippyResult.state === "error") {
|
||||||
logger.error(
|
logger.error(
|
||||||
@ -321,287 +318,220 @@ export class PrinterClient {
|
|||||||
async handleStatusUpdate(status) {
|
async handleStatusUpdate(status) {
|
||||||
logger.trace("Status update:", status);
|
logger.trace("Status update:", status);
|
||||||
status = status[0];
|
status = status[0];
|
||||||
// Process printer status updates
|
|
||||||
if (this.state.type != "deploying") {
|
|
||||||
if (status.print_stats && status.print_stats.state) {
|
|
||||||
logger.info(
|
|
||||||
`Print state for ${this.name}: ${status.print_stats.state}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const stateTypeChanged = status.print_stats.state != this.state.type;
|
if (this.state.type === "deploying") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// When status changes, update states
|
let stateChanged = false;
|
||||||
if (stateTypeChanged) {
|
let progressChanged = false;
|
||||||
this.state.type = status.print_stats.state;
|
|
||||||
await this.updatePrinterState();
|
if (status.print_stats?.state) {
|
||||||
|
const newState = status.print_stats.state;
|
||||||
|
if (newState !== this.state.type) {
|
||||||
|
logger.info(`printer ${this.name} state changed from ${this.state.type} to ${newState}`);
|
||||||
|
this.state.type = newState;
|
||||||
|
stateChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.print_stats?.filament_used) {
|
||||||
|
// Convert mm to cm and calculate volume
|
||||||
|
const filamentLengthCm = status.print_stats.filament_used / 10;
|
||||||
|
const filamentDiameterCm = 0.175; // 1.75mm in cm
|
||||||
|
const filamentRadiusCm = filamentDiameterCm / 2;
|
||||||
|
const filamentVolumeCm3 = Math.PI * Math.pow(filamentRadiusCm, 2) * filamentLengthCm;
|
||||||
|
|
||||||
|
// Calculate weight in grams
|
||||||
|
const filamentWeightG = filamentVolumeCm3 * this.currentFilamentStockDensity;
|
||||||
|
|
||||||
|
console.log('Filament used:', {
|
||||||
|
length: status.print_stats.filament_used,
|
||||||
|
lengthCm: filamentLengthCm,
|
||||||
|
volumeCm3: filamentVolumeCm3,
|
||||||
|
density: this.currentFilamentStockDensity,
|
||||||
|
weightG: filamentWeightG
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.currentSubJobId != null && this.currentJobId != null && this.currentFilamentStockId != null) {
|
||||||
|
this.database.updateFilamentStockWeight(this.currentFilamentStockId, (-1 * filamentWeightG), this.currentSubJobId, this.currentJobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.display_status?.progress !== undefined) {
|
||||||
|
const newProgress = status.display_status.progress;
|
||||||
|
if (newProgress !== this.state.progress) {
|
||||||
|
logger.info(`Printer ${this.name} progress changed from ${this.state.progress} to ${newProgress}`);
|
||||||
|
this.state.progress = newProgress;
|
||||||
|
progressChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.display_status.message) {
|
||||||
|
await this.database.updateDisplayStatus(this.id, status.display_status.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle filament switch sensor
|
||||||
|
if (status['filament_switch_sensor fsensor']?.filament_detected !== undefined) {
|
||||||
|
const newFilamentDetected = status['filament_switch_sensor fsensor'].filament_detected;
|
||||||
|
if (newFilamentDetected !== this.filamentDetected) {
|
||||||
|
logger.info(`Printer ${this.name} filament detection changed from ${this.filamentDetected} to ${newFilamentDetected} with no currentFilamentId`);
|
||||||
|
this.filamentDetected = newFilamentDetected;
|
||||||
|
|
||||||
|
if (newFilamentDetected == true && this.currentFilamentStockId == null) {
|
||||||
|
await this.database.addAlert(this.id, {
|
||||||
|
type: "loadFilamentStock",
|
||||||
|
priority: 1,
|
||||||
|
timestamp: new Date()
|
||||||
|
});
|
||||||
|
} else if (newFilamentDetected == false && this.currentFilamentStockId != null) {
|
||||||
|
|
||||||
|
this.currentFilamentStockId = null
|
||||||
|
await this.database.setCurrentFilamentStock(this.id, null)
|
||||||
|
// Remove filament select alert if it exists
|
||||||
|
await this.database.removeAlerts(this.id, {type:"loadFilamentStock"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateChanged || progressChanged) {
|
||||||
|
// Update printer state first
|
||||||
|
await this.database.updatePrinterState(this.id, this.state, this.online);
|
||||||
|
|
||||||
|
logger.warn('State changed!')
|
||||||
|
// Set current job to null when not printing or paused
|
||||||
|
if (!["printing", "paused"].includes(this.state.type)) {
|
||||||
|
this.currentJobId = null;
|
||||||
|
this.currentSubJobId = null;
|
||||||
|
await this.database.clearCurrentJob(this.id, `Printer is in ${this.state.type} state`);
|
||||||
|
await this.getQueuedJobsInfo();
|
||||||
|
} else {
|
||||||
|
// If we have a current subjob, update its state
|
||||||
|
if (this.currentSubJobId) {
|
||||||
|
logger.debug(`Updating current subjob ${this.currentSubJobId} state:`, this.state);
|
||||||
|
await this.database.updateSubJobState(this.currentSubJobId, this.state);
|
||||||
|
} else {
|
||||||
|
// If no current subjob but we have queued jobs, check if we need to update printer subjobs
|
||||||
await this.getQueuedJobsInfo();
|
await this.getQueuedJobsInfo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.display_status) {
|
|
||||||
console.log("Display status:", status.display_status);
|
|
||||||
this.state.progress = status.display_status.progress;
|
|
||||||
await this.updatePrinterState();
|
|
||||||
await this.updateCurrentJobAndSubJobState();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.socketManager.broadcastToSubscribers(this.id, {
|
|
||||||
method: "notify_status_update",
|
|
||||||
params: status,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.socketManager.broadcastToSubscribers(this.id, {
|
||||||
|
method: "notify_status_update",
|
||||||
|
params: status,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePrinterState() {
|
async updatePrinterState() {
|
||||||
try {
|
try {
|
||||||
if (this.state.type == "printing" && this.state.progress == undefined) {
|
await this.database.updatePrinterState(this.id, this.state, this.online);
|
||||||
this.state.progress = 0;
|
|
||||||
}
|
|
||||||
// Update the printer's state
|
|
||||||
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
|
||||||
this.id,
|
|
||||||
{ state: this.state, online: this.online },
|
|
||||||
{ new: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!updatedPrinter) {
|
|
||||||
logger.error(
|
|
||||||
`Printer with ID ${this.id} not found when updating status`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify clients of the update
|
|
||||||
this.socketManager.broadcast("notify_printer_update", {
|
|
||||||
id: this.id,
|
|
||||||
state: this.state,
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
`Updated printer state to ${this.state.type} and progress to ${this.state?.progress} for ${this.id}`,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
logger.error(`Failed to update printer state:`, error);
|
||||||
`Failed to update job status in database (${this.name}):`,
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async removePrinterSubJob(subJobId) {
|
async removePrinterSubJob(subJobId) {
|
||||||
try {
|
try {
|
||||||
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
await this.database.removePrinterSubJob(this.id, subJobId);
|
||||||
this.id,
|
|
||||||
{
|
|
||||||
$pull: { subJobs: subJobId },
|
|
||||||
},
|
|
||||||
{ new: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!updatedPrinter) {
|
|
||||||
logger.error(
|
|
||||||
`Printer with ID ${this.id} not found when removing failed subjob`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(`Removed subjob ${subJobId} from printer ${this.id}`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
logger.error(`Failed to remove subjob:`, error);
|
||||||
`Failed to remove subjob ${subJobId} from printer ${this.id}:`,
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePrinterSubJobs() {
|
async updatePrinterSubJobs() {
|
||||||
try {
|
try {
|
||||||
logger.debug("Updating Printer Subjobs...");
|
logger.debug(`Updating printer subjobs for ${this.id}`, {
|
||||||
// Get all subjobs for this printer
|
queuedJobIds: this.queuedJobIds,
|
||||||
const subJobs = await printSubJobModel.find({ printer: this.id });
|
currentSubJobId: this.currentSubJobId,
|
||||||
|
currentJobId: this.currentJobId,
|
||||||
|
printerState: this.state.type
|
||||||
|
});
|
||||||
|
|
||||||
for (const subJob of subJobs) {
|
const printer = await printerModel.findById(this.id).populate('subJobs');
|
||||||
if (
|
|
||||||
!this.queuedJobIds.includes(subJob.subJobId) &&
|
|
||||||
!(
|
|
||||||
subJob.state.type == "failed" ||
|
|
||||||
subJob.state.type == "complete" ||
|
|
||||||
subJob.state.type == "draft" ||
|
|
||||||
subJob.state.type == "cancelled"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if (subJob.subJobId == this.subJobCancelId) {
|
|
||||||
const updatedSubJob = await printSubJobModel.findByIdAndUpdate(
|
|
||||||
subJob.id,
|
|
||||||
{ state: { type: "cancelled" } },
|
|
||||||
{ new: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.removePrinterSubJob(subJob.id);
|
const subJobs = printer.subJobs
|
||||||
// Notify clients of the update
|
|
||||||
this.socketManager.broadcast("notify_subjob_update", {
|
|
||||||
id: subJob.id,
|
|
||||||
state: { type: "cancelled" },
|
|
||||||
});
|
|
||||||
if (updatedSubJob) {
|
|
||||||
logger.debug(`Cancelled subjob ${updatedSubJob.id}`);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
logger.debug(
|
|
||||||
`Subjob ${subJob.id} is not in the queued job list. It must be printing or paused. Setting job and subjob for ${this.id}`,
|
|
||||||
);
|
|
||||||
this.currentSubJobId = subJob.id;
|
|
||||||
this.currentJobId = subJob.printJob;
|
|
||||||
await this.updateCurrentJobAndSubJobState();
|
|
||||||
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
|
||||||
this.id,
|
|
||||||
{
|
|
||||||
currentSubJob: this.currentSubJobId,
|
|
||||||
currentJob: this.currentJobId,
|
|
||||||
},
|
|
||||||
{ new: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!updatedPrinter) {
|
// If printer is not printing or paused, clear current job/subjob
|
||||||
logger.error(
|
if (!["printing", "paused"].includes(this.state.type)) {
|
||||||
`Printer with ID ${this.id} not found when setting job and subjob`,
|
if (this.currentSubJobId || this.currentJobId) {
|
||||||
);
|
logger.info(`Clearing current job/subjob for printer ${this.name} as state is ${this.state.type}`);
|
||||||
return;
|
await this.database.clearCurrentJob(this.id);
|
||||||
}
|
this.currentSubJobId = null;
|
||||||
|
this.currentJobId = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Printer is printing or paused - find the current job
|
||||||
|
// Sort subjobs by number property
|
||||||
|
const sortedSubJobs = subJobs.sort((a, b) => a.number - b.number);
|
||||||
|
|
||||||
logger.info(`Set job and subjob for ${this.id}`);
|
// Find subjobs that are in queued state
|
||||||
// If the status is failed or completed, remove the subjob from the printer's subJobs array
|
const queuedSubJobs = sortedSubJobs.filter(subJob =>
|
||||||
|
subJob.state.type === "queued" &&
|
||||||
|
this.queuedJobIds.includes(subJob.subJobId)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Find subjobs that are not in queued state but should be
|
||||||
|
const missingQueuedSubJobs = sortedSubJobs.filter(subJob =>
|
||||||
|
subJob.state.type === "queued" &&
|
||||||
|
!this.queuedJobIds.includes(subJob.subJobId)
|
||||||
|
);
|
||||||
|
|
||||||
|
// If we have missing queued jobs and printer is in standby, mark them as failed
|
||||||
|
if (missingQueuedSubJobs.length > 0 && this.state.type === "standby") {
|
||||||
|
logger.warn(`Found ${missingQueuedSubJobs.length} missing queued jobs for printer ${this.name} in standby state`);
|
||||||
|
for (const subJob of missingQueuedSubJobs) {
|
||||||
|
logger.info(`Marking missing queued subjob ${subJob.id} as failed`);
|
||||||
|
await this.database.updateSubJobState(subJob.id, { type: "failed" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
subJob.state.type === "failed" ||
|
// If we have a current subjob, verify it's still valid
|
||||||
subJob.state.type === "complete"
|
if (this.currentSubJobId) {
|
||||||
) {
|
const currentSubJob = sortedSubJobs.find(sj => sj.id === this.currentSubJobId);
|
||||||
await this.removePrinterSubJob(subJob.id);
|
if (!currentSubJob || !this.queuedJobIds.includes(currentSubJob.subJobId)) {
|
||||||
|
logger.info(`Current subjob ${this.currentSubJobId} is no longer valid, clearing it`);
|
||||||
|
await this.database.clearCurrentJob(this.id);
|
||||||
|
this.currentSubJobId = null;
|
||||||
|
this.currentJobId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have a current subjob but have queued jobs, find the first one
|
||||||
|
if (!this.currentSubJobId) {
|
||||||
|
const result = await this.database.setCurrentJobForPrinting(this.id, this.queuedJobIds);
|
||||||
|
if (result) {
|
||||||
|
logger.info(`Setting first queued subjob as current for printer ${this.name}`, result);
|
||||||
|
this.currentSubJobId = result.currentSubJob._id;
|
||||||
|
this.currentJobId = result.currentJob._id;
|
||||||
|
await this.database.updateSubJobState(this.currentSubJobId, this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update states for all subjobs
|
||||||
|
for (const subJob of subJobs) {
|
||||||
|
if (!this.queuedJobIds.includes(subJob.subJobId)) {
|
||||||
|
if (subJob.subJobId === this.subJobCancelId) {
|
||||||
|
logger.info(`Cancelling subjob ${subJob.id}`);
|
||||||
|
await this.database.updateSubJobState(subJob.id, { type: "cancelled" });
|
||||||
|
await this.database.removePrinterSubJob(this.id, subJob.id);
|
||||||
|
} else if (!["failed", "complete", "draft", "cancelled"].includes(subJob.state.type)) {
|
||||||
|
// Update the subjob state to match printer state
|
||||||
|
await this.database.updateSubJobState(subJob.id, this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (["failed", "complete", "cancelled"].includes(subJob.state.type)) {
|
||||||
|
logger.info(`Removing completed/failed/cancelled subjob ${subJob.id} from printer ${this.name}`);
|
||||||
|
await this.database.removePrinterSubJob(this.id, subJob.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(
|
logger.error(`Failed to update printer subjobs for ${this.id}:`, error);
|
||||||
`Failed to update job status in database (${this.name}):`,
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateCurrentJobAndSubJobState() {
|
|
||||||
try {
|
|
||||||
if (!this.currentJobId || !this.currentSubJobId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentJobState = { type: "unknown", progress: 0 };
|
|
||||||
this.currentSubJobState = { type: "unknown", progress: 0 };
|
|
||||||
|
|
||||||
// Get the current job
|
|
||||||
const currentJob = await printJobModel
|
|
||||||
.findById(this.currentJobId)
|
|
||||||
.populate("subJobs");
|
|
||||||
|
|
||||||
const jobLength = currentJob.subJobs.length;
|
|
||||||
|
|
||||||
let externalProgressSum = 0;
|
|
||||||
|
|
||||||
var printing = 0;
|
|
||||||
var paused = 0;
|
|
||||||
var complete = 0;
|
|
||||||
var failed = 0;
|
|
||||||
|
|
||||||
for (const subJob of currentJob.subJobs.filter(
|
|
||||||
(subJob) => subJob.id != this.currentSubJobId,
|
|
||||||
)) {
|
|
||||||
if (subJob.state.type === "printing") {
|
|
||||||
externalProgressSum = externalProgressSum + subJob.state.progress;
|
|
||||||
printing = printing + 1;
|
|
||||||
}
|
|
||||||
if (subJob.state.type === "paused") {
|
|
||||||
paused = paused + 1;
|
|
||||||
}
|
|
||||||
if (subJob.state.type === "complete") {
|
|
||||||
complete = complete + 1;
|
|
||||||
}
|
|
||||||
if (subJob.state.type === "failed") {
|
|
||||||
failed = failed + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.type === "printing") {
|
|
||||||
this.currentSubJobState.type = "printing";
|
|
||||||
this.currentSubJobState.progress = this.state.progress;
|
|
||||||
printing = printing + 1;
|
|
||||||
} else if (this.state.type === "paused") {
|
|
||||||
this.currentSubJobState.type = "paused";
|
|
||||||
paused = paused + 1;
|
|
||||||
} else if (this.state.type === "complete") {
|
|
||||||
this.currentSubJobState.type = "complete";
|
|
||||||
complete = complete + 1;
|
|
||||||
} else {
|
|
||||||
this.currentSubJobState.type = "failed";
|
|
||||||
failed = failed + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paused > 0) {
|
|
||||||
this.currentJobState.type = "paused";
|
|
||||||
} else if (printing > 0) {
|
|
||||||
this.currentJobState.type = "printing";
|
|
||||||
} else if (failed > 0) {
|
|
||||||
this.currentJobState.type = "failed";
|
|
||||||
} else if (complete == jobLength) {
|
|
||||||
this.currentJobState.type = "complete";
|
|
||||||
} else {
|
|
||||||
this.currentJobState.type = "queued";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.type === "printing") {
|
|
||||||
this.currentJobState.progress =
|
|
||||||
(externalProgressSum + complete + (this.state.progress || 0)) /
|
|
||||||
jobLength;
|
|
||||||
} else {
|
|
||||||
this.currentJobState.progress = externalProgressSum / jobLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentJob.state = this.currentJobState;
|
|
||||||
|
|
||||||
await currentJob.save();
|
|
||||||
|
|
||||||
this.socketManager.broadcast("notify_job_update", {
|
|
||||||
id: this.currentJobId,
|
|
||||||
state: this.currentJobState,
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
`Updated job status to ${this.currentJobState.type} (Progress: ${this.currentJobState.progress}) for ${this.currentSubJobId}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const updatedSubJob = await printSubJobModel.findByIdAndUpdate(
|
|
||||||
this.currentSubJobId,
|
|
||||||
{ state: this.currentSubJobState },
|
|
||||||
{ new: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!updatedSubJob) {
|
|
||||||
logger.error(
|
|
||||||
`Sub job with ID ${this.currentSubJobId} not found when updating status`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify clients of the update
|
|
||||||
this.socketManager.broadcast("notify_subjob_update", {
|
|
||||||
id: this.currentSubJobId,
|
|
||||||
state: this.currentSubJobState,
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
`Updated sub job status to ${this.currentSubJobState.type} (Progress: ${this.currentSubJobState.progress}) for ${this.currentSubJobId}`,
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`Error updating current job state:`, error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,7 +667,7 @@ export class PrinterClient {
|
|||||||
state: { type: "queued" },
|
state: { type: "queued" },
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
},
|
},
|
||||||
{ new: true },
|
{ new: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!updatedSubJob) {
|
if (!updatedSubJob) {
|
||||||
@ -745,17 +675,8 @@ export class PrinterClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the printer's subJobs array
|
// Update the printer's subJobs array
|
||||||
const printer = await printerModel.findById(this.id);
|
await this.database.addSubJobToPrinter(this.id, updatedSubJob._id);
|
||||||
if (printer) {
|
await this.database.updateSubJobState(subJob.id, { type: "queued" });
|
||||||
printer.subJobs.push(updatedSubJob._id);
|
|
||||||
await printer.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.socketManager.broadcast("notify_subjob_update", {
|
|
||||||
id: subJob.id,
|
|
||||||
subJobId: result.queued_jobs[result.queued_jobs.length - 1].job_id,
|
|
||||||
state: { type: "queued" },
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.info("Sub job deployed to printer:", this.id);
|
logger.info("Sub job deployed to printer:", this.id);
|
||||||
}
|
}
|
||||||
@ -786,4 +707,11 @@ export class PrinterClient {
|
|||||||
logger.error(`Error canceling sub job ${subJobId}:`, error);
|
logger.error(`Error canceling sub job ${subJobId}:`, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async loadFilamentStock(filamentStockId) {
|
||||||
|
this.currentFilamentStockId = filamentStockId;
|
||||||
|
const result = await this.database.setCurrentFilamentStock(this.id, this.currentFilamentStockId);
|
||||||
|
this.currentFilamentStockDensity = result.filament.density
|
||||||
|
await this.database.removeAlerts(this.id, {type: 'loadFilamentStock'})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,10 @@ export class PrinterManager {
|
|||||||
async initializePrinterConnections() {
|
async initializePrinterConnections() {
|
||||||
try {
|
try {
|
||||||
// Get all printers from the database
|
// Get all printers from the database
|
||||||
const printers = await printerModel.find({});
|
const printers = await printerModel.find({}).populate({ path: "currentFilamentStock",
|
||||||
|
populate: {
|
||||||
|
path: "filament",
|
||||||
|
},});
|
||||||
|
|
||||||
for (const printer of printers) {
|
for (const printer of printers) {
|
||||||
await this.connectToPrinter(printer);
|
await this.connectToPrinter(printer);
|
||||||
@ -46,7 +49,7 @@ export class PrinterManager {
|
|||||||
// Connect to the printer
|
// Connect to the printer
|
||||||
await printerClientConnection.connect();
|
await printerClientConnection.connect();
|
||||||
|
|
||||||
logger.info(`Connected to printer: ${printer.printerName} (${printer.id})`);
|
logger.info(`Connected to printer: ${printer.name} (${printer.id})`);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,441 +20,510 @@ export class SocketClient {
|
|||||||
this.activeSubscriptions = new Map();
|
this.activeSubscriptions = new Map();
|
||||||
this.scanner = new WebSocketScanner({ maxThreads: 50 });
|
this.scanner = new WebSocketScanner({ maxThreads: 50 });
|
||||||
|
|
||||||
this.socket.on("bridge.list_printers", (data) => {});
|
this.setupSocketEventHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
this.socket.on("bridge.add_printer", (data, callback) => {});
|
setupSocketEventHandlers() {
|
||||||
|
this.socket.on("bridge.list_printers", this.handleListPrinters.bind(this));
|
||||||
|
this.socket.on("bridge.add_printer", this.handleAddPrinter.bind(this));
|
||||||
|
this.socket.on("bridge.remove_printer", this.handleRemovePrinter.bind(this));
|
||||||
|
this.socket.on("bridge.update_printer", this.handleUpdatePrinter.bind(this));
|
||||||
|
this.socket.on("bridge.scan_network.start", this.handleScanNetworkStart.bind(this));
|
||||||
|
this.socket.on("bridge.scan_network.stop", this.handleScanNetworkStop.bind(this));
|
||||||
|
this.socket.on("printer.objects.subscribe", this.handlePrinterObjectsSubscribe.bind(this));
|
||||||
|
this.socket.on("printer.objects.unsubscribe", this.handlePrinterObjectsUnsubscribe.bind(this));
|
||||||
|
this.socket.on("printer.gcode.script", this.handleGcodeScript.bind(this));
|
||||||
|
this.socket.on("printer.objects.query", this.handlePrinterObjectsQuery.bind(this));
|
||||||
|
this.socket.on("printer.emergency_stop", this.handleEmergencyStop.bind(this));
|
||||||
|
this.socket.on("printer.firmware_restart", this.handleFirmwareRestart.bind(this));
|
||||||
|
this.socket.on("printer.restart", this.handlePrinterRestart.bind(this));
|
||||||
|
this.socket.on("server.job_queue.status", this.handleJobQueueStatus.bind(this));
|
||||||
|
this.socket.on("server.job_queue.deploy", this.handleJobQueueDeploy.bind(this));
|
||||||
|
this.socket.on("printer.print.resume", this.handlePrintResume.bind(this));
|
||||||
|
this.socket.on("server.job_queue.cancel", this.handleJobQueueCancel.bind(this));
|
||||||
|
this.socket.on("printer.print.cancel", this.handlePrintCancel.bind(this));
|
||||||
|
this.socket.on("printer.print.pause", this.handlePrintPause.bind(this));
|
||||||
|
this.socket.on("server.job_queue.pause", this.handleJobQueuePause.bind(this));
|
||||||
|
this.socket.on("server.job_queue.start", this.handleJobQueueStart.bind(this));
|
||||||
|
this.socket.on("printer.filamentstock.load", this.handleFilamentStockLoad.bind(this));
|
||||||
|
this.socket.on("disconnect", this.handleDisconnect.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
this.socket.on("bridge.remove_printer", (data, callback) => {});
|
handleListPrinters(data) {
|
||||||
|
// Implementation for bridge.list_printers
|
||||||
|
}
|
||||||
|
|
||||||
this.socket.on("bridge.update_printer", (data, callback) => {});
|
handleAddPrinter(data, callback) {
|
||||||
|
// Implementation for bridge.add_printer
|
||||||
|
}
|
||||||
|
|
||||||
this.socket.on("bridge.scan_network.start", (data, callback) => {
|
handleRemovePrinter(data, callback) {
|
||||||
if (this.scanner.scanning == false) {
|
// Implementation for bridge.remove_printer
|
||||||
try {
|
}
|
||||||
this.scanner = new WebSocketScanner({ maxThreads: 50 });
|
|
||||||
// Listen for found services
|
|
||||||
this.scanner.on("serviceFound", (data) => {
|
|
||||||
logger.info(
|
|
||||||
`Found websocket service at ${data.hostname} (${data.ip})`,
|
|
||||||
);
|
|
||||||
this.socket.emit("notify_scan_network_found", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen for scan progress
|
handleUpdatePrinter(data, callback) {
|
||||||
this.scanner.on("scanProgress", ({ currentIP, progress }) => {
|
// Implementation for bridge.update_printer
|
||||||
logger.info(
|
}
|
||||||
`Scanning ${currentIP} (${progress.toFixed(2)}% complete)`,
|
|
||||||
);
|
|
||||||
this.socket.emit("notify_scan_network_progress", {
|
|
||||||
currentIP: currentIP,
|
|
||||||
progress: progress,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start scanning on port
|
async handleScanNetworkStart(data, callback) {
|
||||||
|
if (this.scanner.scanning == false) {
|
||||||
|
try {
|
||||||
|
this.scanner = new WebSocketScanner({ maxThreads: 50 });
|
||||||
|
// Listen for found services
|
||||||
|
this.scanner.on("serviceFound", (data) => {
|
||||||
logger.info(
|
logger.info(
|
||||||
"Scanning network for websocket services on port:",
|
`Found websocket service at ${data.hostname} (${data.ip})`,
|
||||||
data?.port || 7125,
|
|
||||||
"using protocol:",
|
|
||||||
data?.protocol || "ws",
|
|
||||||
);
|
);
|
||||||
this.scanner
|
this.socket.emit("notify_scan_network_found", data);
|
||||||
.scanNetwork(data?.port || 7125, data?.protocol || "ws")
|
});
|
||||||
.then((foundServices) => {
|
|
||||||
logger.info("Scan complete. Found services:", foundServices);
|
|
||||||
this.socket.emit("notify_scan_network_complete", foundServices);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
logger.error("Scan error:", error);
|
|
||||||
this.socket.emit("notify_scan_network_complete", false);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
logger.error("Scan error:", error);
|
|
||||||
this.socket.emit("notify_scan_network_complete", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("bridge.scan_network.stop", (callback) => {
|
// Listen for scan progress
|
||||||
if (this.scanner.scanning == true) {
|
this.scanner.on("scanProgress", ({ currentIP, progress }) => {
|
||||||
logger.info("Stopping network scan");
|
logger.info(
|
||||||
this.scanner.removeAllListeners("serviceFound");
|
`Scanning ${currentIP} (${progress.toFixed(2)}% complete)`,
|
||||||
this.scanner.removeAllListeners("scanProgress");
|
|
||||||
this.scanner.removeAllListeners("scanComplete");
|
|
||||||
this.scanner.stopScan();
|
|
||||||
callback(true);
|
|
||||||
} else {
|
|
||||||
logger.info("Scan not in progress");
|
|
||||||
callback(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle printer object subscriptions
|
|
||||||
this.socket.on("printer.objects.subscribe", async (data, callback) => {
|
|
||||||
logger.debug("Received printer.objects.subscribe event:", data);
|
|
||||||
try {
|
|
||||||
if (data && data.printerId) {
|
|
||||||
const printerId = data.printerId;
|
|
||||||
|
|
||||||
// Get existing subscription or create new one
|
|
||||||
const existingSubscription =
|
|
||||||
this.activeSubscriptions.get(printerId) || {};
|
|
||||||
|
|
||||||
// Merge the new subscription data with existing data
|
|
||||||
const mergedSubscription = {
|
|
||||||
...existingSubscription.objects,
|
|
||||||
...data.objects,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.activeSubscriptions.set(printerId, mergedSubscription);
|
|
||||||
|
|
||||||
logger.trace("Merged subscription:", mergedSubscription);
|
|
||||||
const result = await this.printerManager.updateSubscription(
|
|
||||||
printerId,
|
|
||||||
socket.id,
|
|
||||||
mergedSubscription,
|
|
||||||
);
|
);
|
||||||
|
this.socket.emit("notify_scan_network_progress", {
|
||||||
if (callback) {
|
currentIP: currentIP,
|
||||||
callback(result);
|
progress: progress,
|
||||||
}
|
});
|
||||||
} else {
|
|
||||||
logger.error("Missing Printer ID in subscription request");
|
|
||||||
if (callback) {
|
|
||||||
callback({ error: "Missing Printer ID" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.error("Error processing subscription request:", e);
|
|
||||||
if (callback) {
|
|
||||||
callback({ error: e.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("printer.objects.unsubscribe", async (data, callback) => {
|
|
||||||
logger.debug("Received printer.objects.unsubscribe event:", data);
|
|
||||||
try {
|
|
||||||
if (data && data.printerId) {
|
|
||||||
const printerId = data.printerId;
|
|
||||||
const existingSubscription = this.activeSubscriptions.get(printerId);
|
|
||||||
|
|
||||||
if (existingSubscription) {
|
|
||||||
// Create a new objects object without the unsubscribed objects
|
|
||||||
const remainingObjects = { ...existingSubscription.objects };
|
|
||||||
if (data.objects) {
|
|
||||||
for (const key of Object.keys(data.objects)) {
|
|
||||||
delete remainingObjects[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are no remaining objects, remove the entire subscription
|
|
||||||
if (Object.keys(remainingObjects).length === 0) {
|
|
||||||
this.activeSubscriptions.delete(printerId);
|
|
||||||
|
|
||||||
// Send subscribe command with updated subscription
|
|
||||||
const result = await this.printerManager.updateSubscription(
|
|
||||||
printerId,
|
|
||||||
socket.id,
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
if (callback) {
|
|
||||||
callback(result);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Update the subscription with remaining objects
|
|
||||||
const updatedSubscription = {
|
|
||||||
printerId: printerId,
|
|
||||||
objects: remainingObjects,
|
|
||||||
};
|
|
||||||
this.activeSubscriptions.set(printerId, updatedSubscription);
|
|
||||||
|
|
||||||
// Send subscribe command with updated subscription
|
|
||||||
const result = await this.printerManager.updateSubscription(
|
|
||||||
printerId,
|
|
||||||
socket.id,
|
|
||||||
updatedSubscription,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.warn(
|
|
||||||
"No existing subscription found for printer:",
|
|
||||||
printerId,
|
|
||||||
);
|
|
||||||
if (callback) {
|
|
||||||
callback({ success: true, message: "No subscription found" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.error("Missing Printer ID in unsubscribe request");
|
|
||||||
if (callback) {
|
|
||||||
callback({ error: "Missing Printer ID" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.error("Error processing unsubscribe request:", e);
|
|
||||||
if (callback) {
|
|
||||||
callback({ error: e.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("printer.gcode.script", async (data, callback) => {
|
|
||||||
logger.debug("Received printer.gcode.script event:", data);
|
|
||||||
try {
|
|
||||||
const result = await this.printerManager.processPrinterCommand({
|
|
||||||
method: "printer.gcode.script",
|
|
||||||
params: data,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (callback) {
|
// Start scanning on port
|
||||||
callback(result);
|
logger.info(
|
||||||
}
|
"Scanning network for websocket services on port:",
|
||||||
} catch (e) {
|
data?.port || 7125,
|
||||||
logger.error("Error processing gcode script request:", e);
|
"using protocol:",
|
||||||
if (callback) {
|
data?.protocol || "ws",
|
||||||
callback({ error: e.message });
|
);
|
||||||
}
|
this.scanner
|
||||||
|
.scanNetwork(data?.port || 7125, data?.protocol || "ws")
|
||||||
|
.then((foundServices) => {
|
||||||
|
logger.info("Scan complete. Found services:", foundServices);
|
||||||
|
this.socket.emit("notify_scan_network_complete", foundServices);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logger.error("Scan error:", error);
|
||||||
|
this.socket.emit("notify_scan_network_complete", false);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Scan error:", error);
|
||||||
|
this.socket.emit("notify_scan_network_complete", false);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.socket.on("printer.objects.query", async (data, callback) => {
|
handleScanNetworkStop(callback) {
|
||||||
logger.debug("Received printer.objects.query event:", data);
|
if (this.scanner.scanning == true) {
|
||||||
try {
|
logger.info("Stopping network scan");
|
||||||
const result = await this.printerManager.processPrinterCommand({
|
this.scanner.removeAllListeners("serviceFound");
|
||||||
method: "printer.objects.query",
|
this.scanner.removeAllListeners("scanProgress");
|
||||||
params: data,
|
this.scanner.removeAllListeners("scanComplete");
|
||||||
});
|
this.scanner.stopScan();
|
||||||
|
callback(true);
|
||||||
|
} else {
|
||||||
|
logger.info("Scan not in progress");
|
||||||
|
callback(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (callback) {
|
async handlePrinterObjectsSubscribe(data, callback) {
|
||||||
callback(result);
|
logger.debug("Received printer.objects.subscribe event:", data);
|
||||||
}
|
try {
|
||||||
} catch (e) {
|
if (data && data.printerId) {
|
||||||
logger.error("Error processing printer objects query request:", e);
|
const printerId = data.printerId;
|
||||||
if (callback) {
|
|
||||||
callback({ error: e.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("printer.emergency_stop", async (data, callback) => {
|
// Get existing subscription or create new one
|
||||||
logger.debug("Received printer.gcode.script event:", data);
|
const existingSubscription =
|
||||||
try {
|
this.activeSubscriptions.get(printerId) || {};
|
||||||
const result = await this.printerManager.processPrinterCommand({
|
|
||||||
method: "printer.emergency_stop",
|
|
||||||
params: data,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (callback) {
|
// Merge the new subscription data with existing data
|
||||||
callback(result);
|
const mergedSubscription = {
|
||||||
}
|
...existingSubscription.objects,
|
||||||
} catch (e) {
|
...data.objects,
|
||||||
logger.error("Error processing gcode script request:", e);
|
};
|
||||||
if (callback) {
|
|
||||||
callback({ error: e.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("printer.firmware_restart", async (data, callback) => {
|
this.activeSubscriptions.set(printerId, mergedSubscription);
|
||||||
logger.debug("Received printer.firmware_restart event:", data);
|
|
||||||
try {
|
|
||||||
const result = await this.printerManager.processPrinterCommand({
|
|
||||||
method: "printer.firmware_restart",
|
|
||||||
params: data,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (callback) {
|
logger.trace("Merged subscription:", mergedSubscription);
|
||||||
callback(result);
|
const result = await this.printerManager.updateSubscription(
|
||||||
}
|
printerId,
|
||||||
} catch (e) {
|
this.socket.id,
|
||||||
logger.error("Error processing firmware restart request:", e);
|
mergedSubscription,
|
||||||
if (callback) {
|
|
||||||
callback({ error: e.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("printer.restart", async (data, callback) => {
|
|
||||||
logger.debug("Received printer.restart event:", data);
|
|
||||||
try {
|
|
||||||
const result = await this.printerManager.processPrinterCommand({
|
|
||||||
method: "printer.restart",
|
|
||||||
params: data,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback(result);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.error("Error processing printer restart request:", e);
|
|
||||||
if (callback) {
|
|
||||||
callback({ error: e.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("server.job_queue.status", async (data, callback) => {
|
|
||||||
logger.debug("Received server.job_queue.status event:", data);
|
|
||||||
try {
|
|
||||||
const result = await this.printerManager.processPrinterCommand({
|
|
||||||
method: "server.job_queue.status",
|
|
||||||
params: data,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback(result);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.error("Error processing job queue status request:", e);
|
|
||||||
if (callback) {
|
|
||||||
callback({ error: e.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("server.job_queue.deploy", async (data, callback) => {
|
|
||||||
logger.debug("Received server.job_queue.deploy event:", data);
|
|
||||||
try {
|
|
||||||
if (!data || !data.printJobId) {
|
|
||||||
throw new Error("Missing required print job ID");
|
|
||||||
}
|
|
||||||
// Deploy the print job to all printers
|
|
||||||
const result = await this.printerManager.deployPrintJob(
|
|
||||||
data.printJobId,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(result);
|
callback(result);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} else {
|
||||||
logger.error("Error processing job queue deploy request:", e);
|
logger.error("Missing Printer ID in subscription request");
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback({ error: e.message });
|
callback({ error: "Missing Printer ID" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
} catch (e) {
|
||||||
|
logger.error("Error processing subscription request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.socket.on("printer.print.resume", async (data, callback) => {
|
async handlePrinterObjectsUnsubscribe(data, callback) {
|
||||||
logger.debug("Received printer.print.resume event:", data);
|
logger.debug("Received printer.objects.unsubscribe event:", data);
|
||||||
try {
|
try {
|
||||||
const result = await this.printerManager.processPrinterCommand({
|
if (data && data.printerId) {
|
||||||
method: "printer.print.resume",
|
const printerId = data.printerId;
|
||||||
params: data,
|
const existingSubscription = this.activeSubscriptions.get(printerId);
|
||||||
});
|
|
||||||
|
|
||||||
if (callback) {
|
if (existingSubscription) {
|
||||||
callback(result);
|
// Create a new objects object without the unsubscribed objects
|
||||||
|
const remainingObjects = { ...existingSubscription };
|
||||||
|
if (data.objects) {
|
||||||
|
for (const key of Object.keys(data.objects)) {
|
||||||
|
delete remainingObjects[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("remainingObjects", remainingObjects)
|
||||||
|
console.log("existingSubscription", existingSubscription)
|
||||||
|
console.log("unsubscribe", data.objects)
|
||||||
|
|
||||||
|
// If there are no remaining objects, remove the entire subscription
|
||||||
|
if (Object.keys(remainingObjects).length === 0) {
|
||||||
|
this.activeSubscriptions.delete(printerId);
|
||||||
|
|
||||||
|
logger.warn("Removing entire subscription")
|
||||||
|
|
||||||
|
// Send subscribe command with updated subscription
|
||||||
|
const result = await this.printerManager.updateSubscription(
|
||||||
|
printerId,
|
||||||
|
this.socket.id,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.activeSubscriptions.set(printerId, remainingObjects);
|
||||||
|
|
||||||
|
logger.warn(remainingObjects)
|
||||||
|
|
||||||
|
// Send subscribe command with updated subscription
|
||||||
|
const result = await this.printerManager.updateSubscription(
|
||||||
|
printerId,
|
||||||
|
this.socket.id,
|
||||||
|
remainingObjects,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn(
|
||||||
|
"No existing subscription found for printer:",
|
||||||
|
printerId,
|
||||||
|
);
|
||||||
|
if (callback) {
|
||||||
|
callback({ success: true, message: "No subscription found" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} else {
|
||||||
logger.error("Error processing print resume request:", e);
|
logger.error("Missing Printer ID in unsubscribe request");
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback({ error: e.message });
|
callback({ error: "Missing Printer ID" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
} catch (e) {
|
||||||
|
logger.error("Error processing unsubscribe request:", e);
|
||||||
this.socket.on("server.job_queue.cancel", async (data, callback) => {
|
if (callback) {
|
||||||
logger.debug("Received server.job_queue.cancel event:", data);
|
callback({ error: e.message });
|
||||||
try {
|
|
||||||
if (!data || !data.subJobId) {
|
|
||||||
throw new Error("Missing required sub job ID");
|
|
||||||
}
|
|
||||||
const result = await this.printerManager.cancelSubJob(data.subJobId);
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback(result);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.error("Error processing job queue delete job request:", e);
|
|
||||||
if (callback) {
|
|
||||||
callback({ error: e.message });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.socket.on("printer.print.cancel", async (data, callback) => {
|
async handleGcodeScript(data, callback) {
|
||||||
logger.debug("Received printer.print.cancel event:", data);
|
logger.debug("Received printer.gcode.script event:", data);
|
||||||
try {
|
try {
|
||||||
const result = await this.printerManager.processPrinterCommand({
|
const result = await this.printerManager.processPrinterCommand({
|
||||||
method: "printer.print.cancel",
|
method: "printer.gcode.script",
|
||||||
params: data,
|
params: data,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(result);
|
callback(result);
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.error("Error processing print cancel request:", e);
|
|
||||||
if (callback) {
|
|
||||||
callback({ error: e.message });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
} catch (e) {
|
||||||
|
logger.error("Error processing gcode script request:", e);
|
||||||
this.socket.on("printer.print.pause", async (data, callback) => {
|
if (callback) {
|
||||||
logger.debug("Received printer.print.pause event:", data);
|
callback({ error: e.message });
|
||||||
try {
|
|
||||||
const result = await this.printerManager.processPrinterCommand({
|
|
||||||
method: "printer.print.pause",
|
|
||||||
params: data,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback(result);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.error("Error processing print pause request:", e);
|
|
||||||
if (callback) {
|
|
||||||
callback({ error: e.message });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.socket.on("server.job_queue.pause", async (data, callback) => {
|
async handlePrinterObjectsQuery(data, callback) {
|
||||||
logger.debug("Received server.job_queue.pause event:", data);
|
logger.debug("Received printer.objects.query event:", data);
|
||||||
try {
|
try {
|
||||||
const result = await this.printerManager.processPrinterCommand({
|
const result = await this.printerManager.processPrinterCommand({
|
||||||
method: "server.job_queue.pause",
|
method: "printer.objects.query",
|
||||||
params: data,
|
params: data,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(result);
|
callback(result);
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.error("Error processing job queue pause request:", e);
|
|
||||||
if (callback) {
|
|
||||||
callback({ error: e.message });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
} catch (e) {
|
||||||
|
logger.error("Error processing printer objects query request:", e);
|
||||||
this.socket.on("server.job_queue.start", async (data, callback) => {
|
if (callback) {
|
||||||
logger.debug("Received server.job_queue.start event:", data);
|
callback({ error: e.message });
|
||||||
try {
|
|
||||||
const result = await this.printerManager.processPrinterCommand({
|
|
||||||
method: "server.job_queue.start",
|
|
||||||
params: data,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback(result);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.error("Error processing job queue start request:", e);
|
|
||||||
if (callback) {
|
|
||||||
callback({ error: e.message });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.socket.on("disconnect", () => {
|
async handleEmergencyStop(data, callback) {
|
||||||
logger.info("External client disconnected:", socket.user?.username);
|
logger.debug("Received printer.gcode.script event:", data);
|
||||||
});
|
try {
|
||||||
|
const result = await this.printerManager.processPrinterCommand({
|
||||||
|
method: "printer.emergency_stop",
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Error processing gcode script request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleFirmwareRestart(data, callback) {
|
||||||
|
logger.debug("Received printer.firmware_restart event:", data);
|
||||||
|
try {
|
||||||
|
const result = await this.printerManager.processPrinterCommand({
|
||||||
|
method: "printer.firmware_restart",
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Error processing firmware restart request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handlePrinterRestart(data, callback) {
|
||||||
|
logger.debug("Received printer.restart event:", data);
|
||||||
|
try {
|
||||||
|
const result = await this.printerManager.processPrinterCommand({
|
||||||
|
method: "printer.restart",
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Error processing printer restart request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleJobQueueStatus(data, callback) {
|
||||||
|
logger.debug("Received server.job_queue.status event:", data);
|
||||||
|
try {
|
||||||
|
const result = await this.printerManager.processPrinterCommand({
|
||||||
|
method: "server.job_queue.status",
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Error processing job queue status request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleJobQueueDeploy(data, callback) {
|
||||||
|
logger.debug("Received server.job_queue.deploy event:", data);
|
||||||
|
try {
|
||||||
|
if (!data || !data.printJobId) {
|
||||||
|
throw new Error("Missing required print job ID");
|
||||||
|
}
|
||||||
|
// Deploy the print job to all printers
|
||||||
|
const result = await this.printerManager.deployPrintJob(
|
||||||
|
data.printJobId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Error processing job queue deploy request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handlePrintResume(data, callback) {
|
||||||
|
logger.debug("Received printer.print.resume event:", data);
|
||||||
|
try {
|
||||||
|
const result = await this.printerManager.processPrinterCommand({
|
||||||
|
method: "printer.print.resume",
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Error processing print resume request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleJobQueueCancel(data, callback) {
|
||||||
|
logger.debug("Received server.job_queue.cancel event:", data);
|
||||||
|
try {
|
||||||
|
if (!data || !data.subJobId) {
|
||||||
|
throw new Error("Missing required sub job ID");
|
||||||
|
}
|
||||||
|
const result = await this.printerManager.cancelSubJob(data.subJobId);
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Error processing job queue delete job request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handlePrintCancel(data, callback) {
|
||||||
|
logger.debug("Received printer.print.cancel event:", data);
|
||||||
|
try {
|
||||||
|
const result = await this.printerManager.processPrinterCommand({
|
||||||
|
method: "printer.print.cancel",
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Error processing print cancel request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handlePrintPause(data, callback) {
|
||||||
|
logger.debug("Received printer.print.pause event:", data);
|
||||||
|
try {
|
||||||
|
const result = await this.printerManager.processPrinterCommand({
|
||||||
|
method: "printer.print.pause",
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Error processing print pause request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleJobQueuePause(data, callback) {
|
||||||
|
logger.debug("Received server.job_queue.pause event:", data);
|
||||||
|
try {
|
||||||
|
const result = await this.printerManager.processPrinterCommand({
|
||||||
|
method: "server.job_queue.pause",
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Error processing job queue pause request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleJobQueueStart(data, callback) {
|
||||||
|
logger.debug("Received server.job_queue.start event:", data);
|
||||||
|
try {
|
||||||
|
const result = await this.printerManager.processPrinterCommand({
|
||||||
|
method: "server.job_queue.start",
|
||||||
|
params: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Error processing job queue start request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleFilamentStockLoad(data, callback) {
|
||||||
|
logger.debug("Received printer.filamentstock.load event:", data);
|
||||||
|
try {
|
||||||
|
if (!data || !data.printerId) {
|
||||||
|
throw new Error("Missing required printer ID");
|
||||||
|
}
|
||||||
|
if (!data || !data.filamentStockId) {
|
||||||
|
throw new Error("Missing required filament stock ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the printer client
|
||||||
|
const printerClient = this.printerManager.getPrinterClient(data.printerId);
|
||||||
|
if (!printerClient) {
|
||||||
|
throw new Error(`Printer with ID ${data.printerId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the filament stock
|
||||||
|
const result = await printerClient.loadFilamentStock(data.filamentStockId);
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(result);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("Error processing filament load request:", e);
|
||||||
|
if (callback) {
|
||||||
|
callback({ error: e.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDisconnect() {
|
||||||
|
logger.info("External client disconnected:", this.socket.user?.username);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user