984 lines
27 KiB
JavaScript
984 lines
27 KiB
JavaScript
import log4js from "log4js";
|
|
import { loadConfig } from "../config.js";
|
|
import { sendIPC } from "../electron/ipc.js";
|
|
|
|
const config = loadConfig();
|
|
const logger = log4js.getLogger("Printer Database");
|
|
logger.level = config.logLevel;
|
|
|
|
export class PrinterDatabase {
|
|
constructor(socketClient, printer) {
|
|
this.socketClient = socketClient;
|
|
this.printer = printer;
|
|
this.id = this.printer._id;
|
|
this.filamentStock = null; // Store current filament stock
|
|
this.existingEvent = null; // Store existing stock event
|
|
logger.info("Initialized PrinterDatabase with socket manager");
|
|
}
|
|
|
|
async getPrinter() {
|
|
const object = await this.socketClient.getObject({
|
|
_id: this.printer._id,
|
|
objectType: "printer",
|
|
populate: ["subJobs"],
|
|
});
|
|
return object;
|
|
}
|
|
|
|
async updatePrinter() {
|
|
sendIPC("setPrinter", this.printer);
|
|
const object = await this.socketClient.editObject({
|
|
_id: this.printer._id,
|
|
objectType: "printer",
|
|
updateData: this.printer,
|
|
});
|
|
return object;
|
|
}
|
|
|
|
async getPrinterConfig() {
|
|
try {
|
|
logger.debug(`Getting printer config for ${this.id}`);
|
|
|
|
const printers = await this.socketClient.listObjects({
|
|
objectType: "printer",
|
|
filter: { _id: this.id },
|
|
populate: ["moonraker"],
|
|
});
|
|
|
|
if (!printers || printers.length === 0) {
|
|
logger.error(
|
|
`Printer with ID ${this.id} not found when getting config`
|
|
);
|
|
return null;
|
|
}
|
|
|
|
const printer = printers[0];
|
|
logger.debug(
|
|
`Retrieved printer config for ${this.id}:`,
|
|
printer.moonraker
|
|
);
|
|
return printer.moonraker;
|
|
} catch (error) {
|
|
logger.error(`Failed to get printer config for ${this.id}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async updatePrinterState(state, online) {
|
|
try {
|
|
logger.debug(`Updating printer state for ${this.printer.name}:`, {
|
|
state,
|
|
online,
|
|
});
|
|
|
|
if (state.type === "printing" && state.progress === undefined) {
|
|
logger.debug(
|
|
`Setting default progress for printing state on printer ${this.printer.name}`
|
|
);
|
|
state.progress = 0;
|
|
}
|
|
|
|
this.printer.state = state;
|
|
this.printer.online = online;
|
|
|
|
const updatedPrinter = await this.updatePrinter();
|
|
|
|
logger.info(`Updated printer ${this.printer.name} state:`, {
|
|
type: state.type,
|
|
progress: state.progress,
|
|
online,
|
|
previousState: updatedPrinter.state,
|
|
});
|
|
|
|
return updatedPrinter;
|
|
} catch (error) {
|
|
logger.error(
|
|
`Failed to update printer state for ${this.printer.name}:`,
|
|
error
|
|
);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async clearCurrentJob() {
|
|
try {
|
|
const updatedPrinter = await this.socketClient.editObject({
|
|
_id: this.id,
|
|
objectType: "printer",
|
|
updateData: {
|
|
currentSubJob: null,
|
|
currentJob: null,
|
|
},
|
|
});
|
|
|
|
if (!updatedPrinter) {
|
|
logger.error(
|
|
`Printer with ID ${this.id} not found when clearing current job`
|
|
);
|
|
return null;
|
|
}
|
|
|
|
logger.info(`Cleared current job for printer ${this.id}`);
|
|
|
|
return {
|
|
currentSubJob: null,
|
|
currentJob: null,
|
|
};
|
|
} catch (error) {
|
|
logger.error(
|
|
`Failed to clear current job for printer ${this.id}:`,
|
|
error
|
|
);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async setCurrentJobForPrinting(queuedJobIds) {
|
|
try {
|
|
const printers = await this.socketClient.listObjects({
|
|
objectType: "printer",
|
|
filter: { _id: this.id },
|
|
populate: ["subJobs"],
|
|
});
|
|
|
|
if (!printers || printers.length === 0) {
|
|
logger.error(`Printer with ID ${this.id} not found`);
|
|
return null;
|
|
}
|
|
|
|
const printer = printers[0];
|
|
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 ${this.id} as printing starts`,
|
|
{
|
|
subJobId: subJob.id,
|
|
jobId: subJob.job,
|
|
}
|
|
);
|
|
|
|
const now = new Date();
|
|
|
|
// Update printer with current job and startedAt
|
|
const oldCurrentJob = printer.currentJob;
|
|
const oldCurrentSubJob = printer.currentSubJob;
|
|
const updatedPrinter = await this.socketClient.editObject({
|
|
_id: this.id,
|
|
objectType: "printer",
|
|
updateData: {
|
|
currentSubJob: subJob.id,
|
|
currentJob: subJob.job,
|
|
startedAt: now,
|
|
},
|
|
});
|
|
|
|
if (!updatedPrinter) {
|
|
logger.error(
|
|
`Printer with ID ${this.id} not found when setting job and subjob`
|
|
);
|
|
return null;
|
|
}
|
|
|
|
// Update subjob with startedAt
|
|
const oldStartedAt = subJob.startedAt;
|
|
await this.socketClient.editObject({
|
|
_id: subJob.id,
|
|
objectType: "subJob",
|
|
updateData: { startedAt: now },
|
|
});
|
|
|
|
// Get the full job object and update its startedAt if null
|
|
const jobs = await this.socketClient.listObjects({
|
|
objectType: "job",
|
|
filter: { _id: subJob.job },
|
|
populate: ["gcodeFile"],
|
|
});
|
|
|
|
if (!jobs || jobs.length === 0) {
|
|
logger.error(`Job with ID ${subJob.job} not found`);
|
|
return null;
|
|
}
|
|
|
|
const job = jobs[0];
|
|
const oldJobStartedAt = job.startedAt;
|
|
if (!job.startedAt) {
|
|
await this.socketClient.editObject({
|
|
_id: subJob.job,
|
|
objectType: "job",
|
|
updateData: { startedAt: now },
|
|
});
|
|
job.startedAt = now;
|
|
}
|
|
|
|
logger.info(`Set current job for printer ${this.id}:`, {
|
|
subJobId: subJob.id,
|
|
jobId: subJob.job,
|
|
});
|
|
|
|
return {
|
|
currentSubJob: subJob,
|
|
currentJob: job,
|
|
};
|
|
}
|
|
}
|
|
return null;
|
|
} catch (error) {
|
|
logger.error(`Failed to set current job for printer ${this.id}:`, 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);
|
|
}
|
|
|
|
// Set startedAt if the subjob is starting to print
|
|
const updateData = { state };
|
|
if (state.type === "printing") {
|
|
const subJobs = await this.socketClient.listObjects({
|
|
objectType: "subJob",
|
|
filter: { _id: subJobId },
|
|
});
|
|
|
|
if (subJobs && subJobs.length > 0) {
|
|
const subJob = subJobs[0];
|
|
if (!subJob.startedAt) {
|
|
updateData.startedAt = new Date();
|
|
logger.info(
|
|
`Setting startedAt for subjob ${subJobId} as printing begins`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set finishedAt if the subjob is complete, failed, or cancelled
|
|
if (["complete", "failed", "cancelled"].includes(state.type)) {
|
|
const subJobs = await this.socketClient.listObjects({
|
|
objectType: "subJob",
|
|
filter: { _id: subJobId },
|
|
});
|
|
|
|
if (subJobs && subJobs.length > 0) {
|
|
const subJob = subJobs[0];
|
|
if (!subJob.finishedAt) {
|
|
updateData.finishedAt = new Date();
|
|
logger.info(
|
|
`Setting finishedAt for subjob ${subJobId} as state is ${state.type}`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
const subJobs = await this.socketClient.listObjects({
|
|
objectType: "subJob",
|
|
filter: { _id: subJobId },
|
|
});
|
|
|
|
if (!subJobs || subJobs.length === 0) {
|
|
logger.error(`Sub job with ID ${subJobId} not found`);
|
|
return;
|
|
}
|
|
|
|
const subJob = subJobs[0];
|
|
const oldState = subJob.state;
|
|
const updatedSubJob = await this.socketClient.editObject({
|
|
_id: subJobId,
|
|
objectType: "subJob",
|
|
updateData,
|
|
});
|
|
|
|
logger.info(`Updated subjob ${subJobId} state:`, {
|
|
type: state.type,
|
|
progress: state.progress,
|
|
previousState: updatedSubJob.state,
|
|
job: updatedSubJob.job,
|
|
startedAt: updatedSubJob.startedAt,
|
|
finishedAt: updatedSubJob.finishedAt,
|
|
});
|
|
|
|
// Update parent job state
|
|
await this.updateJobState(updatedSubJob.job, subJob.printer);
|
|
|
|
return updatedSubJob;
|
|
} catch (error) {
|
|
logger.error(`Failed to update sub job state for ${subJobId}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async updateJobState(jobId, printerId) {
|
|
try {
|
|
logger.debug(`Updating job state for ${jobId}`);
|
|
|
|
const jobs = await this.socketClient.listObjects({
|
|
objectType: "job",
|
|
filter: { _id: jobId },
|
|
populate: ["subJobs"],
|
|
});
|
|
|
|
if (!jobs || jobs.length === 0) {
|
|
logger.error(`Job with ID ${jobId} not found`);
|
|
return;
|
|
}
|
|
|
|
const job = jobs[0];
|
|
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,
|
|
});
|
|
|
|
var oldAuditLogValues = {};
|
|
var newAuditLogValues = {};
|
|
|
|
// Update finishedAt if all subjobs are complete and none are queued
|
|
const oldFinishedAt = job.finishedAt;
|
|
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();
|
|
oldAuditLogValues = {
|
|
...oldAuditLogValues,
|
|
finishedAt: oldFinishedAt,
|
|
};
|
|
newAuditLogValues = {
|
|
...newAuditLogValues,
|
|
finishedAt: job.finishedAt,
|
|
};
|
|
}
|
|
|
|
const oldState = job.state;
|
|
if (oldState.type !== jobState.type) {
|
|
oldAuditLogValues = { ...oldAuditLogValues, state: oldState };
|
|
newAuditLogValues = { ...newAuditLogValues, state: jobState };
|
|
}
|
|
|
|
const updateData = {
|
|
state: jobState,
|
|
subJobStats: stateCounts,
|
|
};
|
|
|
|
if (job.finishedAt) {
|
|
updateData.finishedAt = job.finishedAt;
|
|
}
|
|
|
|
const updatedJob = await this.socketClient.editObject({
|
|
_id: jobId,
|
|
objectType: "job",
|
|
updateData,
|
|
});
|
|
|
|
logger.info(`Updated job ${jobId} state:`, {
|
|
_id: jobId,
|
|
state: jobState,
|
|
subJobStats: stateCounts,
|
|
finishedAt: updatedJob.finishedAt,
|
|
});
|
|
|
|
return updatedJob;
|
|
} 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(subJobId) {
|
|
try {
|
|
logger.debug(`Adding subjob ${subJobId} to printer ${this.id}`);
|
|
|
|
const printers = await this.socketClient.listObjects({
|
|
objectType: "printer",
|
|
filter: { _id: this.id },
|
|
});
|
|
|
|
if (!printers || printers.length === 0) {
|
|
logger.error(`Printer with ID ${this.id} not found`);
|
|
return null;
|
|
}
|
|
|
|
const printer = printers[0];
|
|
const updatedSubJobs = [...printer.subJobs, subJobId];
|
|
|
|
const updatedPrinter = await this.socketClient.editObject({
|
|
_id: this.id,
|
|
objectType: "printer",
|
|
updateData: { subJobs: updatedSubJobs },
|
|
});
|
|
|
|
logger.info(`Added subjob ${subJobId} to printer ${this.id}`, {
|
|
currentSubJobs: updatedPrinter.subJobs.length,
|
|
printerState: updatedPrinter.state,
|
|
});
|
|
|
|
return updatedPrinter;
|
|
} catch (error) {
|
|
logger.error(
|
|
`Failed to add subjob ${subJobId} to printer ${this.id}:`,
|
|
error
|
|
);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async removePrinterSubJob(subJobId) {
|
|
try {
|
|
logger.debug(`Removing subjob ${subJobId} from printer ${this.id}`);
|
|
|
|
const printers = await this.socketClient.listObjects({
|
|
objectType: "printer",
|
|
filter: { _id: this.id },
|
|
});
|
|
|
|
if (!printers || printers.length === 0) {
|
|
logger.error(`Printer with ID ${this.id} not found`);
|
|
return;
|
|
}
|
|
|
|
const printer = printers[0];
|
|
const updatedSubJobs = printer.subJobs.filter(
|
|
(id) => id.toString() !== subJobId.toString()
|
|
);
|
|
|
|
const updatedPrinter = await this.socketClient.editObject({
|
|
_id: this.id,
|
|
objectType: "printer",
|
|
updateData: { subJobs: updatedSubJobs },
|
|
});
|
|
|
|
logger.info(`Removed subjob ${subJobId} from printer ${this.id}`, {
|
|
remainingSubJobs: updatedPrinter.subJobs.length,
|
|
printerState: updatedPrinter.state,
|
|
});
|
|
|
|
return updatedPrinter;
|
|
} catch (error) {
|
|
logger.error(
|
|
`Failed to remove subjob ${subJobId} from printer ${this.id}:`,
|
|
error
|
|
);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async updateDisplayStatus(message) {
|
|
try {
|
|
logger.debug(`Updating display status for printer ${this.id}:`, {
|
|
message,
|
|
});
|
|
|
|
logger.info(`Updated display status for printer ${this.id}:`, {
|
|
message,
|
|
});
|
|
} catch (error) {
|
|
logger.error(
|
|
`Failed to update display status for printer ${this.id}:`,
|
|
error
|
|
);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async updatePrinterFirmware(firmwareVersion) {
|
|
try {
|
|
logger.debug(
|
|
`Updating firmware version for printer ${this.id}:`,
|
|
firmwareVersion
|
|
);
|
|
this.printer.firmware = firmwareVersion;
|
|
await this.updatePrinter();
|
|
logger.info(
|
|
`Updated firmware version for printer ${this.id}:`,
|
|
firmwareVersion
|
|
);
|
|
} catch (error) {
|
|
logger.error(
|
|
`Failed to update firmware version for printer ${this.id}:`,
|
|
error
|
|
);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async addAlert(alert) {
|
|
try {
|
|
logger.debug(`Adding alert to printer ${this.id}:`, alert);
|
|
|
|
const printers = await this.socketClient.listObjects({
|
|
objectType: "printer",
|
|
filter: { _id: this.id },
|
|
});
|
|
|
|
if (!printers || printers.length === 0) {
|
|
logger.error(`Printer with ID ${this.id} not found`);
|
|
return null;
|
|
}
|
|
|
|
const printer = printers[0];
|
|
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 ${this.id}`
|
|
);
|
|
const updatedAlerts = [...printer.alerts];
|
|
updatedAlerts[existingAlertIndex].message = alert.message;
|
|
|
|
const updatedPrinter = await this.socketClient.editObject({
|
|
_id: this.id,
|
|
objectType: "printer",
|
|
updateData: { alerts: updatedAlerts },
|
|
});
|
|
|
|
logger.info(
|
|
`Updated message for existing alert on printer ${this.id}:`,
|
|
{
|
|
type: alert.type,
|
|
message: alert.message,
|
|
}
|
|
);
|
|
|
|
return updatedPrinter;
|
|
}
|
|
|
|
logger.debug(
|
|
`Alert of type ${alert.type} already exists for printer ${this.id}, skipping`
|
|
);
|
|
return printer;
|
|
}
|
|
|
|
// No existing alert found, create a new one
|
|
logger.debug(`Creating new alert for printer ${this.id}:`, {
|
|
type: alert.type,
|
|
priority: alert.priority,
|
|
hasMessage: !!alert.message,
|
|
});
|
|
|
|
const updatedAlerts = [...printer.alerts, alert];
|
|
const updatedPrinter = await this.socketClient.editObject({
|
|
_id: this.id,
|
|
objectType: "printer",
|
|
updateData: { alerts: updatedAlerts },
|
|
});
|
|
|
|
logger.info(`Added new alert to printer ${this.id}:`, {
|
|
type: alert.type,
|
|
priority: alert.priority,
|
|
hasMessage: !!alert.message,
|
|
});
|
|
|
|
return updatedPrinter;
|
|
} catch (error) {
|
|
logger.error(`Failed to add alert to printer ${this.id}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async removeAlerts(options = {}) {
|
|
try {
|
|
logger.debug(`Clearing alerts for printer ${this.id}:`, options);
|
|
|
|
const printers = await this.socketClient.listObjects({
|
|
objectType: "printer",
|
|
filter: { _id: this.id },
|
|
});
|
|
|
|
if (!printers || printers.length === 0) {
|
|
logger.error(
|
|
`Printer with ID ${this.id} not found when clearing alerts`
|
|
);
|
|
return null;
|
|
}
|
|
|
|
const printer = printers[0];
|
|
let filteredAlerts = printer.alerts;
|
|
|
|
if (options.type) {
|
|
filteredAlerts = filteredAlerts.filter(
|
|
(alert) => alert.type !== options.type
|
|
);
|
|
}
|
|
if (options.priority) {
|
|
filteredAlerts = filteredAlerts.filter(
|
|
(alert) => alert.priority !== options.priority
|
|
);
|
|
}
|
|
|
|
const updatedPrinter = await this.socketClient.editObject({
|
|
_id: this.id,
|
|
objectType: "printer",
|
|
updateData: { alerts: filteredAlerts },
|
|
});
|
|
|
|
logger.info(`Cleared alerts for printer ${this.id}:`, {
|
|
options,
|
|
remainingAlerts: updatedPrinter.alerts.length,
|
|
});
|
|
|
|
return updatedPrinter;
|
|
} catch (error) {
|
|
logger.error(`Failed to clear alerts for printer ${this.id}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getAlerts(options = {}) {
|
|
try {
|
|
logger.debug(`Getting alerts for printer ${this.id}:`, options);
|
|
|
|
const printers = await this.socketClient.listObjects({
|
|
objectType: "printer",
|
|
filter: { _id: this.id },
|
|
});
|
|
|
|
if (!printers || printers.length === 0) {
|
|
logger.error(
|
|
`Printer with ID ${this.id} not found when getting alerts`
|
|
);
|
|
return null;
|
|
}
|
|
|
|
const printer = printers[0];
|
|
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 ${this.id}`);
|
|
return alerts;
|
|
} catch (error) {
|
|
logger.error(`Failed to get alerts for printer ${this.id}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async clearAlerts() {
|
|
try {
|
|
logger.debug(`Clearing all alerts for printer ${this.id}`);
|
|
|
|
const updatedPrinter = await this.socketClient.editObject({
|
|
_id: this.id,
|
|
objectType: "printer",
|
|
updateData: { alerts: [] },
|
|
});
|
|
|
|
if (!updatedPrinter) {
|
|
logger.error(
|
|
`Printer with ID ${this.id} not found when clearing alerts`
|
|
);
|
|
return null;
|
|
}
|
|
|
|
logger.info(`Cleared all alerts for printer ${this.id}`);
|
|
|
|
return updatedPrinter;
|
|
} catch (error) {
|
|
logger.error(`Failed to clear alerts for printer ${this.id}:`, error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async setCurrentFilamentStock(filamentStockId) {
|
|
try {
|
|
logger.debug(`Setting current filament stock for printer ${this.id}:`, {
|
|
filamentStockId,
|
|
});
|
|
|
|
const printers = await this.socketClient.listObjects({
|
|
objectType: "printer",
|
|
filter: { _id: this.id },
|
|
populate: [
|
|
{
|
|
path: "currentFilamentStock",
|
|
populate: {
|
|
path: "filament",
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
if (!printers || printers.length === 0) {
|
|
logger.error(
|
|
`Printer with ID ${this.id} not found when setting current filament stock`
|
|
);
|
|
return null;
|
|
}
|
|
|
|
const printer = printers[0];
|
|
const oldFilamentStock = printer.currentFilamentStock;
|
|
const updatedPrinter = await this.socketClient.editObject({
|
|
_id: this.id,
|
|
objectType: "printer",
|
|
updateData: { currentFilamentStock: filamentStockId },
|
|
populate: [
|
|
{
|
|
path: "currentFilamentStock",
|
|
populate: {
|
|
path: "filament",
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
if (!updatedPrinter) {
|
|
logger.error(
|
|
`Printer with ID ${this.id} not found when setting current filament stock`
|
|
);
|
|
return null;
|
|
}
|
|
|
|
logger.info(`Updated current filament stock for printer ${this.id}:`, {
|
|
filamentStock: updatedPrinter.currentFilamentStock,
|
|
});
|
|
|
|
return updatedPrinter.currentFilamentStock;
|
|
} catch (error) {
|
|
logger.error(
|
|
`Failed to set current filament stock for printer ${this.id}:`,
|
|
error
|
|
);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async updateFilamentStockWeight(
|
|
filamentStockId,
|
|
weight,
|
|
subJobId = null,
|
|
jobId = null
|
|
) {
|
|
try {
|
|
// Get or fetch filament stock
|
|
if (
|
|
!this.filamentStock ||
|
|
this.filamentStock._id.toString() !== filamentStockId
|
|
) {
|
|
const filamentStocks = await this.socketClient.listObjects({
|
|
objectType: "filamentStock",
|
|
filter: { _id: filamentStockId },
|
|
populate: ["stockEvents"],
|
|
});
|
|
|
|
if (!filamentStocks || filamentStocks.length === 0) {
|
|
logger.error(`Filament stock with ID ${filamentStockId} not found`);
|
|
return null;
|
|
}
|
|
|
|
this.filamentStock = filamentStocks[0];
|
|
}
|
|
|
|
// 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 = {
|
|
type: newNetWeight <= 0 ? "fullyconsumed" : "partiallyconsumed",
|
|
percent: (1 - remainingPercent).toFixed(2),
|
|
};
|
|
|
|
// Check if a stock event already exists for this subJobId and jobId
|
|
const stockEvents = await this.socketClient.listObjects({
|
|
objectType: "stockEvent",
|
|
filter: {
|
|
filamentStock: filamentStockId,
|
|
subJob: subJobId,
|
|
job: jobId,
|
|
},
|
|
});
|
|
|
|
let stockEvent;
|
|
if (stockEvents && stockEvents.length > 0) {
|
|
// Update existing event
|
|
this.existingEvent = stockEvents[0];
|
|
logger.debug(
|
|
`Updating existing stock event for subJobId ${subJobId} and jobId ${jobId}`
|
|
);
|
|
stockEvent = await this.socketClient.editObject({
|
|
_id: this.existingEvent._id,
|
|
objectType: "stockEvent",
|
|
updateData: {
|
|
value: weight,
|
|
updatedAt: new Date(),
|
|
},
|
|
});
|
|
} else {
|
|
// Create new stock event
|
|
logger.debug(
|
|
`Creating new stock event for subJobId ${subJobId} and jobId ${jobId}`
|
|
);
|
|
stockEvent = await this.socketClient.editObject({
|
|
_id: null, // This will create a new object
|
|
objectType: "stockEvent",
|
|
updateData: {
|
|
type: "subJob",
|
|
value: weight,
|
|
subJob: subJobId,
|
|
job: jobId,
|
|
filamentStock: filamentStockId,
|
|
unit: "g",
|
|
updatedAt: new Date(),
|
|
createdAt: new Date(),
|
|
},
|
|
});
|
|
|
|
// Add the new stock event to the filament stock
|
|
const updatedStockEvents = [
|
|
...this.filamentStock.stockEvents,
|
|
stockEvent._id,
|
|
];
|
|
await this.socketClient.editObject({
|
|
_id: filamentStockId,
|
|
objectType: "filamentStock",
|
|
updateData: { stockEvents: updatedStockEvents },
|
|
});
|
|
}
|
|
|
|
const oldState = this.filamentStock.state;
|
|
const updatedFilamentStock = await this.socketClient.editObject({
|
|
_id: filamentStockId,
|
|
objectType: "filamentStock",
|
|
updateData: {
|
|
currentNetWeight: newNetWeight,
|
|
currentGrossWeight: newGrossWeight,
|
|
state,
|
|
},
|
|
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,
|
|
});
|
|
|
|
return updatedFilamentStock;
|
|
} catch (error) {
|
|
logger.error(
|
|
`Failed to update filament stock weight for ${filamentStockId}:`,
|
|
error
|
|
);
|
|
throw error;
|
|
}
|
|
}
|
|
}
|