Fileservice
This commit is contained in:
93
robot/FCodeClient.js
Normal file
93
robot/FCodeClient.js
Normal file
@@ -0,0 +1,93 @@
|
||||
// robot/FCodeClient.js
|
||||
// Translates FCode messages (FPoint, FPlus, FList, …) into REST calls to appRobotFileservice.
|
||||
// The Driver is the only gateway — controllers never contact the fileservice directly.
|
||||
|
||||
function _baseUrl() {
|
||||
return process.env.FILESERVICE_URL || 'http://appRobot_Fileservice:2100';
|
||||
}
|
||||
|
||||
/** Returns true when the message is an FCode (F + uppercase letter + lowercase, at start). */
|
||||
function isFCode(message) {
|
||||
return /^F[A-Z][a-z]+/.test(String(message).trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch an FCode message to the appropriate fileservice endpoint.
|
||||
* @param {object} robot Current robot state (x,y,z,phi,theta,psi,e,feedrate).
|
||||
* @param {string} message Raw FCode string, e.g. "FPoint", "FPlus", "FLoad demo".
|
||||
* @returns {Promise<{type: string, data?: string, line?: string}>}
|
||||
* type 'step' → line (driver-native GCode, Radian) ready to execute
|
||||
* type 'list'|'show'|'point'|'ok' → data (JSON string) to broadcast
|
||||
*/
|
||||
async function handle(robot, message) {
|
||||
const msg = String(message).trim();
|
||||
|
||||
if (msg.startsWith('FList')) {
|
||||
const data = await _req('GET', '/api/programs');
|
||||
return { type: 'list', data: JSON.stringify(data) };
|
||||
}
|
||||
if (msg.startsWith('FShow')) {
|
||||
const id = msg.slice('FShow'.length).trim();
|
||||
const data = id
|
||||
? await _req('GET', `/api/programs/${encodeURIComponent(id)}`)
|
||||
: await _req('GET', '/api/active');
|
||||
return { type: 'show', data: JSON.stringify(data) };
|
||||
}
|
||||
if (msg.startsWith('FLoad')) {
|
||||
const id = msg.slice('FLoad'.length).trim();
|
||||
const data = await _req('PUT', '/api/active', { id });
|
||||
return { type: 'ok', data: JSON.stringify(data) };
|
||||
}
|
||||
if (msg.startsWith('FSave')) {
|
||||
const name = msg.slice('FSave'.length).trim() || 'unnamed';
|
||||
const data = await _req('POST', '/api/programs', { name, fromActive: true });
|
||||
return { type: 'ok', data: JSON.stringify(data) };
|
||||
}
|
||||
if (msg.startsWith('FClear')) {
|
||||
const data = await _req('POST', '/api/active/clear');
|
||||
return { type: 'ok', data: JSON.stringify(data) };
|
||||
}
|
||||
if (msg.startsWith('FPoint')) {
|
||||
// Driver attaches the current pose (Radian) — fileservice converts to degrees for storage.
|
||||
const pose = {
|
||||
x: robot.x, y: robot.y, z: robot.z,
|
||||
a: robot.phi, b: robot.theta, c: robot.psi, e: robot.e,
|
||||
};
|
||||
const feedrate = robot.feedrate || 1000;
|
||||
const data = await _req('POST', '/api/active/points', { pose, feedrate });
|
||||
return { type: 'point', data: JSON.stringify(data) };
|
||||
}
|
||||
if (msg.startsWith('FPlus')) { const d = await _req('POST', '/api/active/next'); return { type: 'step', line: d.line }; }
|
||||
if (msg.startsWith('FMinus')) { const d = await _req('POST', '/api/active/prev'); return { type: 'step', line: d.line }; }
|
||||
if (msg.startsWith('FFirst')) { const d = await _req('POST', '/api/active/first'); return { type: 'step', line: d.line }; }
|
||||
if (msg.startsWith('FLast')) { const d = await _req('POST', '/api/active/last'); return { type: 'step', line: d.line }; }
|
||||
if (msg.startsWith('FGoto')) {
|
||||
const index = parseInt(msg.slice('FGoto'.length).trim(), 10);
|
||||
const d = await _req('POST', '/api/active/goto', { index });
|
||||
return { type: 'step', line: d.line };
|
||||
}
|
||||
if (msg.startsWith('FPlay')) { const d = await _req('POST', '/api/active/play'); return { type: 'ok', data: JSON.stringify(d) }; }
|
||||
if (msg.startsWith('FStop')) { const d = await _req('POST', '/api/active/stop'); return { type: 'ok', data: JSON.stringify(d) }; }
|
||||
|
||||
throw new Error(`Unbekannter FCode: ${msg}`);
|
||||
}
|
||||
|
||||
async function _req(method, path, body) {
|
||||
const url = `${_baseUrl()}${path}`;
|
||||
const opts = { method };
|
||||
if (body !== undefined && body !== null) {
|
||||
opts.headers = { 'content-type': 'application/json' };
|
||||
opts.body = JSON.stringify(body);
|
||||
}
|
||||
const res = await fetch(url, opts);
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({}));
|
||||
const e = new Error(err.message || res.statusText);
|
||||
e.code = err.code;
|
||||
e.status = res.status;
|
||||
throw e;
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
module.exports = { isFCode, handle };
|
||||
@@ -1,14 +1,11 @@
|
||||
/***
|
||||
* Receives GCode, processes it and moves the Data to the Roboter-Class
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const RobotController = require('./RobotController');
|
||||
|
||||
|
||||
class GCode{
|
||||
|
||||
static fileName = "GCodeFiles/log.gcode";
|
||||
|
||||
static containsMCode(s){
|
||||
return s === 'M1' || s.startsWith('M1 ');
|
||||
}
|
||||
@@ -55,7 +52,7 @@ class GCode{
|
||||
', "a":'+ robot.a +
|
||||
', "b":'+ robot.b +
|
||||
', "c":'+ robot.c +
|
||||
', "e":'+ 0.0 +
|
||||
', "e":'+ (robot.e ?? 0) +
|
||||
'}}';
|
||||
return text;
|
||||
}
|
||||
@@ -94,95 +91,6 @@ class GCode{
|
||||
return RobotController.receive(robot, g);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////77
|
||||
// Commands for Files
|
||||
|
||||
static removeStringFromFile(fileName, stringToRemove) {
|
||||
try {
|
||||
const data = fs.readFileSync(fileName, 'utf8');
|
||||
const modifiedData = data.replace(new RegExp(stringToRemove, 'g'), '');
|
||||
fs.writeFileSync(fileName, modifiedData, 'utf8');
|
||||
} catch (err) {
|
||||
console.error('Error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
static ContainsFilesCommand(message){
|
||||
if(message.indexOf('FPoint') !== -1){return true;} // Writes new Point (at end of file)
|
||||
if(message.indexOf('FPlus') !== -1){return true;} // go to Next Position in Log File
|
||||
if(message.indexOf('FMinus') !== -1){return true;} // go to Previous Position
|
||||
if(message.indexOf('FFirst') !== -1){return true;} // set Cursour to First Position of Log File
|
||||
if(message.indexOf('FLast') !== -1){return true;} // set Cursour to Last Position of Log File
|
||||
|
||||
|
||||
if(message.indexOf('FShow') !== -1){return true;} // Shows/Sends GCode-File
|
||||
if(message.indexOf('FList') !== -1){return true;} // Lists GCode-Files
|
||||
if(message.indexOf('FLoad ') !== -1){return true;} // Loads File into Log
|
||||
if(message.indexOf('FSave ') !== -1){return true;} // Saves Log to GCode-File
|
||||
if(message.indexOf('FClear') !== -1){return true;} // Clears default Log File
|
||||
|
||||
if(message.indexOf('M20') !== -1){return true;} // M20 - List SD Card-Contents // https://marlinfw.org/docs/gcode/M020.html
|
||||
if(message.indexOf('M23') !== -1){return true;} // M23 - Select SD file
|
||||
if(message.indexOf('M28') !== -1){return true;} // M28 - Start SD write // M28 [B1] filename
|
||||
if(message.indexOf('M29') !== -1){return true;} // M29 - Stop SD write
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static receiveFC(robot, message){
|
||||
|
||||
if(message.indexOf('FShow') !== -1){
|
||||
try {
|
||||
const data = fs.readFileSync(this.fileName, 'utf8');
|
||||
const reply = "XYZ__FShow__XYZ" + data; // prepend header
|
||||
return reply.replaceAll("G91 ","").replaceAll("G90 ","").replace(/\s?\bt\d+\b\s?/g, '').trim();
|
||||
|
||||
} catch (err) {
|
||||
console.error('Error:', err);
|
||||
}
|
||||
}
|
||||
|
||||
if(message.indexOf('FPoint') !== -1){
|
||||
|
||||
const secondsSinceEpoch = 10*Math.floor(Date.now() / 100);
|
||||
|
||||
var strGCode = String(`G90 G1 x${robot.x} y${robot.y} z${robot.z} a${(robot.phi*180/Math.PI).toFixed(2)} b${(robot.theta*180/Math.PI).toFixed(2)} c${(robot.psi*180/Math.PI).toFixed(2)} e${(robot.e*180/Math.PI).toFixed(2)} t${secondsSinceEpoch} f1000` )
|
||||
this.removeStringFromFile(this.fileName, ';!')
|
||||
fs.appendFileSync(this.fileName, strGCode+ ';!'+ '\r\n', 'utf8');
|
||||
}
|
||||
|
||||
if(message.indexOf('FMinus') !== -1 || message.indexOf('FPlus') !== -1){
|
||||
let lines = fs.readFileSync(this.fileName, 'utf8').split('\r\n');
|
||||
|
||||
let lineChange = -1;
|
||||
|
||||
// Process lines
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i].indexOf(';!') !== -1) {
|
||||
if(message.indexOf('FMinus') !== -1 && i > 0){
|
||||
lines[i - 1] += ';!';
|
||||
lines[i] = lines[i].split(';!')[0];
|
||||
var gCodePi = this.toPiMultiple(lines[i-1]);
|
||||
this.receiveGCode(robot, gCodePi);
|
||||
}
|
||||
if(message.indexOf('FPlus') !== -1 && i < (lines.length-2) && lineChange == -1){
|
||||
lines[i + 1] += ';!';
|
||||
lines[i] = lines[i].split(';!')[0];
|
||||
lineChange = i+1;
|
||||
var gCodePi = this.toPiMultiple(lines[i+1]);
|
||||
this.receiveGCode(robot, gCodePi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write the updated content back
|
||||
fs.writeFileSync(this.fileName, lines.join('\r\n'), 'utf8');
|
||||
|
||||
}
|
||||
|
||||
return GCode.getM114(robot);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GCode
|
||||
|
||||
Reference in New Issue
Block a user