grid-apps-cmms/src/kiri-run/cli.js
2022-03-05 18:41:05 -05:00

222 lines
6.7 KiB
JavaScript

/** example of how to use Kiri:Moto's slicer engine from the command-line */
let fs = require('fs');
let args = process.argv.slice(2);
let opts = {
dir: process.cwd(),
output: "-",
model: "web/obj/cube.stl",
source: "src/cli/kiri-source.json",
controller: "src/cli/kiri-controller.json",
process: "src/cli/kiri-fdm-process.json",
device: "src/cli/kiri-fdm-device.json",
};
for (let i=0; i<args.length; i++) {
let arg = args[i].split('=');
let [ key, val ] = arg;
if (arg.length === 2) {
key = arg[0];
val = arg[1];
} else if (key.indexOf('--') === 0) {
val = true;
} else if (key.indexOf('-') === 0) {
val = args[++i];
} else {
val = undefined;
}
if (key && val) {
key = key.replace(/-/g,'');
opts[key.trim()] = val;
} else {
key = "model";
val = arg;
}
}
if (opts.help) {
console.log([
"cli <options> <file>",
" --verbose | enable verbose logging",
" --dir=[dir] | root directory for file paths (default: '.')",
" --model=[file] | model file to load (or last parameter)",
" --source=[file] | source file list (defaults to kiri engine)",
" --device=[file] | device definition file (json)",
" --process=[file] | process definition file (json)",
" --controller=[file] | controller definition file (json)",
" --output=[file] | gcode output to file or '-' for stdout",
" --position=x,y,z | move loaded model to position x,y,z",
" --rotate=x,y,z | rotate loaded model x,y,z radians",
" --scale=x,y,z | scale loaded model in x,y,z",
" --move=x,y,z | move loaded model x,y,z millimeters"
].join("\r\n"));
return;
}
let { dir, verbose, model, output, position, move, scale, rotate } = opts;
let exports_save = exports,
navigator = { userAgent: "" },
module_save = module,
THREE = {},
gapp = {},
geo = {},
noop = () => { },
self = this.self = {
gapp,
THREE,
location: { hostname: 'local', port: 0, protocol: 'fake' },
postMessage: (msg) => {
self.kiri.client.onmessage({data:msg});
}
};
// fake fetch for worker to get wasm, if needed
let fetch = function(url, opts = {}) {
if (verbose) console.log({fetch: url});
if (!url.startsWith('/')) {
url = `${dir}/${url}`;
}
let buf = fs.readFileSync(url);
return new Promise((resolve, reject) => {
resolve(new Promise((resolve, reject) => {
if (opts.format === 'string') {
return resolve(buf.toString());
}
if (opts.format === 'buffer') {
return resolve(buf);
}
if (opts.format === 'eval') {
return resolve(eval('(' + buf + ')'));
}
resolve({
arrayBuffer: function() {
return buf;
}
});
}));
});
};
// imitate worker process
class Worker {
constructor(url) {
if (verbose) console.log({worker: url});
}
postMessage(msg) {
setImmediate(() => {
self.kiri.worker.onmessage({data:msg});
});
}
onmessage(msg) {
// if we end up here, something went wrong
console.trace('worker-recv', msg);
}
terminate() {
// if we end up here, something went wrong
console.trace('worker terminate');
}
}
// node is missing these functions so put them in scope during eval
function atob(a) {
return Buffer.from(a).toString('base64');
}
function btoa(b) {
return Buffer.from(b, 'base64').toString();
}
async function run() {
let files = await fetch(opts.source, { format: "eval" } );
let device = await fetch(opts.device, { format: "eval" } );
let process = await fetch(opts.process, { format: "eval" } );
for (let file of files.map(p => `${dir}/src/${p}.js`)) {
let isPNG = file.indexOf("/pngjs") > 0;
let isClip = file.indexOf("/clip") > 0;
let isEarcut = file.indexOf("/earcut") > 0;
let isTHREE = file.indexOf("/three") > 0;
if (isTHREE) {
// THREE.js kung-fu fake-out
exports = {};
}
let swapMod = isEarcut;
if (swapMod) {
module = { exports: {} };
}
let clearMod = isPNG || isClip;
if (clearMod) {
module = undefined;
}
try {
if (verbose) console.log(`loading ... ${file}`);
eval(fs.readFileSync(`${file}`).toString());
} catch (e) {
throw e;
}
if (isClip) {
ClipperLib = self.ClipperLib;
}
if (isTHREE) {
Object.assign(THREE, exports);
// restore exports after faking out THREE.js
exports = exports_save;
}
if (isEarcut) {
self.earcut = module.exports;
}
if (clearMod || swapMod) {
module = module_save;
}
}
let { kiri, moto, load } = self;
console.log({version: kiri.version});
let engine = kiri.newEngine();
let data = await fetch(model)
let buf = new Uint8Array(data.arrayBuffer()).buffer;
return engine.parse(buf)
.then(data => { if (verbose) console.log({loaded: data}) })
.then(() => {
if (position) {
let [ x, y, z ] = position.split(',').map(v => parseFloat(v || 0));
if (verbose) console.log('moveTo', {x, y, z});
engine.moveTo(x,y,z);
}
if (move) {
let [ x, y, z ] = move.split(',').map(v => parseFloat(v || 0));
if (verbose) console.log('move', {x, y, z});
engine.move(x,y,z);
}
if (scale) {
let [ x, y, z ] = scale.split(',').map(v => parseFloat(v || 0));
if (verbose) console.log('scale', {x, y, z});
engine.scale(x,y,z);
}
if (rotate) {
let [ x, y, z ] = rotate.split(',').map(v => parseFloat(v || 0));
if (verbose) console.log('rotate', {x, y, z});
engine.rotate(x,y,z);
}
})
.then(() => engine.setProcess(process))
.then(() => engine.setDevice(device))
.then(eng => eng.slice())
.then(eng => eng.prepare())
.then(eng => engine.export())
.then(gcode => {
if (output === '-') {
console.log({gcode});
} else {
fs.writeFileSync(output, gcode);
}
})
.catch(error => {
console.log({error});
});
}
run();