UNPKG

@fdm-monster/server

Version:

FDM Monster is a bulk OctoPrint manager to set up, configure and monitor 3D printers. Our aim is to provide extremely optimized websocket performance and reliability.

350 lines (349 loc) 14.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "MoonrakerWebsocketAdapter", { enumerable: true, get: function() { return MoonrakerWebsocketAdapter; } }); const _serverconstants = require("../../server.constants"); const _urlutils = require("../../utils/url.utils"); const _octoprintwebsocketadapter = require("../octoprint/octoprint-websocket.adapter"); const _moonrakerconstants = require("./constants/moonraker.constants"); const _socketstatetype = require("../../shared/dtos/socket-state.type"); const _apistatetype = require("../../shared/dtos/api-state.type"); const _prettyprintutils = require("../../utils/pretty-print.utils"); const _lodash = /*#__PURE__*/ _interop_require_default(require("lodash")); const _printerapiinterface = require("../printer-api.interface"); const _websocketrpcextendedadapter = require("../../shared/websocket-rpc-extended.adapter"); const _normalizeurl = require("../../utils/normalize-url"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } class MoonrakerWebsocketAdapter extends _websocketrpcextendedadapter.WebsocketRpcExtendedAdapter { moonrakerClient; eventEmitter2; configService; serverVersion; printerType; socketState; lastMessageReceivedTimestamp; stateUpdated; stateUpdateTimestamp; apiStateUpdated; apiStateUpdateTimestamp; apiState; login; printerId; refreshPrinterObjectsInterval; printerObjects; socketURL; constructor(loggerFactory, moonrakerClient, eventEmitter2, configService, serverVersion){ super(loggerFactory), this.moonrakerClient = moonrakerClient, this.eventEmitter2 = eventEmitter2, this.configService = configService, this.serverVersion = serverVersion, this.printerType = 1, this.socketState = _socketstatetype.SOCKET_STATE.unopened, this.lastMessageReceivedTimestamp = null, this.stateUpdated = false, this.stateUpdateTimestamp = null, this.apiStateUpdated = false, this.apiStateUpdateTimestamp = null, this.apiState = _apistatetype.API_STATE.unset, this.printerObjects = { eventtime: null, status: null }; this.logger = loggerFactory(MoonrakerWebsocketAdapter.name); } get _debugMode() { return this.configService.get(_serverconstants.AppConstants.debugSocketStatesKey, _serverconstants.AppConstants.defaultDebugSocketStates) === "true"; } get subscriptionObjects() { return { pause_resume: [], idle_timeout: [], print_stats: [], heaters: [], heater_bed: [], extruder: [], display_status: [], webhooks: [], virtual_sdcard: [], gcode_move: [], stepper_enable: [], fan: [], motion_report: [], system_stats: [] }; } needsReopen() { return false; } needsSetup() { return this.socketState === _socketstatetype.SOCKET_STATE.unopened; } needsReauth() { return false; } async reauthSession() {} registerCredentials(socketLogin) { const { printerId, loginDto } = socketLogin; this.printerId = printerId; this.login = loginDto; const httpUrlString = (0, _normalizeurl.normalizeUrl)(this.login.printerURL); const httpUrl = new URL(httpUrlString); const httpUrlPath = httpUrl.pathname; const wsUrl = (0, _urlutils.httpToWsUrl)(httpUrlString); wsUrl.pathname = (httpUrlPath ?? "/") + "websocket"; this.socketURL = wsUrl; } open() { if (this.socket) { throw new Error(`Socket already exists by printerId, ignoring open request`); } super.open(this.socketURL); } close() { clearInterval(this.refreshPrinterObjectsInterval); super.close(); } async setupSocketSession() { this.resetSocketState(); await this.moonrakerClient.getApiVersion(this.login).catch((e)=>{ this.setSocketState("aborted"); this.logger.error(`Printer (${this.printerId}) network or transport error, marking it as unreachable; ${e}`); this.setApiState("noResponse"); throw e; }); this.setApiState(_apistatetype.API_STATE.responding); await this.updateCurrentStateSafely(); if (this.refreshPrinterObjectsInterval) { clearInterval(this.refreshPrinterObjectsInterval); } this.refreshPrinterObjectsInterval = setInterval(async ()=>{ await this.updateCurrentStateSafely(); }, 15000); } emitEventSync(event, payload) { if (!this.eventEmittingAllowed) { return; } this.eventEmitter2.emit((0, _moonrakerconstants.moonrakerEvent)(event), { event, payload, printerId: this.printerId }); } resetSocketState() { this.setSocketState("unopened"); this.setApiState("unset"); } isClosedOrAborted() { return this.socketState === _socketstatetype.SOCKET_STATE.closed || this.socketState === _socketstatetype.SOCKET_STATE.aborted; } async afterOpened(_) { this.setSocketState(_socketstatetype.SOCKET_STATE.opened); const response = await this.sendRequest({ jsonrpc: "2.0", method: "server.connection.identify", params: { client_name: "FDM Monster", version: this.serverVersion, type: "other", url: _serverconstants.AppConstants.githubUrl }, id: 1 }); try { const query = this.subscriptionObjects; const result = await this.moonrakerClient.postSubscribePrinterObjects(this.login, response.result.connection_id, query); this.printerObjects = result.data.result; await this.emitCurrentEvent(this.printerObjects); } catch (e) { const ae = e; if (ae.isAxiosError) { if (ae.response?.status === 503) { this.logger.warn(`Klipper host issue ${(0, _prettyprintutils.PP)(ae.response.data?.error?.message)}`); } else if (ae.response?.status === 404) { this.logger.error("Error while afterOpened (404) - usually this means Moonraker is still starting"); } this.setApiState(_apistatetype.API_STATE.noResponse); return; } this.logger.error("Unknown error while afterOpened"); this.setApiState(_apistatetype.API_STATE.noResponse); } } async onEventMessage(event) { this.lastMessageReceivedTimestamp = Date.now(); if (this.socketState !== _socketstatetype.SOCKET_STATE.authenticated) { this.setSocketState("authenticated"); } const eventName = event.method; if (this._debugMode) { this.logger.log(`RX Msg ${eventName} ${JSON.stringify(event.params)?.substring(0, 80)}...`); } const payload = event.params?.length ? event.params[0] : undefined; if (eventName === "notify_service_state_changed") { if (!event.params) { this.logger.error("Received 'notify_service_state_changed' but service indicators params were undefined"); return; } const serviceChanged = event.params[0]; if (serviceChanged.klipper?.active_state || serviceChanged.klipper_mcu?.active_state || serviceChanged.moonraker?.active_state) { this.logger.log("Received notify_service_state_changed, reloading Moonraker printer objects"); await this.setupSocketSession(); } return; } if (eventName === "notify_klippy_ready") { this.logger.log("Received notify_klippy_ready, reloading Moonraker printer objects"); return await this.setupSocketSession(); } if (eventName === "notify_klippy_disconnected") { this.logger.log("Received notify_klippy_disconnected, reloading Moonraker printer objects"); return await this.setupSocketSession(); } if (eventName === "notify_klippy_shutdown") { this.logger.log("Received notify_klippy_shutdown, reloading Moonraker printer objects"); return await this.setupSocketSession(); } if (eventName === "notify_status_update") { if (!event.params) { this.logger.error("Received 'notify_status_update' but service indicators params were undefined"); return; } const [data, eventtime] = event.params; const subState = Object.keys(data)[0]; if (Object.keys(this.printerObjects.status).includes(subState)) { this.printerObjects.status = _lodash.default.merge(this.printerObjects.status, data); this.printerObjects.eventtime = eventtime; await this.emitCurrentEvent(this.printerObjects); } else { this.logger.warn(`Substate ${subState} unknown`); } return; } await this.emitEvent(eventName, payload); } async afterClosed(event) { this.setSocketState("closed"); delete this.socket; await this.emitEvent(_octoprintwebsocketadapter.WsMessage.WS_CLOSED, "connection closed"); } async onError(error) { this.setSocketState("error"); await this.emitEvent(_octoprintwebsocketadapter.WsMessage.WS_ERROR, error?.length ? error : "connection error"); } async updateCurrentStateSafely() { try { const query = this.subscriptionObjects; const objects = await this.moonrakerClient.getPrinterObjectsQuery(this.login, query); this.printerObjects = objects.data.result; this.setApiState(_apistatetype.API_STATE.responding); return await this.emitCurrentEvent(this.printerObjects); } catch (e) { const castError = e; if (castError.isAxiosError) { if (castError?.response?.status == 503) { this.printerObjects.status = null; this.printerObjects.eventtime = Date.now(); return await this.emitCurrentEvent(this.printerObjects); } this.logger.error("Could not update Moonraker printer objects due to a request error"); this.setApiState(_apistatetype.API_STATE.noResponse); return; } this.logger.error(`Could not update Moonraker current due to an unknown error`); this.setApiState(_apistatetype.API_STATE.noResponse); } } async emitCurrentEvent(printerObject) { const originalKlipperObjects = printerObject.status; const flags = { operational: false, printing: false, cancelling: false, pausing: false, paused: false, resuming: false, finishing: false, closedOrError: false, error: false, ready: false, sdReady: false }; let filename = ""; let printTime = null; let stateText = "Unset"; let error = ""; let completion = null; if (originalKlipperObjects != null) { stateText = originalKlipperObjects.display_status?.message; if (originalKlipperObjects.print_stats?.state?.length) { const systemState = originalKlipperObjects.webhooks; const printState = originalKlipperObjects.print_stats.state; const idleState = originalKlipperObjects.idle_timeout?.state; filename = originalKlipperObjects.print_stats.filename; printTime = originalKlipperObjects.print_stats.print_duration; flags.operational = systemState.state === "ready"; if (flags.operational) { flags.printing = printState === "printing"; flags.paused = printState === "paused"; flags.ready = printState === "standby" && idleState !== "Printing"; flags.sdReady = true; } else { flags.error = true; stateText = "Klipper reports: " + (systemState.state ?? "unknown")?.toUpperCase(); } } completion = (originalKlipperObjects.display_status?.progress ?? 0) * 100.0; } const currentMessage = { progress: { printTime, completion }, state: { text: stateText, error, flags }, job: { file: { name: filename, path: filename } } }; await this.emitEvent("notify_status_update", originalKlipperObjects); await this.emitEvent("current", currentMessage); } async emitEvent(event, payload) { if (!this.eventEmittingAllowed) { return; } await this.eventEmitter2.emitAsync((0, _moonrakerconstants.moonrakerEvent)(event), { event, payload, printerId: this.printerId, printerType: _printerapiinterface.MoonrakerType }); } setSocketState(state) { this.socketState = state; this.stateUpdated = true; this.stateUpdateTimestamp = Date.now(); if (this._debugMode) { this.logger.log(`${this.printerId} Socket state updated to: ` + state); } this.emitEventSync(_octoprintwebsocketadapter.WsMessage.WS_STATE_UPDATED, state); } setApiState(state) { if (state === _apistatetype.API_STATE.globalKey) { throw new Error("GlobalKey is an invalid WS state for Moonraker"); } this.apiState = state; this.apiStateUpdated = true; this.apiStateUpdateTimestamp = Date.now(); if (this._debugMode) { this.logger.log(`${this.printerId} API state updated to: ` + state); } this.emitEventSync(_octoprintwebsocketadapter.WsMessage.API_STATE_UPDATED, state); } } //# sourceMappingURL=moonraker-websocket.adapter.js.map