Enhance alert management in PrinterDatabase and PrinterClient. Introduce unique alert IDs using randomUUID, refactor alert removal methods to filter by code, and implement new methods for handling motion errors and axis movements. Update logging for improved clarity.
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit
This commit is contained in:
parent
8da48a158f
commit
8c6176b593
@ -1,3 +1,4 @@
|
||||
import { randomUUID } from "crypto";
|
||||
import log4js from "log4js";
|
||||
import NodeCache from "node-cache";
|
||||
import { loadConfig } from "../config.js";
|
||||
@ -369,14 +370,20 @@ export class PrinterDatabase {
|
||||
|
||||
async addAlert(alert) {
|
||||
try {
|
||||
logger.debug(`Adding alert to printer ${this.id}:`, alert);
|
||||
const alertWithId = {
|
||||
...alert,
|
||||
_id: alert._id ?? randomUUID(),
|
||||
};
|
||||
|
||||
logger.debug(`Adding alert to printer ${this.id}:`, alertWithId);
|
||||
|
||||
const printer = await this.getPrinter();
|
||||
const updatedAlerts = [...printer.alerts, alert];
|
||||
const updatedAlerts = [...printer.alerts, alertWithId];
|
||||
const updatedPrinter = await this.editPrinter({ alerts: updatedAlerts });
|
||||
|
||||
logger.info(`Added new alert to printer ${this.id}:`, {
|
||||
type: alert.type,
|
||||
code: alert.code,
|
||||
priority: alert.priority,
|
||||
hasMessage: !!alert.message,
|
||||
});
|
||||
@ -388,22 +395,18 @@ export class PrinterDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
async removeAlert(alertId) {
|
||||
async removeAlertById(alertId) {
|
||||
try {
|
||||
logger.debug(`Clearing alert ${alertId} for printer ${this.id}`);
|
||||
logger.debug(`Removing alert by id ${alertId} for printer ${this.id}`);
|
||||
|
||||
const printer = await this.getPrinter();
|
||||
let filteredAlerts = printer.alerts;
|
||||
|
||||
if (alertId) {
|
||||
filteredAlerts = filteredAlerts.filter(
|
||||
(alert) => alert._id !== alertId,
|
||||
);
|
||||
}
|
||||
const filteredAlerts = printer.alerts.filter(
|
||||
(alert) => alert._id !== alertId,
|
||||
);
|
||||
|
||||
const updatedPrinter = await this.editPrinter({ alerts: filteredAlerts });
|
||||
|
||||
logger.info(`Cleared alert ${alertId} for printer ${this.id}:`, {
|
||||
logger.info(`Removed alert ${alertId} for printer ${this.id}:`, {
|
||||
alertId,
|
||||
alerts: updatedPrinter.alerts,
|
||||
});
|
||||
@ -411,7 +414,33 @@ export class PrinterDatabase {
|
||||
return updatedPrinter;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Failed to clear alert ${alertId} for printer ${this.id}:`,
|
||||
`Failed to remove alert ${alertId} for printer ${this.id}:`,
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async removeAlertsByCode(code) {
|
||||
try {
|
||||
logger.debug(`Removing alerts by code ${code} for printer ${this.id}`);
|
||||
|
||||
const printer = await this.getPrinter();
|
||||
const filteredAlerts = printer.alerts.filter(
|
||||
(alert) => alert.code !== code,
|
||||
);
|
||||
|
||||
const updatedPrinter = await this.editPrinter({ alerts: filteredAlerts });
|
||||
|
||||
logger.info(`Removed alerts with code ${code} for printer ${this.id}:`, {
|
||||
code,
|
||||
alerts: updatedPrinter.alerts,
|
||||
});
|
||||
|
||||
return updatedPrinter;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Failed to remove alerts with code ${code} for printer ${this.id}:`,
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
@ -431,7 +460,9 @@ export class PrinterDatabase {
|
||||
}
|
||||
|
||||
// Sort alerts by priority
|
||||
alerts.sort((a, b) => a.priority.localeCompare(b.priority));
|
||||
alerts.sort((a, b) =>
|
||||
String(a.priority ?? "").localeCompare(String(b.priority ?? "")),
|
||||
);
|
||||
|
||||
logger.info(`Retrieved ${alerts.length} alerts for printer ${this.id}`);
|
||||
return alerts;
|
||||
|
||||
@ -285,7 +285,7 @@ export class PrinterClient {
|
||||
logger.error(
|
||||
`Klippy error for ${this.printer.name}: ${klippyResult.state_message}`,
|
||||
this.database.addAlert({
|
||||
_id: "klippyError",
|
||||
code: "klippyError",
|
||||
type: "error",
|
||||
message: klippyResult.state_message,
|
||||
actions: ["restart", "restartFirmware"],
|
||||
@ -297,7 +297,7 @@ export class PrinterClient {
|
||||
logger.error(
|
||||
`Klippy error for ${this.printer.name}: ${klippyResult.state_message}`,
|
||||
this.database.addAlert({
|
||||
_id: "klippyError",
|
||||
code: "klippyError",
|
||||
type: "error",
|
||||
message: klippyResult.state_message,
|
||||
actions: ["restart", "restartFirmware"],
|
||||
@ -310,6 +310,7 @@ export class PrinterClient {
|
||||
`Klippy startup message for ${this.printer.name}: ${klippyResult.state_message}`,
|
||||
this.database.addAlert({
|
||||
type: "info",
|
||||
code: "klippyStartup",
|
||||
message: klippyResult.state_message,
|
||||
priority: 8,
|
||||
timestamp: new Date(),
|
||||
@ -490,6 +491,64 @@ export class PrinterClient {
|
||||
}
|
||||
}
|
||||
|
||||
printerActionResult(success, error = null, code = null) {
|
||||
if (success) {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
const result = { success: false, error };
|
||||
if (code !== undefined && code !== null) {
|
||||
result.code = code;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async addMotionErrorAlert(error) {
|
||||
const code = error?.code;
|
||||
if (code === undefined || code === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.database.addAlert({
|
||||
code: `motionError${code}`,
|
||||
type: "error",
|
||||
priority: "9",
|
||||
message: error?.message || "Failed to execute printer movement command",
|
||||
canDismiss: true,
|
||||
});
|
||||
} catch (alertError) {
|
||||
logger.error(
|
||||
`Failed to add motion error alert for ${this.printer.name}:`,
|
||||
alertError,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async runGcodeScript(script) {
|
||||
if (!this.isOnline) {
|
||||
return this.printerActionResult(
|
||||
false,
|
||||
"Printer is not connected to Moonraker",
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.jsonRpc.callMethodWithKwargs("printer.gcode.script", {
|
||||
script,
|
||||
});
|
||||
return this.printerActionResult(true);
|
||||
} catch (error) {
|
||||
logger.error(`G-code script failed for ${this.printer.name}:`, error);
|
||||
await this.addMotionErrorAlert(error);
|
||||
return this.printerActionResult(
|
||||
false,
|
||||
error?.message || "Failed to send command to printer",
|
||||
error?.code,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async handleStatusUpdate(status) {
|
||||
logger.trace("Status update:", status);
|
||||
status = status[0];
|
||||
@ -706,9 +765,9 @@ export class PrinterClient {
|
||||
console.log(this.currentFilamentStock);
|
||||
|
||||
if (newFilamentDetected == false && this.currentFilamentStock == null) {
|
||||
await this.database.removeAlert("noFilamentLoaded");
|
||||
await this.database.removeAlertsByCode("noFilamentLoaded");
|
||||
await this.database.addAlert({
|
||||
_id: "noFilamentSelected",
|
||||
code: "noFilamentSelected",
|
||||
type: "info",
|
||||
actions: ["loadFilamentStock"],
|
||||
canDismiss: false,
|
||||
@ -721,9 +780,9 @@ export class PrinterClient {
|
||||
) {
|
||||
await this.setCurrentFilamentStock(null);
|
||||
await this.database.setCurrentFilamentStock(null);
|
||||
await this.database.removeAlert("noFilamentSelected");
|
||||
await this.database.removeAlertsByCode("noFilamentSelected");
|
||||
await this.database.addAlert({
|
||||
_id: "noFilamentLoaded",
|
||||
code: "noFilamentLoaded",
|
||||
type: "info",
|
||||
actions: ["loadFilamentStock"],
|
||||
canDismiss: false,
|
||||
@ -734,15 +793,15 @@ export class PrinterClient {
|
||||
newFilamentDetected == true &&
|
||||
this.currentFilamentStock != null
|
||||
) {
|
||||
await this.database.removeAlert("noFilamentSelected");
|
||||
await this.database.removeAlert("noFilamentLoaded");
|
||||
await this.database.removeAlertsByCode("noFilamentSelected");
|
||||
await this.database.removeAlertsByCode("noFilamentLoaded");
|
||||
} else if (
|
||||
newFilamentDetected == false &&
|
||||
this.currentFilamentStock != null
|
||||
) {
|
||||
await this.database.removeAlert("noFilamentLoaded");
|
||||
await this.database.removeAlertsByCode("noFilamentLoaded");
|
||||
await this.database.addAlert({
|
||||
_id: "noFilamentLoaded",
|
||||
code: "noFilamentLoaded",
|
||||
type: "info",
|
||||
message:
|
||||
"No filament loaded. Please load filament to continue printing.",
|
||||
@ -1330,8 +1389,103 @@ export class PrinterClient {
|
||||
}
|
||||
}
|
||||
|
||||
async homeAxis(axis) {
|
||||
|
||||
async homeAxis({ axis }) {
|
||||
logger.info(`Homing axis for ${this.printer.name}:`, axis);
|
||||
|
||||
if (!this.isOnline) {
|
||||
logger.error(
|
||||
`Cannot home axis: Not connected to Moonraker (${this.printer.name})`,
|
||||
);
|
||||
return this.printerActionResult(
|
||||
false,
|
||||
"Printer is not connected to Moonraker",
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if (!axis) {
|
||||
logger.warn(`No axis provided for homing on ${this.printer.name}`);
|
||||
return this.printerActionResult(false, "No axis provided for homing");
|
||||
}
|
||||
|
||||
const gcodeCommand = axis === "ALL" ? "G28" : `G28 ${axis}`;
|
||||
const result = await this.runGcodeScript(gcodeCommand);
|
||||
|
||||
if (!result.success) {
|
||||
logger.error(
|
||||
`Failed to home axis with command: ${gcodeCommand}`,
|
||||
result.error,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
logger.info(`Successfully homed axis for ${this.printer.name}`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error(`Error homing axis for ${this.printer.name}:`, error);
|
||||
await this.addMotionErrorAlert(error);
|
||||
return this.printerActionResult(
|
||||
false,
|
||||
error?.message || "Failed to home axis",
|
||||
error?.code,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async moveAxis({ axis, distance, rate }) {
|
||||
logger.info(`Moving axis for ${this.printer.name}:`, {
|
||||
axis,
|
||||
distance,
|
||||
rate,
|
||||
});
|
||||
|
||||
if (!this.isOnline) {
|
||||
logger.error(
|
||||
`Cannot move axis: Not connected to Moonraker (${this.printer.name})`,
|
||||
);
|
||||
return this.printerActionResult(
|
||||
false,
|
||||
"Printer is not connected to Moonraker",
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const validAxes = ["X", "Y", "Z", "E"];
|
||||
if (!axis || !validAxes.includes(axis)) {
|
||||
logger.warn(
|
||||
`Invalid axis provided for move on ${this.printer.name}: ${axis}`,
|
||||
);
|
||||
return this.printerActionResult(false, `Invalid axis: ${axis}`);
|
||||
}
|
||||
|
||||
if (distance === undefined || distance === null) {
|
||||
logger.warn(`No distance provided for move on ${this.printer.name}`);
|
||||
return this.printerActionResult(false, "No distance provided for move");
|
||||
}
|
||||
|
||||
const feedRate = rate ?? 1000;
|
||||
const gcodeCommand = `G91\nG1 ${axis}${distance} F${feedRate}\nG90`;
|
||||
const result = await this.runGcodeScript(gcodeCommand);
|
||||
|
||||
if (!result.success) {
|
||||
logger.error(
|
||||
`Failed to move axis with command: ${gcodeCommand}`,
|
||||
result.error,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
logger.info(`Successfully moved axis for ${this.printer.name}`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error(`Error moving axis for ${this.printer.name}:`, error);
|
||||
await this.addMotionErrorAlert(error);
|
||||
return this.printerActionResult(
|
||||
false,
|
||||
error?.message || "Failed to move axis",
|
||||
error?.code,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async restartPrinterFirmware() {
|
||||
@ -1525,8 +1679,8 @@ export class PrinterClient {
|
||||
await this.database.setCurrentFilamentStock(filamentStock);
|
||||
await this.setCurrentFilamentStock(filamentStock);
|
||||
await this.getCurrentFilament();
|
||||
await this.database.removeAlert("noFilamentSelected");
|
||||
await this.database.removeAlert("noFilamentLoaded");
|
||||
await this.database.removeAlertsByCode("noFilamentSelected");
|
||||
await this.database.removeAlertsByCode("noFilamentLoaded");
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
|
||||
@ -79,6 +79,16 @@ export class PrinterManager {
|
||||
callback(setTempResult);
|
||||
return;
|
||||
|
||||
case "homeAxis":
|
||||
const homeAxisResult = await printer.homeAxis(action.data);
|
||||
callback(homeAxisResult);
|
||||
return;
|
||||
|
||||
case "moveAxis":
|
||||
const moveAxisResult = await printer.moveAxis(action.data);
|
||||
callback(moveAxisResult);
|
||||
return;
|
||||
|
||||
case "restartPrinterFirmware":
|
||||
const restartPrinterFirmwareResult =
|
||||
await printer.restartPrinterFirmware();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user