From 071997884efe826e519cecadd7a1818536dc7820 Mon Sep 17 00:00:00 2001 From: Stewart Allen Date: Fri, 14 Feb 2025 19:50:46 -0500 Subject: [PATCH] fix video state management. rework ams tray display and popup --- mods/bambu/bambu.js | 156 ++++++++++++++++++++++++++++++------ mods/bambu/frames.js | 6 +- mods/bambu/init.js | 8 +- src/kiri-mode/fdm/export.js | 2 +- web/kiri/index.css | 6 ++ 5 files changed, 149 insertions(+), 29 deletions(-) diff --git a/mods/bambu/bambu.js b/mods/bambu/bambu.js index 15938344..78b428f7 100644 --- a/mods/bambu/bambu.js +++ b/mods/bambu/bambu.js @@ -11,8 +11,10 @@ self.kiri.load(api => { let init = false; let status = {}; let monitors = []; + let showing = false; let video_on = false; - let bound, device, printers, select, selected, conn_alert, export_select; + let video_auto = false; + let bound, device, printers, select, selected, conn_alert, export_select, tray_info; let btn_del, in_host, in_code, in_serial, filelist, print_ams_select = 'auto'; let ptype, host, password, serial, amsmap, socket = { open: false, @@ -45,6 +47,8 @@ self.kiri.load(api => { }); return; } + // receiving a frame will show the video feed (for debugging) + set_video_visible(true); const img = new Image(); img.src = `data:image/jpeg;base64,${frame}`; img.onload = () => { @@ -103,6 +107,10 @@ self.kiri.load(api => { } }; + function range(length, start = 0) { + return Array.from({ length }, (_, i) => i + start); + } + function deepMerge(target, source) { // console.log({ target, source }); if (!source) { @@ -172,6 +180,10 @@ self.kiri.load(api => { function printer_video_set(bool) { video_on = bool; set_frames(selected?.rec?.serial, bool); + set_video_visible(bool); + } + + function set_video_visible(bool) { ui.setVisible($('bbl_video_frame'), bool); ui.setClass($('bbl_vid_toggle'), 'bred', bool); const canvas = $('bbl_video'); @@ -180,10 +192,11 @@ self.kiri.load(api => { } function printer_select(name = '') { + let isvid = video_on; + printer_video_set(false); for (let [ printer, rec ] of Object.entries(printers)) { rec.selected = printer === name; } - printer_video_set(false); btn_del.disabled = false; let rec = printers[name] || {}; selected = { name, rec }; @@ -193,6 +206,7 @@ self.kiri.load(api => { in_host.onkeypress = in_host.onblur = printer_update; in_code.onkeypress = in_code.onblur = printer_update; in_serial.onkeypress = in_serial.onblur = printer_update; + printer_video_set(isvid); monitor_start(rec); printer_render(); file_list(); @@ -261,25 +275,35 @@ self.kiri.load(api => { print_ams_select = ev.target.value; }; }); - let tdiv = $('bbl_ams_trays'); - tdiv.setAttribute("style",[ - "gap: 5px;", - "display:grid", - `grid-template-columns:repeat(4,1fr)`, - `grid-template-rows:repeat(${ams.ams.length},auto)` - ].join(';')) - h.bind(tdiv, trays.map(tray => h.button({ - _: tray.tray_type, - class:`a-center${tray.tray_color?'':' checker'}`, - style: [ - tray_now === tray.id ? `border-color: red` : undefined, - tray.tray_color ? undefined : `border-style: dashed`, - `background-color:#${tray.tray_color||'000'}`, - `color:#${calcFG(tray.tray_color||'000')}`, - `min-height:10px`, - `aspect-ratio:1` - ].filter(v => v).join(';') - }))) + range(16).map(id => { + let btn = $(`bbl_tray_${id}`); + ui.setVisible(btn, id < trays.length); + $('bbl_ams_trays').style.gridTemplateRows = `repeat(${trays.length / 4},auto)`; + if (id < trays.length) { + let { style } = btn; + let { tray_color, tray_type } = trays[id]; + if (tray_color) { + style.color = `#${calcFG(tray_color)}`; + style.backgroundColor = `#${tray_color}`; + btn.classList.remove('checker'); + } else { + style.color = ''; + style.backgroundColor = ''; + btn.classList.add('checker'); + } + if (btn.tray_type !== tray_type) { + // otherwise rewriting the text kills the popup + btn.tray_type = btn.innerText = tray_type || ''; + } + if (tray_now == id) { + style.borderColor = 'red'; + style.borderStyle = 'dashed'; + } else { + style.borderColor = ''; + style.borderStyle = ''; + } + } + }); } else { $('bbl_ams_tray').innerHTML = ''; $('bbl_file_spool').innerHTML = ''; @@ -746,8 +770,81 @@ self.kiri.load(api => { }); } }), ]), - h.div({ id: "bbl_ams_trays" }) + h.div({ + id: "bbl_ams_trays", + "style": [ + "gap: 5px;", + "display:grid", + `grid-template-columns:repeat(4,1fr)`, + `grid-template-rows:repeat(4,auto)` + ].join(';') + }, range(16).map(id => h.button({ + _: id, + id: `bbl_tray_${id}`, + class:`a-center hide`, + style: [ + `min-height:10px`, + `aspect-ratio:1`, + `position:relative` + ].join(';'), + onclick(ev) { + let { target } = ev; + if (target.nodeName === 'BUTTON') { + target.appendChild(tray_info); + ui.setVisible(tray_info, true); + } + }, + onmouseleave() { + ui.setVisible(tray_info, false); + } + }))) ]), + + h.div({ id: "bbl_tray_info", + class: "t-body f-col gap3 pad5 hide", + style: [ + "position:absolute", + "top:100%", + "right:0", + "transform:translateX(30%)", + "padding:10px !important", + "z-index:1000", + ].join(';') + }, [ + h.div({ class: "var-row" }, [ + // h.label('filament'), + h.select({ id: "bbl_tray_type", onchange() { + cmd_direct({ + print: { + command: 'ams_change_filament', + curr_temp: 220, + tar_temp: 220, + target: parseInt(new_tray) + } + }); + }}, api.bambu.filament.map(row => h.option({ + _: row[1], + value: row[0] + }))), + ]), + h.div({ class: "var-row" }, [ + // home_flag bit 11 (0x400) + h.label(''), + h.span('#'), + h.input({ value: "001122", size: 6 }), + h.select({ id: "bbl_tray_color", onchange() { + cmd_direct({ + print: { + auto_switch_filament: $('bbl_ams_spool').checked, + command: 'print_option', + sequence_id: '123', + option: $('bbl_ams_spool').checked ? 1 : 0 + } + }) + }}) + ]), + ]), + h.div({ class: "t-body t-inset f-col gap3 pad4 grow" }, [ h.div({ class: "set-header", onclick() { file_list(); @@ -850,6 +947,7 @@ self.kiri.load(api => { in_host = modal.bbl_host; in_code = modal.bbl_code; in_serial = modal.bbl_serial; + tray_info = modal.bbl_tray_info; api.ui.modals['bambu'] = modal['mod-bambu']; btn_del.disabled = true; select.onchange = (ev => printer_select(select.value)); @@ -877,19 +975,25 @@ self.kiri.load(api => { } } printer_render({ files: [] }); + printer_video_set(video_auto); printer_select(); socket.start(); render_list(); get_ams_map(api.conf.get()); + showing = true; }); - api.event.on("modal.hide", which => { + api.event.on("modal.hide", () => { if (selected?.rec.modified) { api.conf.save(); } - printer_video_set(false); - selected = undefined; - status = {}; + if (showing) { + video_auto = video_on; + printer_video_set(false); + selected = undefined; + showing = false; + status = {}; + } }); api.event.on("device.selected", devsel => { diff --git a/mods/bambu/frames.js b/mods/bambu/frames.js index 999dce0c..a3792559 100644 --- a/mods/bambu/frames.js +++ b/mods/bambu/frames.js @@ -3,9 +3,10 @@ * and stores the resulting jpeg stream into frame files */ +const { bblCA } = require('./certificates'); const EventEmitter = require('events'); const tls = require('tls'); -const { bblCA } = require('./certificates'); +const debug = false; class FrameStream extends EventEmitter { #remoteSocket; @@ -40,11 +41,13 @@ class FrameStream extends EventEmitter { remoteSocket.write(Buffer.from(abuf)); this.emit('connect', host); }); + debug && console.log('start frames', serial); remoteSocket.on('data', (data) => { if (data.length === 16) { if (frame) { this.emit('frame', frame); + debug && console.log('frame', serial); frame = undefined; } } else { @@ -58,6 +61,7 @@ class FrameStream extends EventEmitter { }); remoteSocket.on('close', () => { + debug && console.log('close frames', serial); this.emit('close'); }) } diff --git a/mods/bambu/init.js b/mods/bambu/init.js index e0df3982..0b41d39a 100644 --- a/mods/bambu/init.js +++ b/mods/bambu/init.js @@ -11,6 +11,7 @@ module.exports = async (server) => { const mcache = {}; const wsopen = []; const found = {}; + const video = {}; const useCA = false; const debug = false; @@ -64,6 +65,7 @@ module.exports = async (server) => { } set_frames(bool) { + video[this.#serial] = bool; if (this.#frames && !bool) { this.#frames.end(); this.#frames = undefined; @@ -112,6 +114,7 @@ module.exports = async (server) => { util.log('mqtt end', this.#serial); this.#client.end(); this.#client = undefined; + this.set_frames(false); } this.#topic_report = undefined; this.#topic_request = undefined; @@ -139,6 +142,7 @@ module.exports = async (server) => { if (onconn) { onconn(mqtt); } + mcache[serial]?.set_frames(video[serial]); }, error => fns.reject(error), onmsg); } @@ -261,8 +265,9 @@ module.exports = async (server) => { } } - // insert script before all others in kiri client + // insert scripts before all others in kiri client server.inject("kiri", "bambu.js"); + server.inject("kiri", "filament.js"); function o2s(obj) { return JSON.stringify(obj); @@ -420,6 +425,7 @@ module.exports = async (server) => { case "frames": debug && util.log('request frames', serial, frames); mcache[serial]?.set_frames(frames); + video[serial] = frames; break; case "keepalive": // util.log({ keepalive: serial }); diff --git a/src/kiri-mode/fdm/export.js b/src/kiri-mode/fdm/export.js index 07ba65cd..b984e8b7 100644 --- a/src/kiri-mode/fdm/export.js +++ b/src/kiri-mode/fdm/export.js @@ -137,7 +137,7 @@ FDM.export = function(print, online, ondone, ondebug) { subst.tool_count = tools_used.length; // encodes an array offset as a single "on" bit in an - // 8 byte array then converts the array to base64 which + // 8 byte array, then converts the array to base64 which // is what Bambu's M624 uses to flag an object as currently // being printed. the array is presented at the top of the // gcode as a comment: "; model label id: {model_labels}" diff --git a/web/kiri/index.css b/web/kiri/index.css index 7cc5203a..2edef98e 100644 --- a/web/kiri/index.css +++ b/web/kiri/index.css @@ -654,6 +654,9 @@ details[open] summary::after { .font-smol { font-size: smaller; } +.font-tiny { + font-size: x-small; +} .gap1 { gap: 1px; } @@ -678,6 +681,9 @@ details[open] summary::after { .pad4 { padding: 4px !important; } +.pad5 { + padding: 5px !important; +} .h100 { height: 100%; }