WS statt Telnet
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -43,6 +43,9 @@ build/Release
|
|||||||
node_modules/
|
node_modules/
|
||||||
jspm_packages/
|
jspm_packages/
|
||||||
|
|
||||||
|
# SSH keys
|
||||||
|
*.pem
|
||||||
|
|
||||||
# Snowpack dependency directory (https://snowpack.dev/)
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
web_modules/
|
web_modules/
|
||||||
|
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
const net = require("net");
|
|
||||||
|
|
||||||
class TelnetConnection {
|
|
||||||
|
|
||||||
constructor(config){
|
|
||||||
|
|
||||||
this.host = config.host;
|
|
||||||
this.port = config.port;
|
|
||||||
this.reconnectDelay = config.reconnectDelay || 30000;
|
|
||||||
|
|
||||||
this.socket = null;
|
|
||||||
|
|
||||||
this.dataListeners = [];
|
|
||||||
this.connectionListeners = [];
|
|
||||||
|
|
||||||
this.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(){
|
|
||||||
|
|
||||||
console.log("Connecting to FluidNC:", this.host);
|
|
||||||
|
|
||||||
this.socket = net.createConnection({
|
|
||||||
host: this.host,
|
|
||||||
port: this.port
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("connect",()=>{
|
|
||||||
|
|
||||||
console.log("FluidNC connected");
|
|
||||||
|
|
||||||
this.connectionListeners.forEach(fn=>fn(true));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("data",(data)=>{
|
|
||||||
|
|
||||||
const msg = data.toString();
|
|
||||||
|
|
||||||
this.dataListeners.forEach(fn=>fn(msg));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("close",()=>{
|
|
||||||
|
|
||||||
console.log("FluidNC disconnected");
|
|
||||||
|
|
||||||
this.connectionListeners.forEach(fn=>fn(false));
|
|
||||||
|
|
||||||
setTimeout(()=>{
|
|
||||||
this.connect();
|
|
||||||
}, this.reconnectDelay);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
this.socket.on("error",(err)=>{
|
|
||||||
console.log("Telnet error:",err.message);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
send(cmd){
|
|
||||||
|
|
||||||
if(!this.socket) return;
|
|
||||||
|
|
||||||
this.socket.write(cmd+"\n");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
realtime(cmd){
|
|
||||||
|
|
||||||
if(!this.socket) return;
|
|
||||||
|
|
||||||
this.socket.write(cmd);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
onData(fn){
|
|
||||||
this.dataListeners.push(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
onConnection(fn){
|
|
||||||
this.connectionListeners.push(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = TelnetConnection;
|
|
||||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -7,9 +7,11 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "fluidnc-webcontrol",
|
"name": "fluidnc-webcontrol",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2",
|
"express": "^4.22.1",
|
||||||
"ws": "^8.16.0"
|
"telnet-stream": "^1.1.0",
|
||||||
|
"ws": "^8.19.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
@@ -214,6 +216,7 @@
|
|||||||
"version": "4.22.1",
|
"version": "4.22.1",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
|
||||||
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
|
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.8",
|
"accepts": "~1.3.8",
|
||||||
"array-flatten": "1.1.1",
|
"array-flatten": "1.1.1",
|
||||||
@@ -712,6 +715,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/telnet-stream": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/telnet-stream/-/telnet-stream-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-saVuav/ScOFlrQXSB8xUqwwIi3sifjSfBiiywGMu7B8wJz60duqFCyG8IhcRjoPpl6VGGkBH+yH3Tnbxh36h1Q==",
|
||||||
|
"license": "AGPL-3.0"
|
||||||
|
},
|
||||||
"node_modules/toidentifier": {
|
"node_modules/toidentifier": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||||
@@ -760,6 +769,7 @@
|
|||||||
"version": "8.19.0",
|
"version": "8.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||||
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
16
package.json
16
package.json
@@ -6,7 +6,17 @@
|
|||||||
"start": "node server/server.js"
|
"start": "node server/server.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2",
|
"express": "^4.22.1",
|
||||||
"ws": "^8.16.0"
|
"telnet-stream": "^1.1.0",
|
||||||
}
|
"ws": "^8.19.0"
|
||||||
|
},
|
||||||
|
"description": "WebPage to show X-Z-Options. The FluidNC is connected to this app.",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "http://thinkcentre.local:3000/ChK/appRobotControlScara.git"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"type": "commonjs"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
fluidnc: {
|
fluidnc: {
|
||||||
host: "fluidncsilver.local",
|
host: "fluidncred.local",
|
||||||
port: 81,
|
port: 80,
|
||||||
reconnectDelay: 30000
|
reconnectDelay: 30000
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,104 +1,69 @@
|
|||||||
|
const WebSocket = require("ws");
|
||||||
|
const EventEmitter = require("events");
|
||||||
|
|
||||||
const config = require("../config/config");
|
class FluidNCClient extends EventEmitter {
|
||||||
const net = require("net");
|
constructor(cfg) {
|
||||||
const { resolve } = require("path");
|
super();
|
||||||
const TelnetSocket = require("telnet-stream");
|
|
||||||
|
|
||||||
|
this.host = cfg.host;
|
||||||
|
this.port = cfg.port || 81;
|
||||||
|
|
||||||
|
this.ws = null;
|
||||||
|
this.reconnectDelay = 2000;
|
||||||
|
|
||||||
class FluidNCClient {
|
this.connect();
|
||||||
constructor() {
|
|
||||||
console.log("[FluidNCClient] Initializing...");
|
|
||||||
|
|
||||||
this.url = config.fluidnc.host;
|
|
||||||
this.port = config.fluidnc.port || 23;
|
|
||||||
this.tSocket = null;
|
|
||||||
this.connected = false;
|
|
||||||
this.state = { x:0, z:0, state:"unknown" };
|
|
||||||
this.listeners = [];
|
|
||||||
|
|
||||||
this._connect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_connect() {
|
connect() {
|
||||||
console.log(`[FluidNCClient] Connecting to FluidNC: ${this.url}`);
|
const url = `ws://${this.host}:${this.port}`;
|
||||||
|
console.log("[FluidNC] Connecting to:", url);
|
||||||
|
|
||||||
let socket = null;
|
this.ws = new WebSocket(url);
|
||||||
|
|
||||||
new Promise((resolve, reject) => {
|
this.ws.on("open", () => {
|
||||||
socket = net.createConnection({host:this.url, port:this.port}, () => {
|
console.log("[FluidNC] Connected (WS)");
|
||||||
resolve(socket);
|
|
||||||
}).on("error", reject);
|
|
||||||
}).then(connection => {
|
|
||||||
console.log("[FluidNCClient] Telnet socket connected");
|
|
||||||
this.connected = true;
|
|
||||||
|
|
||||||
connection.on("data", data => {
|
|
||||||
const msg = data.toString().replace(/\r/g,"").trim();
|
|
||||||
if(!msg) return;
|
|
||||||
console.log("[FluidNCClient] Received:", msg);
|
|
||||||
|
|
||||||
if(msg.startsWith("<") && msg.includes("MPos:")){
|
|
||||||
const match = msg.match(/MPos:([\d\.\-]+),[\d\.\-]+,([\d\.\-]+)/);
|
|
||||||
if(match){
|
|
||||||
this.state.x = parseFloat(match[1]);
|
|
||||||
this.state.z = parseFloat(match[2]);
|
|
||||||
this.state.state = msg.split(",")[0].replace("<","");
|
|
||||||
this._broadcast(this.state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if(socket != null){
|
this.ws.on("message", (msg) => {
|
||||||
this.tSocket = TelnetSocket(socket);
|
this.emit("message", msg.toString());
|
||||||
|
});
|
||||||
|
|
||||||
this.tSocket.on("close", () => {
|
this.ws.on("close", () => {
|
||||||
console.log("[FluidNCClient] Telnet Closed");
|
console.log("[FluidNC] Disconnected → retry");
|
||||||
this.tSocket = null;
|
setTimeout(() => this.connect(), this.reconnectDelay);
|
||||||
this.connected = false;
|
});
|
||||||
setTimeout(()=>this._connect(), 30000);
|
|
||||||
|
this.ws.on("error", (err) => {
|
||||||
|
console.log("[FluidNC] WS Error:", err.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}, error => {
|
// --- BASIC COMMANDS ---
|
||||||
console.log("[FluidNCClient] Telnet Connection Error:", error.toString());
|
sendLine(cmd) {
|
||||||
this.tSocket = null;
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
||||||
this.connected = false;
|
this.ws.send(cmd + "\n");
|
||||||
setTimeout(()=>this._connect(), 30000);
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_broadcast(msg){
|
requestStatus() {
|
||||||
this.listeners.forEach(fn => fn(msg));
|
this.sendLine("?");
|
||||||
}
|
|
||||||
|
|
||||||
onMessage(fn){
|
|
||||||
this.listeners.push(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
send(cmd){
|
|
||||||
if(this.tSocket){
|
|
||||||
console.log("[FluidNCClient] Sending:", cmd);
|
|
||||||
this.tSocket.write(cmd + "\r\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jog(axis, value) {
|
jog(axis, value) {
|
||||||
if(!this.connected) return;
|
const cmd = `$J=G91 ${axis}${value} F2000`;
|
||||||
this.send("G91");
|
this.sendLine(cmd);
|
||||||
this.send(`G0 ${axis.toUpperCase()}${value}`);
|
|
||||||
this.send("G90");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sendGcode(cmd) {
|
sendGcode(cmd) {
|
||||||
if(!this.connected) return;
|
this.sendLine(cmd);
|
||||||
this.send("G90");
|
|
||||||
this.send(cmd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setZero() {
|
setZero() {
|
||||||
if(!this.connected) return;
|
this.sendLine("G92 X0 Y0 Z0");
|
||||||
this.send("G10 L20 P1 X0 Z0");
|
}
|
||||||
|
|
||||||
|
onMessage(fn) {
|
||||||
|
this.on("message", fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,88 +9,72 @@ const FluidNCClient = require("./fluidnc/FluidNCClient");
|
|||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
// Serve frontend
|
||||||
app.use(express.static(path.join(__dirname, "../web")));
|
app.use(express.static(path.join(__dirname, "../web")));
|
||||||
|
|
||||||
const server = https.createServer({
|
// HTTPS server
|
||||||
|
const server = https.createServer(
|
||||||
|
{
|
||||||
key: fs.readFileSync(path.join(__dirname, "../cert/key.pem")),
|
key: fs.readFileSync(path.join(__dirname, "../cert/key.pem")),
|
||||||
cert: fs.readFileSync(path.join(__dirname, "../cert/cert.pem"))
|
cert: fs.readFileSync(path.join(__dirname, "../cert/cert.pem"))
|
||||||
},app);
|
},
|
||||||
|
app);
|
||||||
|
|
||||||
|
// Websocket server (browser connections)
|
||||||
const wss = new WebSocket.Server({ server });
|
const wss = new WebSocket.Server({ server });
|
||||||
|
|
||||||
const fluid = new FluidNCClient();
|
// Create FluidNC WebSocket client
|
||||||
|
const fluid = new FluidNCClient(config.fluidnc);
|
||||||
|
|
||||||
|
// Connected browser clients
|
||||||
let clients = [];
|
let clients = [];
|
||||||
|
|
||||||
wss.on("connection", (ws) => {
|
wss.on("connection", (ws) => {
|
||||||
|
console.log("[WS] Browser connected");
|
||||||
console.log("[WS] Client connected");
|
|
||||||
|
|
||||||
clients.push(ws);
|
clients.push(ws);
|
||||||
|
|
||||||
ws.on("message", (msg) => {
|
ws.on("message", (msg) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const data = JSON.parse(msg);
|
const data = JSON.parse(msg);
|
||||||
|
|
||||||
console.log("[WS] Received message:", data);
|
|
||||||
|
|
||||||
if (data.type === "jog") {
|
if (data.type === "jog") {
|
||||||
|
|
||||||
fluid.jog(data.axis, data.value);
|
fluid.jog(data.axis, data.value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.type === "gcode") {
|
if (data.type === "gcode") {
|
||||||
|
|
||||||
fluid.sendGcode(data.cmd);
|
fluid.sendGcode(data.cmd);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.type === "zero") {
|
if (data.type === "zero") {
|
||||||
|
|
||||||
fluid.setZero();
|
fluid.setZero();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.log("[WS] Error parsing:", e);
|
||||||
console.log("[WS] Error parsing message:", e);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on("close", () => {
|
ws.on("close", () => {
|
||||||
|
|
||||||
clients = clients.filter(c => c !== ws);
|
clients = clients.filter(c => c !== ws);
|
||||||
|
console.log("[WS] Browser disconnected");
|
||||||
console.log("[WS] Client disconnected");
|
});
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// FluidNC → Browser broadcasting
|
||||||
fluid.onMessage((msg) => {
|
fluid.onMessage((msg) => {
|
||||||
|
|
||||||
const json = JSON.stringify(msg);
|
|
||||||
|
|
||||||
clients.forEach(c => {
|
clients.forEach(c => {
|
||||||
|
|
||||||
if (c.readyState === WebSocket.OPEN) {
|
if (c.readyState === WebSocket.OPEN) {
|
||||||
|
c.send(msg.toString());
|
||||||
c.send(json);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
// Status polling ("?" every 200ms)
|
||||||
|
setInterval(() => {
|
||||||
|
fluid.requestStatus();
|
||||||
|
}, 200);
|
||||||
|
|
||||||
server.listen(config.server.port, () => {
|
server.listen(config.server.port, () => {
|
||||||
|
|
||||||
console.log("[Server] Running at:");
|
console.log("[Server] Running at:");
|
||||||
console.log(`https://localhost:${config.server.port}`);
|
console.log(`https://localhost:${config.server.port}`);
|
||||||
|
|
||||||
});
|
});
|
||||||
186
web/index.html
186
web/index.html
@@ -1,131 +1,141 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
<meta charset="UTF-8">
|
<title>SCARA Robot Control</title>
|
||||||
<title>FluidNC Control</title>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family:Arial;
|
background: #202020;
|
||||||
margin:40px;
|
color: #e0e0e0;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#posBox {
|
||||||
|
margin: 20px auto;
|
||||||
|
width: 300px;
|
||||||
|
padding: 15px;
|
||||||
|
background: #333;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btnGrid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 120px);
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
width:60px;
|
background: #444;
|
||||||
height:40px;
|
border: none;
|
||||||
margin:3px;
|
padding: 15px;
|
||||||
|
font-size: 18px;
|
||||||
|
color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.axis{
|
button:hover {
|
||||||
margin-bottom:30px;
|
background: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status{
|
|
||||||
font-size:20px;
|
|
||||||
margin-bottom:20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h2 id="connection">Trying to connect to FluidNC...</h2>
|
<h1>SCARA Robot Control</h1>
|
||||||
|
|
||||||
<div class="axis">
|
|
||||||
|
|
||||||
<h3>X Axis</h3>
|
|
||||||
|
|
||||||
<button onclick="jog('x',-10)">-10</button>
|
|
||||||
<button onclick="jog('x',-1)">-1</button>
|
|
||||||
|
|
||||||
<span id="x">0</span>
|
|
||||||
|
|
||||||
<button onclick="jog('x',1)">+1</button>
|
|
||||||
<button onclick="jog('x',10)">+10</button>
|
|
||||||
|
|
||||||
|
<div id="posBox">
|
||||||
|
<div>X: <span id="posX">0.000</span></div>
|
||||||
|
<div>Z: <span id="posZ">0.000</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="axis">
|
<h2>Jog Controls</h2>
|
||||||
|
|
||||||
<h3>Z Axis</h3>
|
<div class="btnGrid">
|
||||||
|
<button onclick="jog('X', -10)">X -10</button>
|
||||||
<button onclick="jog('z',-10)">-10</button>
|
<button></button>
|
||||||
<button onclick="jog('z',-1)">-1</button>
|
<button onclick="jog('X', 10)">X +10</button>
|
||||||
|
|
||||||
<span id="z">0</span>
|
|
||||||
|
|
||||||
<button onclick="jog('z',1)">+1</button>
|
|
||||||
<button onclick="jog('z',10)">+10</button>
|
|
||||||
|
|
||||||
|
<button onclick="jog('Z', 10)">Z +10</button>
|
||||||
|
<button></button>
|
||||||
|
<button onclick="jog('Z', -10)">Z -10</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3>GCode</h3>
|
|
||||||
|
|
||||||
<input id="gcode" style="width:300px"/>
|
|
||||||
<button onclick="sendGcode()">Send</button>
|
|
||||||
|
|
||||||
<br><br>
|
|
||||||
|
|
||||||
<button onclick="setZero()">Set Current Position to Zero</button>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
let ws;
|
||||||
|
|
||||||
const ws = new WebSocket("wss://"+location.host);
|
function connectWS() {
|
||||||
|
ws = new WebSocket("wss://" + window.location.hostname + ":3000");
|
||||||
ws.onmessage = (e)=>{
|
|
||||||
|
|
||||||
const data = JSON.parse(e.data);
|
|
||||||
|
|
||||||
if(data.type==="connection"){
|
|
||||||
|
|
||||||
if(data.connected){
|
|
||||||
document.getElementById("connection").innerText="FluidNC connected";
|
|
||||||
}else{
|
|
||||||
document.getElementById("connection").innerText="Trying to connect to FluidNC...";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(data.type==="status"){
|
|
||||||
|
|
||||||
document.getElementById("x").innerText=data.x.toFixed(2);
|
|
||||||
document.getElementById("z").innerText=data.z.toFixed(2);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
ws.onopen = () => {
|
||||||
|
console.log("WS connected");
|
||||||
};
|
};
|
||||||
|
|
||||||
function jog(axis,val){
|
ws.onclose = () => {
|
||||||
|
console.warn("WS disconnected → reconnecting...");
|
||||||
|
setTimeout(connectWS, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onmessage = (ev) => {
|
||||||
|
const text = ev.data;
|
||||||
|
|
||||||
|
// GRBL/FluidNC status line?
|
||||||
|
if (text.startsWith("<")) {
|
||||||
|
handleStatus(text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try JSON only if valid JSON
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(text);
|
||||||
|
console.log("JSON:", data);
|
||||||
|
} catch (e) {
|
||||||
|
// non JSON → ignore
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onerror = (e) => console.error("WS error:", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
connectWS();
|
||||||
|
|
||||||
|
// --- Jog-Funktion ---
|
||||||
|
function jog(axis, value) {
|
||||||
ws.send(JSON.stringify({
|
ws.send(JSON.stringify({
|
||||||
type: "jog",
|
type: "jog",
|
||||||
axis: axis,
|
axis: axis,
|
||||||
value:val
|
value: value
|
||||||
}));
|
}));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendGcode(){
|
// --- GRBL Status Parser ---
|
||||||
|
function handleStatus(line) {
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type:"gcode",
|
|
||||||
cmd:document.getElementById("gcode").value
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
let match = line.match(/WPos:([^|>]+)/);
|
||||||
|
if (!match) {
|
||||||
|
match = line.match(/MPos:([^|>]+)/);
|
||||||
|
if (!match) return; // Keiner von beiden vorhanden
|
||||||
}
|
}
|
||||||
|
|
||||||
function setZero(){
|
|
||||||
|
|
||||||
ws.send(JSON.stringify({
|
const coords = match[1].split(",").map(Number);
|
||||||
type:"zero"
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
// Nur X (Index 0) und Z (Index 2) anzeigen
|
||||||
|
const x = coords[0];
|
||||||
|
const z = coords[2];
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById("posX").textContent = x.toFixed(3);
|
||||||
|
document.getElementById("posZ").textContent = z.toFixed(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user