Claude API
This commit is contained in:
@@ -2,7 +2,19 @@
|
||||
const fs = require('fs');
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const LOG_DIR = './logs';
|
||||
|
||||
/**
|
||||
* Ensures the log directory exists so the first appendFileSync() call cannot
|
||||
* crash on a fresh container/checkout. Idempotent thanks to { recursive: true }.
|
||||
*/
|
||||
function ensureLogDir(dir = LOG_DIR) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
function initInputWS(server, robot, GCode, sharedState) {
|
||||
ensureLogDir();
|
||||
|
||||
const wss = new WebSocket.Server({ server });
|
||||
|
||||
wss.on('connection', (ws) => {
|
||||
@@ -19,34 +31,53 @@ function initInputWS(server, robot, GCode, sharedState) {
|
||||
ws.on('message', (msg) => {
|
||||
const message = msg.toString();
|
||||
|
||||
/* ---------- Ping ---------- */
|
||||
/* ---------- Ping (heartbeat) → targeted reply to requester ---------- */
|
||||
if (message === "Ping") {
|
||||
logPing(sharedState, clientIP);
|
||||
broadcast(wss, message);
|
||||
reply(ws, "Ping");
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---------- GCode ---------- */
|
||||
/* ---------- M114 (status query) → targeted reply to requester ---------- */
|
||||
if (message === "M114") {
|
||||
logCommand(sharedState, clientIP, message);
|
||||
reply(ws, GCode.getM114(robot));
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---------- G-code (motion command) → broadcast new state to all ----------
|
||||
* A move changes the robot position, which is a status update every client
|
||||
* (e.g. the simulation) should see → broadcast. Parsing/execution failures
|
||||
* are reported back to the sender as a machine-readable error. */
|
||||
if (GCode.containsCommand(message)) {
|
||||
console.log("🔵 GCode.receiveGCode: Incoming command: " + message);
|
||||
logCommand(sharedState, clientIP, message);
|
||||
GCode.receiveGCode(robot, message);
|
||||
try {
|
||||
GCode.receiveGCode(robot, message);
|
||||
} catch (err) {
|
||||
return sendError(ws, 'GCODE_ERROR', err.message, message);
|
||||
}
|
||||
broadcast(wss, GCode.getM114(robot));
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---------- File Commands ---------- */
|
||||
/* ---------- File commands → broadcast result ----------
|
||||
* Behaviour kept as-is on purpose: file/log management (and finer-grained
|
||||
* targeting, e.g. FShow as a requester-only reply) is owned by ToDo 4. */
|
||||
if (GCode.ContainsFilesCommand(message)) {
|
||||
logCommand(sharedState, clientIP, message);
|
||||
broadcast(wss, GCode.receiveFC(robot, message));
|
||||
let result;
|
||||
try {
|
||||
result = GCode.receiveFC(robot, message);
|
||||
} catch (err) {
|
||||
return sendError(ws, 'FILE_ERROR', err.message, message);
|
||||
}
|
||||
if (result !== undefined) broadcast(wss, result);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---------- Status ---------- */
|
||||
if (message === "M114") {
|
||||
logCommand(sharedState, clientIP, message);
|
||||
broadcast(wss, GCode.getM114(robot));
|
||||
}
|
||||
/* ---------- Unknown input → targeted machine-readable error ---------- */
|
||||
sendError(ws, 'UNKNOWN_COMMAND', 'Unrecognized input', message);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,6 +86,21 @@ function initInputWS(server, robot, GCode, sharedState) {
|
||||
|
||||
/* ---------- Helpers ---------- */
|
||||
|
||||
/** Targeted reply to a single client (status updates use broadcast instead). */
|
||||
function reply(ws, payload) {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(payload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Machine-readable error response, sent only to the requesting client.
|
||||
* Shape: { type: "error", code, message, input }
|
||||
*/
|
||||
function sendError(ws, code, message, input) {
|
||||
reply(ws, JSON.stringify({ type: 'error', code, message, input }));
|
||||
}
|
||||
|
||||
function broadcast(wss, payload) {
|
||||
wss.clients.forEach(client => {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
@@ -65,7 +111,7 @@ function broadcast(wss, payload) {
|
||||
|
||||
function logCommand(state, ip, message) {
|
||||
fs.appendFileSync(
|
||||
'./logs/gcode_commands.log',
|
||||
`${LOG_DIR}/gcode_commands.log`,
|
||||
`${new Date().toISOString()} ${ip}: ${message}\n`
|
||||
);
|
||||
state.lastCommands.push(`${new Date().toISOString()}: ${message}`);
|
||||
@@ -74,11 +120,12 @@ function logCommand(state, ip, message) {
|
||||
|
||||
function logPing(state, ip) {
|
||||
fs.appendFileSync(
|
||||
'./logs/pings.log',
|
||||
`${LOG_DIR}/pings.log`,
|
||||
`${new Date().toISOString()} ${ip} : Ping\n`
|
||||
);
|
||||
state.lastPings.push(`${new Date().toISOString()} ${ip} : Ping`);
|
||||
if (state.lastPings.length > 10) state.lastPings.shift();
|
||||
}
|
||||
|
||||
module.exports = initInputWS;
|
||||
module.exports = initInputWS;
|
||||
module.exports.ensureLogDir = ensureLogDir;
|
||||
Reference in New Issue
Block a user