Tom Butcher 5db74f2c5c
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit
New version of farmcontrol-server. Includes electron UI, connects only via WS and authenticates using OTP and then auth code.
2025-09-05 23:33:21 +01:00

96 lines
2.7 KiB
JavaScript

// jsonrpc.js - Implementation of JSON-RPC 2.0 protocol for Moonraker communication
import { loadConfig } from "../config.js";
import log4js from "log4js";
// Load configuration
const config = loadConfig();
const logger = log4js.getLogger("JSON RPC");
logger.level = config.logLevel;
export class JsonRPC {
constructor() {
this.idCounter = 0;
this.methods = {};
this.pendingRequests = {};
}
// Generate a unique ID for RPC requests
generateId() {
return this.idCounter++;
}
// Register a method to handle incoming notifications/responses
registerMethod(methodName, callback) {
this.methods[methodName] = callback;
}
// Process incoming messages
processMessage(message) {
if (message.method && this.methods[message.method]) {
// Handle method call or notification
this.methods[message.method](message.params);
logger.trace(`JSON-RPC notification: ${message.method}`);
} else if (message.id !== undefined) {
// Handle response to a previous request
const rpcPromise = this.pendingRequests[message.id];
if (rpcPromise) {
if (message.error) {
logger.error(`Error in JSON-RPC response: ${message.error}`);
rpcPromise.reject(message.error);
} else {
logger.debug(`JSON-RPC response: OK`);
logger.trace("Result:", message.result);
rpcPromise.resolve(message.result);
}
delete this.pendingRequests[message.id];
}
}
// If it's a notification without a registered method, ignore it
}
// Call a method without parameters
callMethod(method) {
return this.callMethodWithKwargs(method, {});
}
// Call a method with parameters
callMethodWithKwargs(method, params) {
logger.debug(`Calling method: ${method}`);
logger.trace("Params:", params);
const id = this.generateId();
const request = {
jsonrpc: "2.0",
method: method,
params: params,
id: id,
};
return new Promise((resolve, reject) => {
this.pendingRequests[id] = { resolve, reject };
// The actual sending of the message is done by the WebSocket connection
// This just prepares the message and returns a promise
if (this.socket) {
this.socket.send(JSON.stringify(request));
} else {
// If socket is not directly attached to this instance, the caller
// is responsible for sending the serialized request
this.lastRequest = JSON.stringify(request);
}
});
}
// For external socket handling
getLastRequest() {
const req = this.lastRequest;
this.lastRequest = null;
return req;
}
// Associate a WebSocket with this RPC instance for direct communication
setSocket(socket) {
this.socket = socket;
}
}