add more state, file, print management
This commit is contained in:
parent
2e1114510a
commit
b4b31cd6b8
4 changed files with 192 additions and 85 deletions
|
|
@ -15,7 +15,7 @@ self.kiri.load(api => {
|
|||
let init = false;
|
||||
let status = {};
|
||||
let bound, device, printers, select, selected;
|
||||
let btn_del, in_host, in_code, in_serial;
|
||||
let btn_del, in_host, in_code, in_serial, filelist;
|
||||
let host, password, serial, amsmap, socket = {
|
||||
open: false,
|
||||
q: [],
|
||||
|
|
@ -34,17 +34,22 @@ self.kiri.load(api => {
|
|||
};
|
||||
ws.onmessage = msg => {
|
||||
let data = JSON.parse(msg.data);
|
||||
let { serial, message, error } = data;
|
||||
let { serial, message, files, deleted, error } = data;
|
||||
if (error) {
|
||||
console.log({ serial, error });
|
||||
api.alerts.show(`Bambu Error: ${error}`, 3);
|
||||
// printer_status(`error: ${error}`);
|
||||
} else if (deleted) {
|
||||
console.log('file deleted', deleted);
|
||||
file_list();
|
||||
} else if (serial) {
|
||||
let rec = status[serial] = deepMerge(status[serial] || {}, message);
|
||||
if (files) {
|
||||
rec.files = files;
|
||||
}
|
||||
if (selected?.rec.serial === serial) {
|
||||
selected.status = rec;
|
||||
printer_render(rec);
|
||||
} else {
|
||||
console.log('update', serial, rec);
|
||||
}
|
||||
} else {
|
||||
console.log('ignored', serial, data);
|
||||
|
|
@ -142,11 +147,11 @@ self.kiri.load(api => {
|
|||
in_serial.onkeypress = in_serial.onblur = printer_update;
|
||||
monitor_start(rec);
|
||||
printer_render();
|
||||
file_list();
|
||||
$('bbl_name').innerText = name;
|
||||
}
|
||||
|
||||
function printer_render(rec = {}) {
|
||||
$('bbl_rec').value = JSON.stringify(deepSortObject(rec), undefined, 2);
|
||||
let { info, print, files } = rec;
|
||||
let {
|
||||
ams_status,
|
||||
|
|
@ -157,6 +162,7 @@ self.kiri.load(api => {
|
|||
chamber_temper,
|
||||
cooling_fan_speed,
|
||||
gcode_file,
|
||||
gcode_state, // PAUSE, RUNNING, FAILED
|
||||
heatbreak_fan_speed,
|
||||
layer_num,
|
||||
mc_percent,
|
||||
|
|
@ -170,41 +176,45 @@ self.kiri.load(api => {
|
|||
total_layer_num,
|
||||
upload
|
||||
} = print || {};
|
||||
let state = (gcode_state || 'unknown').toLowerCase();
|
||||
$('bbl_noz').value = nozzle_diameter || '';
|
||||
$('bbl_noz_temp').value = nozzle_temper?.toFixed(1) ?? '';
|
||||
$('bbl_noz_target').value = nozzle_target_temper?.toFixed(1) ?? '';
|
||||
$('bbl_bed_temp').value = bed_temper?.toFixed(1) ?? '';
|
||||
$('bbl_bed_target').value = bed_target_temper?.toFixed(1) ?? '';
|
||||
if (files) {
|
||||
h.bind($('bbl_files'), files.map(file => {
|
||||
$('bbl_pause').disabled = (gcode_state !== 'RUNNING');
|
||||
$('bbl_resume').disabled = (gcode_state !== 'PAUSE');
|
||||
$('bbl_resume').disabled = (gcode_state !== 'FAILED');
|
||||
if (files && filelist.selectedIndex === -1) {
|
||||
h.bind(filelist, files.map(file => {
|
||||
let name = file.name
|
||||
.toLowerCase()
|
||||
.replace('.gcode','')
|
||||
.replace('.3mf','');
|
||||
return h.option({
|
||||
_: name,
|
||||
style: "max-width: 20em",
|
||||
value:`${file.path}${file.name}`
|
||||
style: "max-width: 20em"
|
||||
});
|
||||
}));
|
||||
rec.files = undefined;
|
||||
filelist.selectedIndex = 0;
|
||||
filelist.onchange();
|
||||
}
|
||||
// provide only the print info from the serial recorld
|
||||
$('bbl_rec').value = JSON.stringify(deepSortObject({ ...rec.print }), undefined, 2);
|
||||
if (print_error) {
|
||||
bbl_status.value = `print error ${print_error}`
|
||||
} else if (mc_remaining_time) {
|
||||
bbl_status.value = `printing layer ${layer_num} of ${total_layer_num} | ${mc_percent}% complete | ${mc_remaining_time} minutes left`
|
||||
} else if (mc_remaining_time && gcode_state !== 'FAILED') {
|
||||
bbl_status.value = `layer ${layer_num} of ${total_layer_num} | ${mc_percent}% complete | ${mc_remaining_time} minutes left | ${state}`
|
||||
} else {
|
||||
bbl_status.value = `printer idle`;
|
||||
bbl_status.value = `printer ${print_type || ""} | ${state}`;
|
||||
}
|
||||
}
|
||||
|
||||
function printer_status(msg) {
|
||||
$('bbl_status').value = msg;
|
||||
}
|
||||
|
||||
function render_list(to) {
|
||||
let list = Object.keys(printers).map(name => {
|
||||
return h.option({ _: name, value: name })
|
||||
return selected?.name === name ?
|
||||
h.option({ _: name, value: name, selected: true }) :
|
||||
h.option({ _: name, value: name });
|
||||
});
|
||||
list = [
|
||||
h.option({ _: '', value: '' }),
|
||||
|
|
@ -244,12 +254,31 @@ self.kiri.load(api => {
|
|||
}
|
||||
}
|
||||
|
||||
function list_files() {
|
||||
function file_list() {
|
||||
if (selected?.rec?.host) {
|
||||
filelist.selectedIndex = -1;
|
||||
$('bbl_file_size').value =
|
||||
$('bbl_file_date').value = '';
|
||||
$('bbl_file_delete').disabled =
|
||||
$('bbl_file_print').disabled = true;
|
||||
socket.send({ cmd: "files", ...selected.rec });
|
||||
}
|
||||
}
|
||||
|
||||
function file_delete(path) {
|
||||
if (selected?.rec?.host && path) {
|
||||
let { host, code } = selected.rec
|
||||
socket.send({ cmd: "file-delete", path, host, code });
|
||||
}
|
||||
}
|
||||
|
||||
function file_print(path) {
|
||||
if (selected?.rec?.host && path) {
|
||||
let { host, code, serial } = selected.rec;
|
||||
socket.send({ cmd: "file-print", path, host, code, serial, amsmap });
|
||||
}
|
||||
}
|
||||
|
||||
api.event.on("init-done", function() {
|
||||
if (init) {
|
||||
return;
|
||||
|
|
@ -366,28 +395,57 @@ self.kiri.load(api => {
|
|||
rows: 15, cols: 65
|
||||
})
|
||||
]),
|
||||
h.div({ class: "t-body t-inset f-col gap3 pad4" }, [
|
||||
h.div({ class: "set-header", onclick() {
|
||||
// console.log('bbl reload files');
|
||||
list_files();
|
||||
} }, h.a({ class: "flex f-row grow" }, [
|
||||
h.label('files'),
|
||||
h.span({ class: "fat5 grow" }),
|
||||
h.i({ class: "fa-solid fa-rotate" })
|
||||
])),
|
||||
h.select({ id: "bbl_files", style: "height: 100%", size: 5 }, []),
|
||||
h.div({ class: "f-col gap3" }, [
|
||||
h.div({ class: "t-body t-inset f-col gap3 pad4 grow" }, [
|
||||
h.div({ class: "set-header", onclick() {
|
||||
file_list();
|
||||
} }, h.a({ class: "flex f-row grow" }, [
|
||||
h.label('file'),
|
||||
h.span({ class: "fat5 grow" }),
|
||||
h.i({ class: "fa-solid fa-rotate" })
|
||||
])),
|
||||
h.select({ id: "bbl_files" }, []),
|
||||
h.div({ class: "var-row" }, [
|
||||
h.label('size'),
|
||||
h.input({ id: "bbl_file_size", size: 12 })
|
||||
]),
|
||||
h.div({ class: "var-row" }, [
|
||||
h.label('date'),
|
||||
h.input({ id: "bbl_file_date", size: 12 })
|
||||
]),
|
||||
h.div({ class: "grow" }),
|
||||
h.button({
|
||||
_: 'delete',
|
||||
id: "bbl_file_delete",
|
||||
class: "f-col a-center t-center",
|
||||
disabled: true,
|
||||
onclick() {
|
||||
console.log({ deleting: selected.file.path });
|
||||
file_delete(selected.file.path);
|
||||
}}),
|
||||
h.button({
|
||||
_: 'print',
|
||||
id: "bbl_file_print",
|
||||
class: "f-col a-center t-center",
|
||||
disabled: true,
|
||||
onclick() {
|
||||
console.log({ printing: selected.file.path });
|
||||
file_print(selected.file.path);
|
||||
}}),
|
||||
])
|
||||
])
|
||||
]),
|
||||
h.div({ class: "set-sep "}),
|
||||
h.div({ class: "gap4" }, [
|
||||
h.label({ class: "set-header dev-sel" }, [ h.a('status') ]),
|
||||
h.input({ id: "bbl_status", class: "t-left mono grow" }),
|
||||
h.button({ _: "pause", class: "a-center", onclick() { cmd_if("pause") } }),
|
||||
h.button({ _: "resume", class: "a-center", onclick() { cmd_if("resume") } }),
|
||||
h.button({ _: "pause", id: "bbl_pause", class: "a-center", onclick() { cmd_if("pause") } }),
|
||||
h.button({ _: "resume", id: "bbl_resume", class: "a-center", onclick() { cmd_if("resume") } }),
|
||||
h.button({ _: "cancel", class: "a-center", onclick() { cmd_if("cancel") } }),
|
||||
])
|
||||
]), { before: true });
|
||||
select = modal.bbl_sel;
|
||||
filelist = modal.bbl_files;
|
||||
btn_del = modal.bbl_pdel;
|
||||
in_host = modal.bbl_host;
|
||||
in_code = modal.bbl_code;
|
||||
|
|
@ -395,6 +453,13 @@ self.kiri.load(api => {
|
|||
api.ui.modals['bambu'] = modal['mod-bambu'];
|
||||
btn_del.disabled = true;
|
||||
select.onchange = (ev => printer_select(select.value));
|
||||
filelist.onchange = (ev => {
|
||||
let file = selected.file = selected.status.files[filelist.selectedIndex];
|
||||
$('bbl_file_size').value = file.size;
|
||||
$('bbl_file_date').value = file.date;
|
||||
$('bbl_file_delete').disabled =
|
||||
$('bbl_file_print').disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
api.event.on("modal.show", which => {
|
||||
|
|
@ -402,6 +467,7 @@ self.kiri.load(api => {
|
|||
return;
|
||||
}
|
||||
render_list();
|
||||
get_ams_map(api.conf.get());
|
||||
});
|
||||
|
||||
api.event.on("modal.hide", which => {
|
||||
|
|
@ -427,6 +493,17 @@ self.kiri.load(api => {
|
|||
}
|
||||
});
|
||||
|
||||
function get_ams_map(settings) {
|
||||
const ams = settings.device?.gcodePre.filter(line => line.indexOf(defams) === 0)[0];
|
||||
if (ams) {
|
||||
try {
|
||||
amsmap = ams.substring(defams.length).trim().replaceAll(' ','');
|
||||
} catch (e) {
|
||||
console.log({ invalid_ams_map: ams });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function prep_export(gen3mf, gcode, info, settings) {
|
||||
if (!settings.device.extras?.bbl) {
|
||||
$('bambu-output').style.display = 'none';
|
||||
|
|
@ -436,8 +513,12 @@ self.kiri.load(api => {
|
|||
let devlist = $('print-bambu-device');
|
||||
render_list(devlist);
|
||||
$('bambu-output').style.display = 'flex';
|
||||
$('print-bambu').onclick = function() {
|
||||
gen3mf(zip => send(`${$('print-filename').value}.3mf`, zip));
|
||||
$('print-bambu-1').onclick = function() {
|
||||
gen3mf(zip => send(`${$('print-filename').value}.3mf`, zip, false));
|
||||
}
|
||||
$('print-bambu-2').onclick = function() {
|
||||
gen3mf(zip => send(`${$('print-filename').value}.3mf`, zip, true));
|
||||
api.modal.show('bambu');
|
||||
}
|
||||
devlist.onchange = () => {
|
||||
let info = printers[devlist.value];
|
||||
|
|
@ -445,27 +526,23 @@ self.kiri.load(api => {
|
|||
host = info.host;
|
||||
serial = info.serial;
|
||||
password = info.code;
|
||||
$('print-bambu').disabled = (host && serial && password) ? false : true;
|
||||
const ams = settings.device?.gcodePre.filter(line => line.indexOf(defams) === 0)[0];
|
||||
if (ams) {
|
||||
try {
|
||||
amsmap = ams.substring(defams.length).trim().replaceAll(' ','');
|
||||
} catch (e) {
|
||||
console.log({ invalid_ams_map: ams });
|
||||
}
|
||||
}
|
||||
$('print-bambu-1').disabled =
|
||||
$('print-bambu-2').disabled =
|
||||
(host && serial && password) ? false : true;
|
||||
get_ams_map(settings);
|
||||
console.log({ bambu: host, serial, amsmap });
|
||||
};
|
||||
}
|
||||
|
||||
function send(filename, gcode) {
|
||||
function send(filename, gcode, start) {
|
||||
const baseUrl = '/api/bambu_send';
|
||||
const url = new URL(baseUrl, window.location.origin);
|
||||
url.searchParams.append('host', host);
|
||||
url.searchParams.append('password', password);
|
||||
url.searchParams.append('code', password);
|
||||
url.searchParams.append('filename', filename);
|
||||
url.searchParams.append('serial', serial);
|
||||
url.searchParams.append('ams', amsmap);
|
||||
url.searchParams.append('start', start ?? false);
|
||||
|
||||
const alert = api.alerts.show('Sending to Bambu Printer');
|
||||
|
||||
|
|
@ -494,5 +571,5 @@ self.kiri.load(api => {
|
|||
|
||||
setInterval(monitor_keepalive, 5000);
|
||||
|
||||
api.bambu = { send, amsmap, prep_export };
|
||||
api.bambu = { send, prep_export };
|
||||
});
|
||||
|
|
|
|||
|
|
@ -82,6 +82,10 @@ module.exports = async (server) => {
|
|||
}
|
||||
}
|
||||
|
||||
function if_mqtt(serial, msg) {
|
||||
mcache[serial]?.send(msg);
|
||||
}
|
||||
|
||||
function get_mqtt(host, code, serial, onmsg, onconn) {
|
||||
const fns = {};
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
|
|
@ -147,18 +151,58 @@ module.exports = async (server) => {
|
|||
const list = [];
|
||||
try {
|
||||
let files = await client.list();
|
||||
files.forEach(file => file.path = "/");
|
||||
files.forEach(file => file.root = "");
|
||||
list.push(...files);
|
||||
} catch (e) { }
|
||||
try {
|
||||
let files = await client.list("/cache");
|
||||
files.forEach(file => file.path = "/cache/");
|
||||
files.forEach(file => file.root = "cache/");
|
||||
list.push(...files);
|
||||
} catch (e) { }
|
||||
client.close();
|
||||
return list;
|
||||
}
|
||||
|
||||
async function ftp_delete(args = {}) {
|
||||
const client = await ftp_open(args);
|
||||
try {
|
||||
await client.remove(args.path);
|
||||
} catch (error) {
|
||||
util.log({ ftp_delete_error: error });
|
||||
}
|
||||
client.close();
|
||||
}
|
||||
|
||||
function file_print(opts = {}) {
|
||||
const { host, code, serial, filename, amsmap } = opts;
|
||||
console.log({ file_print: opts });
|
||||
const cmd = {
|
||||
print: {
|
||||
command: "project_file",
|
||||
url: `file:///sdcard/${filename}`,
|
||||
param: "Metadata/plate_1.gcode",
|
||||
subtask_id: "0",
|
||||
use_ams: amsmap ? true : false,
|
||||
timelapse: false,
|
||||
flow_cali: false,
|
||||
bed_leveling: false,
|
||||
layer_inspect: false,
|
||||
vibration_cali: false
|
||||
}
|
||||
};
|
||||
if (amsmap) {
|
||||
cmd.print.ams_mapping = amsmap;
|
||||
}
|
||||
get_mqtt(host, code, serial, message => {
|
||||
util.log('mqtt_recv', message);
|
||||
wsend({ serial, message });
|
||||
})
|
||||
.then(mqtt => mqtt.send(cmd))
|
||||
.catch(err => {
|
||||
util.log({ mqtt_err: err });
|
||||
});
|
||||
}
|
||||
|
||||
function decode_post(req, res, next) {
|
||||
if (req.method === 'POST') {
|
||||
let chunks = [];
|
||||
|
|
@ -198,37 +242,12 @@ module.exports = async (server) => {
|
|||
res.setHeader("Content-Type", "application/octet-stream");
|
||||
res.setHeader('Cache-Control', 'no-cache, no-store, private');
|
||||
const data = req.app.post;
|
||||
const { host, password, filename, serial, ams } = query;
|
||||
const ams_mapping = ams ? ams.split(',').map(v => parseInt(v)) : undefined;
|
||||
const mqtt_conn = serial ? get_mqtt(host, password, serial, message => {
|
||||
util.log('mqtt_recv', message);
|
||||
// util.log('mqtt_recv', JSON.parse(message.toString()));
|
||||
}) : undefined;
|
||||
ftp_send({ host, password, filename, data })
|
||||
const { host, code, filename, serial, ams, start } = query;
|
||||
const amsmap = ams ? ams.split(',').map(v => parseInt(v)) : undefined;
|
||||
ftp_send({ host, code, filename, data })
|
||||
.then(() => {
|
||||
if (serial) {
|
||||
const cmd = {
|
||||
print: {
|
||||
command: "project_file",
|
||||
url: `file:///sdcard/${filename}`,
|
||||
param: "Metadata/plate_1.gcode",
|
||||
subtask_id: "0",
|
||||
use_ams: ams_mapping ? true : false,
|
||||
timelapse: false,
|
||||
flow_cali: false,
|
||||
bed_leveling: false,
|
||||
layer_inspect: false,
|
||||
vibration_cali: false
|
||||
}
|
||||
};
|
||||
if (ams_mapping) {
|
||||
cmd.print.ams_mapping = ams_mapping;
|
||||
}
|
||||
mqtt_conn
|
||||
.then(mqtt => mqtt.send(cmd))
|
||||
.catch(err => {
|
||||
util.log({ mqtt_err: err });
|
||||
});
|
||||
if (serial && start ==='true') {
|
||||
file_print({ host, code, serial, filename, amsmap });
|
||||
}
|
||||
res.end(o2s({ sent: true }));
|
||||
})
|
||||
|
|
@ -244,7 +263,7 @@ module.exports = async (server) => {
|
|||
util.log('ws open', req.url, wsopen.length);
|
||||
ws.on('message', msg => {
|
||||
msg = JSON.parse(msg);
|
||||
let { cmd, host, code, serial } = msg;
|
||||
let { cmd, host, code, serial, path, amsmap } = msg;
|
||||
switch (cmd) {
|
||||
case "monitor":
|
||||
get_mqtt(host, code, serial, message => {
|
||||
|
|
@ -280,26 +299,36 @@ module.exports = async (server) => {
|
|||
.filter(file => file.name.toLowerCase().endsWith(".3mf"))
|
||||
.map(file => {
|
||||
return {
|
||||
path: file.path,
|
||||
root: file.root,
|
||||
name: file.name,
|
||||
path: file.root + file.name,
|
||||
size: file.size,
|
||||
date: file.rawModifiedAt
|
||||
};
|
||||
});
|
||||
wsend({ serial, message: { files }});
|
||||
wsend({ serial, files });
|
||||
}).catch(error => {
|
||||
util.log({ ftp_error: error });
|
||||
wsend({ serial, error: error.message || error.toString() });
|
||||
});
|
||||
break;
|
||||
case "file-delete":
|
||||
ftp_delete({ host, code, path }).then(() => {
|
||||
util.log({ ftp_delete: path });
|
||||
wsend({ serial, deleted: path });
|
||||
});
|
||||
break;
|
||||
case "file-print":
|
||||
file_print({ host, code, serial, filename: path, amsmap });
|
||||
break;
|
||||
case "pause":
|
||||
wsend({ print: { command: "pause", sequence_id: "0" } });
|
||||
if_mqtt(serial, { print: { command: "pause", sequence_id: "0" } });
|
||||
break;
|
||||
case "resume":
|
||||
wsend({ print: { command: "resume", sequence_id: "0" } });
|
||||
if_mqtt(serial, { print: { command: "resume", sequence_id: "0" } });
|
||||
break;
|
||||
case "cancel":
|
||||
wsend({ print: { command: "stop", sequence_id: "0", param: "" } });
|
||||
if_mqtt(serial, { print: { command: "stop", sequence_id: "0", param: "" } });
|
||||
break;
|
||||
case "keepalive":
|
||||
// util.log({ keepalive: serial });
|
||||
|
|
|
|||
Loading…
Reference in a new issue