Files
appRobotHoming/server/driverClient.js
2026-06-25 17:16:30 +02:00

78 lines
2.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* driverClient.js WebSocket-Transport zum appRobotDriver
*
* Der Driver nimmt Steuerbefehle als Plain-Text-G-Code über einen WebSocket
* entgegen (wss://…:2096, self-signed), NICHT über HTTP — siehe
* appRobotDriver/doc/API.md. Ein früher angenommenes `POST /api/state` existiert
* dort nicht (war Platzhalter, vgl. doc/accessRobotAPI.md). G92 setzt am Driver
* die Motorposition ohne Bewegung (intern M92) = exakt die Homing-Semantik.
*
* DRIVER_WS_URL nicht gesetzt → kein Kontakt, klarer 501-Fehler (analog zum
* früheren ROBOT_URL-Verhalten).
*/
import { WebSocket } from 'ws';
const DRIVER_WS_URL = process.env.DRIVER_WS_URL || '';
/** true, wenn ein Driver-WebSocket konfiguriert ist. */
export function isDriverConfigured() {
return Boolean(DRIVER_WS_URL);
}
/**
* Öffnet eine kurzlebige WS-Verbindung zum Driver, sendet eine G-Code-Zeile und
* wartet auf die erste Antwort (Positions-JSON bzw. Fehler-Envelope). Der Driver
* broadcastet nach jedem G-Code das aktuelle Positions-JSON an alle Clients —
* der Sender ist selbst Client und bekommt es zurück.
*
* @param {string} line z.B. "G92 X1 Y2 …"
* @param {{timeoutMs?: number}} [opts]
* @returns {Promise<{ok:boolean, sent:string, response?:any, error?:string, note?:string}>}
*/
export function sendGcode(line, { timeoutMs = 4000 } = {}) {
const text = String(line ?? '').trim();
if (!text) {
return Promise.reject(Object.assign(new Error('Leere G-Code-Zeile'), { statusCode: 400 }));
}
if (!DRIVER_WS_URL) {
return Promise.reject(Object.assign(
new Error('DRIVER_WS_URL ist nicht konfiguriert'), { statusCode: 501 }));
}
return new Promise((resolve, reject) => {
// Self-signed Cert am Driver → Zertifikatsprüfung deaktivieren (interner Hop).
const ws = new WebSocket(DRIVER_WS_URL, { rejectUnauthorized: false });
let settled = false;
const finish = (fn, arg) => {
if (settled) return;
settled = true;
clearTimeout(timer);
try { ws.close(); } catch { /* egal */ }
fn(arg);
};
// Gesendet, aber keine Antwort rechtzeitig: kein harter Fehler — der Befehl
// ist raus, der Driver antwortet nur evtl. nicht broadcastfähig.
const timer = setTimeout(() => {
finish(resolve, { ok: true, sent: text, response: null, note: 'keine Antwort (Timeout)' });
}, timeoutMs);
ws.on('open', () => ws.send(text));
ws.on('message', (data) => {
const raw = data.toString();
let parsed;
try { parsed = JSON.parse(raw); } catch { parsed = raw; }
if (parsed && typeof parsed === 'object' && parsed.type === 'error') {
finish(resolve, { ok: false, sent: text, error: parsed.message || raw, response: parsed });
} else {
finish(resolve, { ok: true, sent: text, response: parsed });
}
});
ws.on('error', (err) => finish(reject, Object.assign(
new Error(`Driver-WS-Fehler: ${err.message}`), { statusCode: 502 })));
});
}