G92 senden
This commit is contained in:
77
server/driverClient.js
Normal file
77
server/driverClient.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* 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 })));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user