Added CI Support and other things.
This commit is contained in:
parent
2ac65ec717
commit
90d031c5fe
45
Jenkinsfile
vendored
Normal file
45
Jenkinsfile
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
pipeline {
|
||||||
|
agent {
|
||||||
|
docker {
|
||||||
|
image 'node:20-alpine'
|
||||||
|
args '-v /var/run/docker.sock:/var/run/docker.sock'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
environment {
|
||||||
|
NODE_ENV = 'production'
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Install Dependencies') {
|
||||||
|
steps {
|
||||||
|
sh 'npm ci'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Build') {
|
||||||
|
steps {
|
||||||
|
sh 'npm run build'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Deploy') {
|
||||||
|
steps {
|
||||||
|
echo 'Deploying application...'
|
||||||
|
// Add your deployment steps here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
cleanWs()
|
||||||
|
}
|
||||||
|
success {
|
||||||
|
echo 'Pipeline completed successfully!'
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
echo 'Pipeline failed!'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"server": {
|
"server": {
|
||||||
"port": 8081,
|
"port": 8081,
|
||||||
"logLevel": "debug"
|
"logLevel": "info"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
|||||||
3543
package-lock.json
generated
3543
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,6 @@
|
|||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
|
||||||
"start": "node src/index.js",
|
"start": "node src/index.js",
|
||||||
"dev": "nodemon src/index.js"
|
"dev": "nodemon src/index.js"
|
||||||
},
|
},
|
||||||
@ -22,6 +21,8 @@
|
|||||||
"ws": "^8.18.1"
|
"ws": "^8.18.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^3.1.9"
|
"jest": "^29.7.0",
|
||||||
|
"nodemon": "^3.1.9",
|
||||||
|
"supertest": "^6.3.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,13 +13,7 @@ const filamentStockSchema = new Schema(
|
|||||||
currentGrossWeight: { type: Number, required: true },
|
currentGrossWeight: { type: Number, required: true },
|
||||||
currentNetWeight: { type: Number, required: true },
|
currentNetWeight: { type: Number, required: true },
|
||||||
filament: { type: mongoose.Schema.Types.ObjectId, ref: "Filament" },
|
filament: { type: mongoose.Schema.Types.ObjectId, ref: "Filament" },
|
||||||
stockEvents: [{
|
stockEvents: [{ type: mongoose.Schema.Types.ObjectId, ref: "StockEvent" }]
|
||||||
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 },
|
{ timestamps: true },
|
||||||
);
|
);
|
||||||
|
|||||||
@ -17,6 +17,7 @@ const alertSchema = new Schema(
|
|||||||
{
|
{
|
||||||
priority: { type: String, required: true }, // order to show
|
priority: { type: String, required: true }, // order to show
|
||||||
type: { type: String, required: true }, // selectFilament, error, info, message,
|
type: { type: String, required: true }, // selectFilament, error, info, message,
|
||||||
|
message: { type: String, required: false }
|
||||||
},
|
},
|
||||||
{ timestamps: true, _id: false }
|
{ timestamps: true, _id: false }
|
||||||
);
|
);
|
||||||
|
|||||||
@ -13,6 +13,7 @@ const printJobSchema = new mongoose.Schema({
|
|||||||
createdAt: { required: true, type: Date },
|
createdAt: { required: true, type: Date },
|
||||||
updatedAt: { required: true, type: Date },
|
updatedAt: { required: true, type: Date },
|
||||||
startedAt: { required: false, type: Date },
|
startedAt: { required: false, type: Date },
|
||||||
|
finishedAt: { required: false, type: Date },
|
||||||
gcodeFile: {
|
gcodeFile: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: "GCodeFile",
|
ref: "GCodeFile",
|
||||||
|
|||||||
@ -36,7 +36,9 @@ const printSubJobSchema = new mongoose.Schema({
|
|||||||
updatedAt: {
|
updatedAt: {
|
||||||
type: Date,
|
type: Date,
|
||||||
default: Date.now
|
default: Date.now
|
||||||
}
|
},
|
||||||
|
startedAt: { required: false, type: Date },
|
||||||
|
finishedAt: { required: false, type: Date },
|
||||||
});
|
});
|
||||||
|
|
||||||
printSubJobSchema.virtual("id").get(function () {
|
printSubJobSchema.virtual("id").get(function () {
|
||||||
|
|||||||
25
src/database/stockevent.schema.js
Normal file
25
src/database/stockevent.schema.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
const { Schema } = mongoose;
|
||||||
|
|
||||||
|
const stockEventSchema = new Schema(
|
||||||
|
{
|
||||||
|
type: { type: String, required: true },
|
||||||
|
value: { type: Number, required: true },
|
||||||
|
subJob: { type: Schema.Types.ObjectId, ref: "PrintSubJob", required: false },
|
||||||
|
job: { type: Schema.Types.ObjectId, ref: "PrintJob", required: false },
|
||||||
|
filamentStock: { type: Schema.Types.ObjectId, ref: "FilamentStock", required: true },
|
||||||
|
timestamp: { type: Date, default: Date.now }
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add virtual id getter
|
||||||
|
stockEventSchema.virtual("id").get(function () {
|
||||||
|
return this._id.toHexString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure JSON serialization to include virtuals
|
||||||
|
stockEventSchema.set("toJSON", { virtuals: true });
|
||||||
|
|
||||||
|
// Create and export the model
|
||||||
|
export const stockEventModel = mongoose.model("StockEvent", stockEventSchema);
|
||||||
@ -4,6 +4,7 @@ import { printSubJobModel } from "../database/printsubjob.schema.js";
|
|||||||
import { gcodeFileModel } from "../database/gcodefile.schema.js"
|
import { gcodeFileModel } from "../database/gcodefile.schema.js"
|
||||||
import { filamentStockModel } from "../database/filamentstock.schema.js";
|
import { filamentStockModel } from "../database/filamentstock.schema.js";
|
||||||
import { filamentModel } from "../database/filament.schema.js";
|
import { filamentModel } from "../database/filament.schema.js";
|
||||||
|
import { stockEventModel } from "../database/stockevent.schema.js";
|
||||||
import log4js from "log4js";
|
import log4js from "log4js";
|
||||||
import { loadConfig } from "../config.js";
|
import { loadConfig } from "../config.js";
|
||||||
|
|
||||||
@ -11,9 +12,52 @@ const config = loadConfig();
|
|||||||
const logger = log4js.getLogger("Printer Database");
|
const logger = log4js.getLogger("Printer Database");
|
||||||
logger.level = config.server.logLevel;
|
logger.level = config.server.logLevel;
|
||||||
|
|
||||||
|
// Debounce utility for rate limiting database updates
|
||||||
|
class Debouncer {
|
||||||
|
constructor(delay) {
|
||||||
|
this.delay = delay;
|
||||||
|
this.timeouts = new Map();
|
||||||
|
this.pendingUpdates = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
debounce(key, fn) {
|
||||||
|
// If this is the first call for this key, execute immediately
|
||||||
|
if (!this.timeouts.has(key)) {
|
||||||
|
this.timeouts.set(key, true);
|
||||||
|
return fn().finally(() => {
|
||||||
|
// After the first call completes, set up the delayed call
|
||||||
|
setTimeout(() => {
|
||||||
|
this.timeouts.delete(key);
|
||||||
|
// If there's a pending update, execute it
|
||||||
|
if (this.pendingUpdates.has(key)) {
|
||||||
|
const pendingFn = this.pendingUpdates.get(key);
|
||||||
|
this.pendingUpdates.delete(key);
|
||||||
|
pendingFn();
|
||||||
|
}
|
||||||
|
}, this.delay);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// If there's already a call in progress, store this one as pending
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.pendingUpdates.set(key, async () => {
|
||||||
|
try {
|
||||||
|
const result = await fn();
|
||||||
|
resolve(result);
|
||||||
|
} catch (error) {
|
||||||
|
resolve(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class PrinterDatabase {
|
export class PrinterDatabase {
|
||||||
constructor(socketManager) {
|
constructor(socketManager) {
|
||||||
this.socketManager = socketManager;
|
this.socketManager = socketManager;
|
||||||
|
this.debouncer = new Debouncer(2000); // 2 second delay
|
||||||
|
this.filamentStock = null; // Store current filament stock
|
||||||
|
this.existingEvent = null; // Store existing stock event
|
||||||
logger.info("Initialized PrinterDatabase with socket manager");
|
logger.info("Initialized PrinterDatabase with socket manager");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,48 +80,50 @@ export class PrinterDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updatePrinterState(printerId, state, online) {
|
async updatePrinterState(printerId, state, online) {
|
||||||
try {
|
// Broadcast immediately
|
||||||
logger.debug(`Updating printer state for ${printerId}:`, { state, online });
|
logger.debug(`Broadcasting printer state update for ${printerId}:`, { state, online });
|
||||||
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
|
_id: printerId,
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
|
||||||
if (state.type === "printing" && state.progress === undefined) {
|
return this.debouncer.debounce(`printer_${printerId}`, async () => {
|
||||||
logger.debug(`Setting default progress for printing state on printer ${printerId}`);
|
try {
|
||||||
state.progress = 0;
|
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
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedPrinter;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to update printer state for ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
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) {
|
async clearCurrentJob(printerId) {
|
||||||
try {
|
try {
|
||||||
logger.warn(`Clearing current job for printer ${printerId}`);
|
|
||||||
|
|
||||||
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
||||||
printerId,
|
printerId,
|
||||||
{
|
{
|
||||||
@ -93,8 +139,9 @@ export class PrinterDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast the update through websocket
|
// Broadcast the update through websocket
|
||||||
|
logger.debug(`Broadcasting current job clear for printer ${printerId}`);
|
||||||
this.socketManager.broadcast("notify_printer_update", {
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
id: printerId,
|
_id: printerId,
|
||||||
currentSubJob: null,
|
currentSubJob: null,
|
||||||
currentJob: null
|
currentJob: null
|
||||||
});
|
});
|
||||||
@ -111,7 +158,6 @@ export class PrinterDatabase {
|
|||||||
|
|
||||||
async setCurrentJobForPrinting(printerId, queuedJobIds) {
|
async setCurrentJobForPrinting(printerId, queuedJobIds) {
|
||||||
try {
|
try {
|
||||||
logger.error(`Setting current job for printer ${printerId} as printing starts`);
|
|
||||||
|
|
||||||
const printer = await printerModel.findById(printerId).populate('subJobs');
|
const printer = await printerModel.findById(printerId).populate('subJobs');
|
||||||
if (!printer) {
|
if (!printer) {
|
||||||
@ -168,8 +214,12 @@ export class PrinterDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast the update through websocket
|
// Broadcast the update through websocket
|
||||||
|
logger.debug(`Broadcasting current job update for printer ${printerId}:`, {
|
||||||
|
subJobId: subJob.id,
|
||||||
|
jobId: subJob.printJob
|
||||||
|
});
|
||||||
this.socketManager.broadcast("notify_printer_update", {
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
id: printerId,
|
_id: printerId,
|
||||||
currentSubJob: subJob,
|
currentSubJob: subJob,
|
||||||
currentJob: job
|
currentJob: job
|
||||||
});
|
});
|
||||||
@ -188,109 +238,148 @@ export class PrinterDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateSubJobState(subJobId, state) {
|
async updateSubJobState(subJobId, state) {
|
||||||
try {
|
// Broadcast immediately
|
||||||
|
logger.debug(`Broadcasting subjob state update for ${subJobId}:`, { state });
|
||||||
|
this.socketManager.broadcast("notify_subjob_update", {
|
||||||
|
_id: subJobId,
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.debouncer.debounce(`subjob_${subJobId}`, async () => {
|
||||||
|
try {
|
||||||
if (state.type == "standby" || state.type == "error" || state.type == "offline") {
|
if (state.type == "standby" || state.type == "error" || state.type == "offline") {
|
||||||
state.type = 'failed'
|
state.type = 'failed'
|
||||||
logger.warn(`Updating subjob state for ${subJobId}:`, state);
|
logger.warn(`Updating subjob state for ${subJobId}:`, state);
|
||||||
} else {
|
} else {
|
||||||
logger.debug(`Updating subjob state for ${subJobId}:`, state);
|
logger.debug(`Updating subjob state for ${subJobId}:`, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set startedAt if the subjob is starting to print
|
||||||
|
const updateData = { state };
|
||||||
|
if (state.type === 'printing') {
|
||||||
|
const subJob = await printSubJobModel.findById(subJobId);
|
||||||
|
if (!subJob.startedAt) {
|
||||||
|
updateData.startedAt = new Date();
|
||||||
|
logger.info(`Setting startedAt for subjob ${subJobId} as printing begins`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const updatedSubJob = await printSubJobModel.findByIdAndUpdate(
|
// Set finishedAt if the subjob is complete, failed, or cancelled
|
||||||
subJobId,
|
if (['complete', 'failed', 'cancelled'].includes(state.type)) {
|
||||||
{ state },
|
const subJob = await printSubJobModel.findById(subJobId);
|
||||||
{ new: true }
|
if (!subJob.finishedAt) {
|
||||||
);
|
updateData.finishedAt = new Date();
|
||||||
|
logger.info(`Setting finishedAt for subjob ${subJobId} as state is ${state.type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!updatedSubJob) {
|
const updatedSubJob = await printSubJobModel.findByIdAndUpdate(
|
||||||
logger.error(`Sub job with ID ${subJobId} not found`);
|
subJobId,
|
||||||
return;
|
updateData,
|
||||||
|
{ 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,
|
||||||
|
startedAt: updatedSubJob.startedAt,
|
||||||
|
finishedAt: updatedSubJob.finishedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update parent job state
|
||||||
|
await this.updateJobState(updatedSubJob.printJob);
|
||||||
|
|
||||||
|
return updatedSubJob;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to update sub job state for ${subJobId}:`, error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
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) {
|
async updateJobState(jobId) {
|
||||||
try {
|
return this.debouncer.debounce(`job_${jobId}`, async () => {
|
||||||
logger.debug(`Updating job state for ${jobId}`);
|
try {
|
||||||
|
logger.debug(`Updating job state for ${jobId}`);
|
||||||
|
|
||||||
const job = await printJobModel.findById(jobId).populate("subJobs");
|
const job = await printJobModel.findById(jobId).populate("subJobs");
|
||||||
if (!job) {
|
if (!job) {
|
||||||
logger.error(`Job with ID ${jobId} not found`);
|
logger.error(`Job with ID ${jobId} not found`);
|
||||||
return;
|
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
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update finishedAt if all subjobs are complete and none are queued
|
||||||
|
if ((stateCounts.complete + stateCounts.failed + stateCounts.cancelled) === job.subJobs.length && stateCounts.queued === 0 && !job.finishedAt) {
|
||||||
|
logger.info(`Setting finishedAt for job ${jobId} as all subjobs are complete`);
|
||||||
|
job.finishedAt = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
job.state = jobState;
|
||||||
|
job.subJobStats = stateCounts;
|
||||||
|
|
||||||
|
await job.save();
|
||||||
|
|
||||||
|
logger.info(`Updated job ${jobId} state:`, {
|
||||||
|
_id: jobId,
|
||||||
|
state: jobState,
|
||||||
|
subJobStats: stateCounts,
|
||||||
|
finishedAt: job.finishedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
// Broadcast immediately after calculating new state
|
||||||
|
logger.debug(`Broadcasting job state update for ${jobId}:`, {
|
||||||
|
state: jobState,
|
||||||
|
subJobStats: stateCounts,
|
||||||
|
finishedAt: job.finishedAt
|
||||||
|
});
|
||||||
|
this.socketManager.broadcast("notify_job_update", {
|
||||||
|
_id: jobId,
|
||||||
|
state: jobState,
|
||||||
|
subJobStats: stateCounts,
|
||||||
|
finishedAt: job.finishedAt
|
||||||
|
});
|
||||||
|
|
||||||
|
return job;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to update job state for ${jobId}:`, error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
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) {
|
determineJobState(stateCounts) {
|
||||||
@ -374,6 +463,7 @@ logger.warn(`Updating subjob state for ${subJobId}:`, state);
|
|||||||
try {
|
try {
|
||||||
logger.debug(`Updating display status for printer ${printerId}:`, { message });
|
logger.debug(`Updating display status for printer ${printerId}:`, { message });
|
||||||
|
|
||||||
|
logger.debug(`Broadcasting display status for printer ${printerId}:`, { message });
|
||||||
this.socketManager.broadcast("notify_display_status", {
|
this.socketManager.broadcast("notify_display_status", {
|
||||||
printerId,
|
printerId,
|
||||||
message
|
message
|
||||||
@ -417,25 +507,67 @@ logger.warn(`Updating subjob state for ${subJobId}:`, state);
|
|||||||
try {
|
try {
|
||||||
logger.debug(`Adding alert to printer ${printerId}:`, alert);
|
logger.debug(`Adding alert to printer ${printerId}:`, alert);
|
||||||
|
|
||||||
|
// First check if an alert of this type already exists
|
||||||
|
const printer = await printerModel.findById(printerId);
|
||||||
|
if (!printer) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found when adding alert`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingAlertIndex = printer.alerts.findIndex(a => a.type === alert.type);
|
||||||
|
if (existingAlertIndex !== -1) {
|
||||||
|
// If we have a message to update, update the existing alert
|
||||||
|
if (alert.message) {
|
||||||
|
logger.debug(`Updating message for existing alert of type ${alert.type} on printer ${printerId}`);
|
||||||
|
printer.alerts[existingAlertIndex].message = alert.message;
|
||||||
|
const updatedPrinter = await printer.save();
|
||||||
|
|
||||||
|
// Broadcast the update
|
||||||
|
logger.debug(`Broadcasting alert update for printer ${printerId}:`, {
|
||||||
|
alerts: updatedPrinter.alerts
|
||||||
|
});
|
||||||
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
|
_id: printerId,
|
||||||
|
alerts: updatedPrinter.alerts
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Updated message for existing alert on printer ${printerId}:`, {
|
||||||
|
type: alert.type,
|
||||||
|
message: alert.message
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedPrinter;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`Alert of type ${alert.type} already exists for printer ${printerId}, skipping`);
|
||||||
|
return printer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No existing alert found, create a new one
|
||||||
|
logger.debug(`Creating new alert for printer ${printerId}:`, {
|
||||||
|
type: alert.type,
|
||||||
|
priority: alert.priority,
|
||||||
|
hasMessage: !!alert.message
|
||||||
|
});
|
||||||
|
|
||||||
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
||||||
printerId,
|
printerId,
|
||||||
{ $push: { alerts: alert } },
|
{ $push: { alerts: alert } },
|
||||||
{ new: true }
|
{ new: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!updatedPrinter) {
|
logger.info(`Added new alert to printer ${printerId}:`, {
|
||||||
logger.error(`Printer with ID ${printerId} not found when adding alert`);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(`Added alert to printer ${printerId}:`, {
|
|
||||||
type: alert.type,
|
type: alert.type,
|
||||||
priority: alert.priority
|
priority: alert.priority,
|
||||||
|
hasMessage: !!alert.message
|
||||||
});
|
});
|
||||||
|
|
||||||
// Broadcast the alert through websocket
|
// Broadcast the alert through websocket
|
||||||
|
logger.debug(`Broadcasting new alert for printer ${printerId}:`, {
|
||||||
|
alerts: updatedPrinter.alerts
|
||||||
|
});
|
||||||
this.socketManager.broadcast("notify_printer_update", {
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
id: printerId,
|
_id: printerId,
|
||||||
alerts: updatedPrinter.alerts
|
alerts: updatedPrinter.alerts
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -475,8 +607,12 @@ logger.warn(`Updating subjob state for ${subJobId}:`, state);
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Broadcast the alert clear through websocket
|
// Broadcast the alert clear through websocket
|
||||||
|
logger.debug(`Broadcasting alert removal for printer ${printerId}:`, {
|
||||||
|
options,
|
||||||
|
remainingAlerts: updatedPrinter.alerts.length
|
||||||
|
});
|
||||||
this.socketManager.broadcast("notify_printer_update", {
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
id: printerId,
|
_id: printerId,
|
||||||
alerts: updatedPrinter.alerts
|
alerts: updatedPrinter.alerts
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -518,6 +654,37 @@ logger.warn(`Updating subjob state for ${subJobId}:`, state);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clearAlerts(printerId) {
|
||||||
|
try {
|
||||||
|
logger.debug(`Clearing all alerts for printer ${printerId}`);
|
||||||
|
|
||||||
|
const updatedPrinter = await printerModel.findByIdAndUpdate(
|
||||||
|
printerId,
|
||||||
|
{ $set: { alerts: [] } },
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!updatedPrinter) {
|
||||||
|
logger.error(`Printer with ID ${printerId} not found when clearing alerts`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Cleared all alerts for printer ${printerId}`);
|
||||||
|
|
||||||
|
// Broadcast the alert clear through websocket
|
||||||
|
logger.debug(`Broadcasting all alerts clear for printer ${printerId}`);
|
||||||
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
|
_id: printerId,
|
||||||
|
alerts: []
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedPrinter;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to clear alerts for printer ${printerId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async setCurrentFilamentStock(printerId, filamentStockId) {
|
async setCurrentFilamentStock(printerId, filamentStockId) {
|
||||||
try {
|
try {
|
||||||
logger.debug(`Setting current filament stock for printer ${printerId}:`, { filamentStockId });
|
logger.debug(`Setting current filament stock for printer ${printerId}:`, { filamentStockId });
|
||||||
@ -541,8 +708,11 @@ logger.warn(`Updating subjob state for ${subJobId}:`, state);
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Broadcast the update through websocket
|
// Broadcast the update through websocket
|
||||||
|
logger.debug(`Broadcasting filament stock update for printer ${printerId}:`, {
|
||||||
|
filamentStock: updatedPrinter.currentFilamentStock
|
||||||
|
});
|
||||||
this.socketManager.broadcast("notify_printer_update", {
|
this.socketManager.broadcast("notify_printer_update", {
|
||||||
id: printerId,
|
_id: printerId,
|
||||||
currentFilamentStock: updatedPrinter.currentFilamentStock
|
currentFilamentStock: updatedPrinter.currentFilamentStock
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -555,125 +725,164 @@ logger.warn(`Updating subjob state for ${subJobId}:`, state);
|
|||||||
|
|
||||||
async updateFilamentStockWeight(filamentStockId, weight, subJobId = null, jobId = null) {
|
async updateFilamentStockWeight(filamentStockId, weight, subJobId = null, jobId = null) {
|
||||||
try {
|
try {
|
||||||
logger.debug(`Updating filament stock weight for ${filamentStockId}:`, { weight, subJobId, jobId });
|
// Get or fetch filament stock
|
||||||
|
if (!this.filamentStock || this.filamentStock._id.toString() !== filamentStockId) {
|
||||||
const filamentStock = await filamentStockModel.findById(filamentStockId);
|
this.filamentStock = await filamentStockModel.findById(filamentStockId).populate('stockEvents');
|
||||||
if (!filamentStock) {
|
if (!this.filamentStock) {
|
||||||
logger.error(`Filament stock with ID ${filamentStockId} not found`);
|
logger.error(`Filament stock with ID ${filamentStockId} not found`);
|
||||||
return null;
|
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;
|
|
||||||
|
// Calculate new weights immediately
|
||||||
|
const totalEventWeight = this.filamentStock.stockEvents.reduce((sum, event) => {
|
||||||
|
// Skip the existing event if it exists
|
||||||
|
if (this.existingEvent && event._id.toString() === this.existingEvent._id.toString()) {
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
return sum + event.value;
|
||||||
|
}, 0) + weight;
|
||||||
|
const newNetWeight = totalEventWeight;
|
||||||
|
const newGrossWeight = totalEventWeight + (this.filamentStock.startingGrossWeight - this.filamentStock.startingNetWeight);
|
||||||
|
const remainingPercent = newNetWeight / this.filamentStock.startingNetWeight;
|
||||||
const state = {
|
const state = {
|
||||||
type: 'partiallyconsumed',
|
type: 'partiallyconsumed',
|
||||||
percent: (1 - remainingPercent).toFixed(2)
|
percent: (1 - remainingPercent).toFixed(2)
|
||||||
};
|
};
|
||||||
|
|
||||||
const filamentStockWithState = await filamentStockModel.findByIdAndUpdate(
|
// Broadcast both updates with calculated values
|
||||||
filamentStockId,
|
logger.debug(`Broadcasting filament stock weight update for ${filamentStockId}:`, {
|
||||||
{ state },
|
newNetWeight,
|
||||||
{ new: true }
|
newGrossWeight,
|
||||||
);
|
state
|
||||||
|
|
||||||
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", {
|
this.socketManager.broadcast("notify_filamentstock_update", {
|
||||||
id: filamentStockId,
|
_id: filamentStockId,
|
||||||
currentNetWeight: newNetWeight,
|
currentNetWeight: newNetWeight,
|
||||||
currentGrossWeight: newGrossWeight,
|
currentGrossWeight: newGrossWeight,
|
||||||
stockEvents: finalFilamentStock.stockEvents,
|
updatedAt: new Date(),
|
||||||
state
|
state
|
||||||
});
|
});
|
||||||
|
|
||||||
return filamentStockWithState;
|
if (this.existingEvent) {
|
||||||
|
|
||||||
|
logger.debug(`Broadcasting stock event update for ${filamentStockId}:`, {
|
||||||
|
_id: this.existingEvent._id,
|
||||||
|
value: weight,
|
||||||
|
subJob: subJobId,
|
||||||
|
job: jobId
|
||||||
|
})
|
||||||
|
|
||||||
|
this.socketManager.broadcast("notify_stockevent_update", {
|
||||||
|
_id: this.existingEvent._id,
|
||||||
|
value: weight,
|
||||||
|
updatedAt: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.debouncer.debounce('filament_stock', async () => {
|
||||||
|
try {
|
||||||
|
// Check if a stock event already exists for this subJobId and jobId
|
||||||
|
this.existingEvent = await stockEventModel.findOne({
|
||||||
|
filamentStock: filamentStockId,
|
||||||
|
subJob: subJobId,
|
||||||
|
job: jobId
|
||||||
|
});
|
||||||
|
|
||||||
|
let stockEvent;
|
||||||
|
if (this.existingEvent) {
|
||||||
|
// Update existing event
|
||||||
|
logger.debug(`Updating existing stock event for subJobId ${subJobId} and jobId ${jobId}`);
|
||||||
|
stockEvent = await stockEventModel.findByIdAndUpdate(
|
||||||
|
this.existingEvent._id,
|
||||||
|
{
|
||||||
|
value: weight,
|
||||||
|
updatedAt: new Date()
|
||||||
|
},
|
||||||
|
{ new: true }
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Create new stock event
|
||||||
|
logger.debug(`Creating new stock event for subJobId ${subJobId} and jobId ${jobId}`);
|
||||||
|
stockEvent = await stockEventModel.create({
|
||||||
|
type: 'subJob',
|
||||||
|
value: weight,
|
||||||
|
subJob: subJobId,
|
||||||
|
job: jobId,
|
||||||
|
filamentStock: filamentStockId,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
createdAt: new Date()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the new stock event to the filament stock
|
||||||
|
await filamentStockModel.findByIdAndUpdate(
|
||||||
|
filamentStockId,
|
||||||
|
{ $push: { stockEvents: stockEvent._id } }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedFilamentStock = await filamentStockModel.findByIdAndUpdate(
|
||||||
|
filamentStockId,
|
||||||
|
{
|
||||||
|
currentNetWeight: newNetWeight,
|
||||||
|
currentGrossWeight: newGrossWeight,
|
||||||
|
state
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new: true,
|
||||||
|
populate: {
|
||||||
|
path: 'stockEvents',
|
||||||
|
populate: [
|
||||||
|
{
|
||||||
|
path: 'subJob',
|
||||||
|
select: 'number'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'job',
|
||||||
|
select: 'startedAt'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the cached filament stock
|
||||||
|
this.filamentStock = updatedFilamentStock;
|
||||||
|
|
||||||
|
logger.info(`Updated filament stock ${filamentStockId}:`, {
|
||||||
|
newGrossWeight: newGrossWeight,
|
||||||
|
newNetWeight: newNetWeight,
|
||||||
|
eventCount: updatedFilamentStock.stockEvents.length,
|
||||||
|
updatedExistingEvent: !!this.existingEvent,
|
||||||
|
remainingPercent: remainingPercent.toFixed(2),
|
||||||
|
consumedPercent: state.percent,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Broadcast the final state after database update
|
||||||
|
logger.debug(`Broadcasting final filament stock update for ${filamentStockId}:`, {
|
||||||
|
newNetWeight,
|
||||||
|
newGrossWeight,
|
||||||
|
eventCount: updatedFilamentStock.stockEvents.length,
|
||||||
|
state
|
||||||
|
});
|
||||||
|
this.socketManager.broadcast("notify_filamentstock_update", {
|
||||||
|
_id: filamentStockId,
|
||||||
|
currentNetWeight: newNetWeight,
|
||||||
|
currentGrossWeight: newGrossWeight,
|
||||||
|
state
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedFilamentStock;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Failed to update filament stock weight for ${filamentStockId}:`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Failed to update filament stock weight for ${filamentStockId}:`, error);
|
logger.error(`Failed to initialize update for filament stock ${filamentStockId}:`, error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@ export class PrinterClient {
|
|||||||
this.printerManager = printerManager;
|
this.printerManager = printerManager;
|
||||||
this.socketManager = socketManager;
|
this.socketManager = socketManager;
|
||||||
this.state = { type: 'offline '};
|
this.state = { type: 'offline '};
|
||||||
this.klippyState = { type: 'offline '};
|
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();
|
||||||
@ -111,7 +111,7 @@ export class PrinterClient {
|
|||||||
|
|
||||||
this.socket.on("open", () => {
|
this.socket.on("open", () => {
|
||||||
logger.info(`Connected to Moonraker (${this.name})`);
|
logger.info(`Connected to Moonraker (${this.name})`);
|
||||||
this.online = true;
|
this.isOnline = true;
|
||||||
this.identifyConnection();
|
this.identifyConnection();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ export class PrinterClient {
|
|||||||
|
|
||||||
this.socket.on("close", () => {
|
this.socket.on("close", () => {
|
||||||
logger.info(`Disconnected from Moonraker (${this.name})`);
|
logger.info(`Disconnected from Moonraker (${this.name})`);
|
||||||
this.online = false;
|
this.isOnline = false;
|
||||||
this.state = { type: "offline" };
|
this.state = { type: "offline" };
|
||||||
this.updatePrinterState();
|
this.updatePrinterState();
|
||||||
this.connectionId = null;
|
this.connectionId = null;
|
||||||
@ -175,7 +175,7 @@ export class PrinterClient {
|
|||||||
try {
|
try {
|
||||||
// Get server info
|
// Get server info
|
||||||
const serverResult = await this.jsonRpc.callMethod("server.info");
|
const serverResult = await this.jsonRpc.callMethod("server.info");
|
||||||
this.online = true;
|
this.isOnline = true;
|
||||||
this.klippyState = { type: serverResult.klippy_state };
|
this.klippyState = { type: serverResult.klippy_state };
|
||||||
logger.info(
|
logger.info(
|
||||||
"Server:",
|
"Server:",
|
||||||
@ -194,9 +194,27 @@ export class PrinterClient {
|
|||||||
`Updated firmware version for ${this.name} to ${klippyResult.software_version}`,
|
`Updated firmware version for ${this.name} to ${klippyResult.software_version}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (klippyResult.state === "error") {
|
if (klippyResult.state === "error" && klippyResult.state_message) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`Klippy error for ${this.name}: ${klippyResult.state_message}`,
|
`Klippy error for ${this.name}: ${klippyResult.state_message}`,
|
||||||
|
this.database.addAlert(this.id, {
|
||||||
|
type: "klippyError",
|
||||||
|
message: klippyResult.state_message,
|
||||||
|
priority: 9,
|
||||||
|
timestamp: new Date()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (klippyResult.state === "startup" && klippyResult.state_message) {
|
||||||
|
logger.warn(
|
||||||
|
`Klippy startup message for ${this.name}: ${klippyResult.state_message}`,
|
||||||
|
this.database.addAlert(this.id, {
|
||||||
|
type: "klippyStartup",
|
||||||
|
message: klippyResult.state_message,
|
||||||
|
priority: 8,
|
||||||
|
timestamp: new Date()
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -219,7 +237,7 @@ export class PrinterClient {
|
|||||||
|
|
||||||
async getPrinterState() {
|
async getPrinterState() {
|
||||||
logger.info(`Getting state of (${this.name})`);
|
logger.info(`Getting state of (${this.name})`);
|
||||||
if (!this.online) {
|
if (!this.isOnline) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`Cannot send command: Not connected to Moonraker (${this.name})`,
|
`Cannot send command: Not connected to Moonraker (${this.name})`,
|
||||||
);
|
);
|
||||||
@ -267,9 +285,9 @@ export class PrinterClient {
|
|||||||
Object.assign(allSubscriptions, value);
|
Object.assign(allSubscriptions, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("Combined subscriptions:", allSubscriptions);
|
logger.debug("Combined subscriptions:", Object.keys(allSubscriptions).join(", "));
|
||||||
|
|
||||||
if (!this.online) {
|
if (!this.isOnline) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`Cannot send command: Not connected to Moonraker (${this.name})`,
|
`Cannot send command: Not connected to Moonraker (${this.name})`,
|
||||||
);
|
);
|
||||||
@ -281,6 +299,9 @@ export class PrinterClient {
|
|||||||
objects: allSubscriptions,
|
objects: allSubscriptions,
|
||||||
});
|
});
|
||||||
logger.debug(`Command sent to (${this.name})`);
|
logger.debug(`Command sent to (${this.name})`);
|
||||||
|
logger.debug({
|
||||||
|
objects: allSubscriptions,
|
||||||
|
})
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Error sending command to (${this.name}):`, error);
|
logger.error(`Error sending command to (${this.name}):`, error);
|
||||||
@ -290,7 +311,7 @@ export class PrinterClient {
|
|||||||
|
|
||||||
async sendPrinterCommand(command) {
|
async sendPrinterCommand(command) {
|
||||||
logger.info(`Sending ${command.method} command to (${this.name})`);
|
logger.info(`Sending ${command.method} command to (${this.name})`);
|
||||||
if (!this.online) {
|
if (!this.isOnline) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`Cannot send command: Not connected to Moonraker (${this.name})`,
|
`Cannot send command: Not connected to Moonraker (${this.name})`,
|
||||||
);
|
);
|
||||||
@ -329,7 +350,7 @@ export class PrinterClient {
|
|||||||
if (status.print_stats?.state) {
|
if (status.print_stats?.state) {
|
||||||
const newState = status.print_stats.state;
|
const newState = status.print_stats.state;
|
||||||
if (newState !== this.state.type) {
|
if (newState !== this.state.type) {
|
||||||
logger.info(`printer ${this.name} state changed from ${this.state.type} to ${newState}`);
|
logger.info(`Printer ${this.name} state changed from ${this.state.type} to ${newState}`);
|
||||||
this.state.type = newState;
|
this.state.type = newState;
|
||||||
stateChanged = true;
|
stateChanged = true;
|
||||||
}
|
}
|
||||||
@ -345,14 +366,6 @@ export class PrinterClient {
|
|||||||
// Calculate weight in grams
|
// Calculate weight in grams
|
||||||
const filamentWeightG = filamentVolumeCm3 * this.currentFilamentStockDensity;
|
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) {
|
if (this.currentSubJobId != null && this.currentJobId != null && this.currentFilamentStockId != null) {
|
||||||
this.database.updateFilamentStockWeight(this.currentFilamentStockId, (-1 * filamentWeightG), this.currentSubJobId, this.currentJobId);
|
this.database.updateFilamentStockWeight(this.currentFilamentStockId, (-1 * filamentWeightG), this.currentSubJobId, this.currentJobId);
|
||||||
}
|
}
|
||||||
@ -398,9 +411,7 @@ this.database.updateFilamentStockWeight(this.currentFilamentStockId, (-1 * filam
|
|||||||
|
|
||||||
if (stateChanged || progressChanged) {
|
if (stateChanged || progressChanged) {
|
||||||
// Update printer state first
|
// Update printer state first
|
||||||
await this.database.updatePrinterState(this.id, this.state, this.online);
|
await this.updatePrinterState()
|
||||||
|
|
||||||
logger.warn('State changed!')
|
|
||||||
// Set current job to null when not printing or paused
|
// Set current job to null when not printing or paused
|
||||||
if (!["printing", "paused"].includes(this.state.type)) {
|
if (!["printing", "paused"].includes(this.state.type)) {
|
||||||
this.currentJobId = null;
|
this.currentJobId = null;
|
||||||
@ -428,7 +439,8 @@ this.database.updateFilamentStockWeight(this.currentFilamentStockId, (-1 * filam
|
|||||||
|
|
||||||
async updatePrinterState() {
|
async updatePrinterState() {
|
||||||
try {
|
try {
|
||||||
await this.database.updatePrinterState(this.id, this.state, this.online);
|
const state = this.klippyState.type !== 'ready' ? this.klippyState : this.state;
|
||||||
|
await this.database.updatePrinterState(this.id, state, this.isOnline);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Failed to update printer state:`, error);
|
logger.error(`Failed to update printer state:`, error);
|
||||||
}
|
}
|
||||||
@ -504,7 +516,7 @@ this.database.updateFilamentStockWeight(this.currentFilamentStockId, (-1 * filam
|
|||||||
if (!this.currentSubJobId) {
|
if (!this.currentSubJobId) {
|
||||||
const result = await this.database.setCurrentJobForPrinting(this.id, this.queuedJobIds);
|
const result = await this.database.setCurrentJobForPrinting(this.id, this.queuedJobIds);
|
||||||
if (result) {
|
if (result) {
|
||||||
logger.info(`Setting first queued subjob as current for printer ${this.name}`, result);
|
logger.info(`Setting first queued subjob as current for printer ${this.name}: `, result.currentSubJob._id);
|
||||||
this.currentSubJobId = result.currentSubJob._id;
|
this.currentSubJobId = result.currentSubJob._id;
|
||||||
this.currentJobId = result.currentJob._id;
|
this.currentJobId = result.currentJob._id;
|
||||||
await this.database.updateSubJobState(this.currentSubJobId, this.state);
|
await this.database.updateSubJobState(this.currentSubJobId, this.state);
|
||||||
@ -538,10 +550,12 @@ this.database.updateFilamentStockWeight(this.currentFilamentStockId, (-1 * filam
|
|||||||
async handleKlippyDisconnected() {
|
async handleKlippyDisconnected() {
|
||||||
logger.info(`Klippy disconnected (${this.name})`);
|
logger.info(`Klippy disconnected (${this.name})`);
|
||||||
this.state = { type: "offline" };
|
this.state = { type: "offline" };
|
||||||
|
this.klippyState = { type: 'offline'};
|
||||||
this.isOnline = false;
|
this.isOnline = false;
|
||||||
this.isPrinting = false;
|
this.isPrinting = false;
|
||||||
this.isError = false;
|
this.isError = false;
|
||||||
this.isReady = false;
|
this.isReady = false;
|
||||||
|
await this.database.clearAlerts(this.id);
|
||||||
await this.updatePrinterState();
|
await this.updatePrinterState();
|
||||||
await this.updatePrinterSubJobs();
|
await this.updatePrinterSubJobs();
|
||||||
}
|
}
|
||||||
@ -565,7 +579,7 @@ this.database.updateFilamentStockWeight(this.currentFilamentStockId, (-1 * filam
|
|||||||
|
|
||||||
async uploadGcodeFile(fileBlob, fileName) {
|
async uploadGcodeFile(fileBlob, fileName) {
|
||||||
logger.info(`Uploading G-code file ${fileName} to ${this.name}`);
|
logger.info(`Uploading G-code file ${fileName} to ${this.name}`);
|
||||||
if (!this.online) {
|
if (!this.isOnline) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`Cannot upload file: Not connected to Moonraker (${this.name})`,
|
`Cannot upload file: Not connected to Moonraker (${this.name})`,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -138,7 +138,7 @@ export class SocketClient {
|
|||||||
|
|
||||||
// Merge the new subscription data with existing data
|
// Merge the new subscription data with existing data
|
||||||
const mergedSubscription = {
|
const mergedSubscription = {
|
||||||
...existingSubscription.objects,
|
...existingSubscription,
|
||||||
...data.objects,
|
...data.objects,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user