Compare commits
3 commits
4daf15d390
...
ff2b0bc11f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff2b0bc11f | ||
|
|
73176cc41d | ||
|
|
88988237ae |
3 changed files with 39 additions and 5 deletions
|
|
@ -34,6 +34,7 @@ function requestFullReport(c) {
|
||||||
async function flushTrays(c) {
|
async function flushTrays(c) {
|
||||||
clearTimeout(c.traysTimer);
|
clearTimeout(c.traysTimer);
|
||||||
c.traysTimer = null;
|
c.traysTimer = null;
|
||||||
|
c.pendingKey = null;
|
||||||
const trays = allTrays(c);
|
const trays = allTrays(c);
|
||||||
if (!c.ams && !c.external) return null;
|
if (!c.ams && !c.external) return null;
|
||||||
c.syncedKey = traysKey(trays);
|
c.syncedKey = traysKey(trays);
|
||||||
|
|
@ -60,7 +61,11 @@ function handleMessage(c, payload) {
|
||||||
if (Array.isArray(units) && units.every((u) => Array.isArray(u.tray))) c.ams = describeAms(units);
|
if (Array.isArray(units) && units.every((u) => Array.isArray(u.tray))) c.ams = describeAms(units);
|
||||||
if (p.vt_tray) c.external = describeTray(p.vt_tray, null);
|
if (p.vt_tray) c.external = describeTray(p.vt_tray, null);
|
||||||
|
|
||||||
if ((c.ams || c.external) && traysKey(allTrays(c)) !== c.syncedKey) {
|
// A printer mid-print reports every second or so, so only a change in what's
|
||||||
|
// loaded restarts the wait — otherwise the sync would never come round.
|
||||||
|
const key = (c.ams || c.external) ? traysKey(allTrays(c)) : null;
|
||||||
|
if (key && key !== c.syncedKey && key !== c.pendingKey) {
|
||||||
|
c.pendingKey = key;
|
||||||
clearTimeout(c.traysTimer);
|
clearTimeout(c.traysTimer);
|
||||||
c.traysTimer = setTimeout(() => flushTrays(c), TRAYS_SETTLE_MS);
|
c.traysTimer = setTimeout(() => flushTrays(c), TRAYS_SETTLE_MS);
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +87,7 @@ function open(p) {
|
||||||
ams: null,
|
ams: null,
|
||||||
external: null,
|
external: null,
|
||||||
syncedKey: null,
|
syncedKey: null,
|
||||||
|
pendingKey: null,
|
||||||
lastSync: null,
|
lastSync: null,
|
||||||
traysTimer: null,
|
traysTimer: null,
|
||||||
waiters: [],
|
waiters: [],
|
||||||
|
|
|
||||||
|
|
@ -2630,8 +2630,11 @@ function drawnSlots(p) {
|
||||||
// What's in each slot, as last reported, under the drawing.
|
// What's in each slot, as last reported, under the drawing.
|
||||||
function loadedHtml(p) {
|
function loadedHtml(p) {
|
||||||
if (!p.loaded) return p.id ? '<p class="form-hint">Nothing reported yet — “Test & sync” to read it.</p>' : '';
|
if (!p.loaded) return p.id ? '<p class="form-hint">Nothing reported yet — “Test & sync” to read it.</p>' : '';
|
||||||
// The tracked spool in that slot names the colour; otherwise the nearest Bambu colour.
|
// A tagged spool is Bambu's, so its colour has a name: from the tracked spool,
|
||||||
|
// or the Bambu colour it matches. An untagged one could be any brand, so it
|
||||||
|
// gets the colour the printer reports and nothing more.
|
||||||
const colourName = (t) => {
|
const colourName = (t) => {
|
||||||
|
if (!t.tagged) return t.colour || '';
|
||||||
const s = spools.find((x) => x.ams_slot === `${p.serial}/${t.unit == null ? 'ext' : t.unit}/${t.slot == null ? 0 : t.slot}`);
|
const s = spools.find((x) => x.ams_slot === `${p.serial}/${t.unit == null ? 'ext' : t.unit}/${t.slot == null ? 0 : t.slot}`);
|
||||||
return (s && s.colour) || (t.colour ? paletteName(t.colour, t.material) : '') || '';
|
return (s && s.colour) || (t.colour ? paletteName(t.colour, t.material) : '') || '';
|
||||||
};
|
};
|
||||||
|
|
@ -2747,7 +2750,10 @@ const temp = (now, target) => {
|
||||||
return target ? `${t} / ${Math.round(target)} °C` : `${t} °C`;
|
return target ? `${t} / ${Math.round(target)} °C` : `${t} °C`;
|
||||||
};
|
};
|
||||||
|
|
||||||
function printerStatusHtml(snap) {
|
// These report a placeholder chamber temperature (5 °C) rather than a reading.
|
||||||
|
const NO_CHAMBER_SENSOR = ['A1 mini', 'A1', 'A2L', 'P1P', 'P1S'];
|
||||||
|
|
||||||
|
function printerStatusHtml(snap, model) {
|
||||||
if (!snap || !snap.live) {
|
if (!snap || !snap.live) {
|
||||||
return '<div class="status-head"><span class="status-pill is-off">Not connected</span> <span class="form-hint">Fill in the address, serial number and access code to connect.</span></div>';
|
return '<div class="status-head"><span class="status-pill is-off">Not connected</span> <span class="form-hint">Fill in the address, serial number and access code to connect.</span></div>';
|
||||||
}
|
}
|
||||||
|
|
@ -2778,7 +2784,7 @@ function printerStatusHtml(snap) {
|
||||||
const temps = [
|
const temps = [
|
||||||
temp(s.nozzle, s.nozzleTarget) && `Nozzle ${temp(s.nozzle, s.nozzleTarget)}`,
|
temp(s.nozzle, s.nozzleTarget) && `Nozzle ${temp(s.nozzle, s.nozzleTarget)}`,
|
||||||
temp(s.bed, s.bedTarget) && `Bed ${temp(s.bed, s.bedTarget)}`,
|
temp(s.bed, s.bedTarget) && `Bed ${temp(s.bed, s.bedTarget)}`,
|
||||||
s.chamber ? `Chamber ${temp(s.chamber)}` : null,
|
s.chamber > 5 && !NO_CHAMBER_SENSOR.includes(model) ? `Chamber ${temp(s.chamber)}` : null,
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
if (temps.length) lines.push(`<div class="status-facts">${temps.map(escapeHtml).join(' · ')}</div>`);
|
if (temps.length) lines.push(`<div class="status-facts">${temps.map(escapeHtml).join(' · ')}</div>`);
|
||||||
|
|
||||||
|
|
@ -2808,7 +2814,7 @@ async function pollPrinters() {
|
||||||
const snap = live[id];
|
const snap = live[id];
|
||||||
const box = card.querySelector('.printer-status');
|
const box = card.querySelector('.printer-status');
|
||||||
if (box) {
|
if (box) {
|
||||||
const html = printerStatusHtml(snap);
|
const html = printerStatusHtml(snap, card.querySelector('select[name="model"]').value);
|
||||||
if (box.dataset.html !== html) { box.innerHTML = html; box.dataset.html = html; }
|
if (box.dataset.html !== html) { box.innerHTML = html; box.dataset.html = html; }
|
||||||
}
|
}
|
||||||
// A spool swap shows as soon as the printer reports it.
|
// A spool swap shows as soon as the printer reports it.
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,28 @@ test('live status merges partial reports, and a tray change syncs spools once',
|
||||||
assert.equal(c.published.at(-1).body.pushing.command, 'pushall');
|
assert.equal(c.published.at(-1).body.pushing.command, 'pushall');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a busy printer’s steady reports don’t hold back a spool sync', async (t) => {
|
||||||
|
t.mock.timers.enable({ apis: ['setTimeout'] });
|
||||||
|
t.after(() => live.stopAll());
|
||||||
|
clients.length = 0;
|
||||||
|
const synced = [];
|
||||||
|
live.setTraysHandler(async (id) => { synced.push(id); return {}; });
|
||||||
|
live.reconcile([{ ...printerRow, id: 5, serial: 'SER5' }]);
|
||||||
|
const c = clients[0];
|
||||||
|
c.connected = true;
|
||||||
|
c.emit('connect');
|
||||||
|
c.emit('message', 't', report({ ams: { ams: [{ id: '0', tray: [tray(0, 'PLA', 'FF0000FF')] }] } }));
|
||||||
|
for (let i = 0; i < 10; i += 1) {
|
||||||
|
t.mock.timers.tick(1000);
|
||||||
|
c.emit('message', 't', report({ mc_percent: i }));
|
||||||
|
}
|
||||||
|
assert.equal(synced.length, 1, 'synced once, five seconds after the change');
|
||||||
|
// The same trays again change nothing.
|
||||||
|
c.emit('message', 't', report({ ams: { ams: [{ id: '0', tray: [tray(0, 'PLA', 'FF0000FF')] }] } }));
|
||||||
|
t.mock.timers.tick(6000);
|
||||||
|
assert.equal(synced.length, 1);
|
||||||
|
});
|
||||||
|
|
||||||
test('changing a printer’s code reopens its connection; removing it closes it', () => {
|
test('changing a printer’s code reopens its connection; removing it closes it', () => {
|
||||||
clients.length = 0;
|
clients.length = 0;
|
||||||
live.reconcile([printerRow]);
|
live.reconcile([printerRow]);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue