Each sync keeps what the printer reported, slot by slot (material, colour, empty, tagged — nothing that identifies a spool). The card's drawing colours in the first AMS unit's spools, dashes empty slots, and lists every slot and the external holder underneath with its colour name. "Test & sync" redraws the card with what it just read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
105 lines
3.8 KiB
JavaScript
105 lines
3.8 KiB
JavaScript
// The printers you've added.
|
|
//
|
|
// Access codes are stored here but never handed back to the browser: every
|
|
// read returns `codeSet` instead, and saving with a blank code keeps the one
|
|
// already stored.
|
|
const db = require('./db');
|
|
|
|
// Which AMS types can say how much filament is left. An AMS lite only knows
|
|
// full or empty, so its reading is never used as a weight.
|
|
const AMS_TYPES = { none: false, ams_lite: false, ams: true, ams_2_pro: true, ams_ht: true };
|
|
|
|
const clean = (value, max) => {
|
|
const s = String(value ?? '').trim();
|
|
return s ? s.slice(0, max) : null;
|
|
};
|
|
|
|
function publicPrinter(row) {
|
|
if (!row) return null;
|
|
const { access_code: accessCode, ...rest } = row;
|
|
return { ...rest, codeSet: Boolean(accessCode) };
|
|
}
|
|
|
|
function fields(data) {
|
|
return {
|
|
nickname: clean(data.nickname, 60),
|
|
model: clean(data.model, 40),
|
|
host: clean(data.host, 255),
|
|
serial: clean(data.serial, 40),
|
|
ams_type: Object.prototype.hasOwnProperty.call(AMS_TYPES, data.ams_type) ? data.ams_type : null,
|
|
};
|
|
}
|
|
|
|
async function list() {
|
|
const { rows } = await db.query('SELECT * FROM printers ORDER BY id');
|
|
return rows.map(publicPrinter);
|
|
}
|
|
|
|
// Server-side only: includes the access code, for connecting.
|
|
async function listWithCodes() {
|
|
const { rows } = await db.query('SELECT * FROM printers ORDER BY id');
|
|
return rows;
|
|
}
|
|
|
|
async function get(id, { withCode = false } = {}) {
|
|
const { rows: [row] } = await db.query('SELECT * FROM printers WHERE id = $1', [id]);
|
|
if (!row) return null;
|
|
return withCode ? row : publicPrinter(row);
|
|
}
|
|
|
|
async function create(data) {
|
|
const f = fields(data);
|
|
const { rows: [row] } = await db.query(
|
|
`INSERT INTO printers (nickname, model, host, serial, access_code, ams_type)
|
|
VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
|
|
[f.nickname, f.model, f.host, f.serial, clean(data.access_code, 64), f.ams_type]);
|
|
return publicPrinter(row);
|
|
}
|
|
|
|
// Only the fields sent are changed; a blank access code keeps the stored one.
|
|
async function update(id, data) {
|
|
const f = fields(data);
|
|
const has = (key) => Object.prototype.hasOwnProperty.call(data, key);
|
|
const { rows: [row] } = await db.query(
|
|
`UPDATE printers SET
|
|
nickname = CASE WHEN $2::boolean THEN $3 ELSE nickname END,
|
|
model = CASE WHEN $4::boolean THEN $5 ELSE model END,
|
|
host = CASE WHEN $6::boolean THEN $7 ELSE host END,
|
|
serial = CASE WHEN $8::boolean THEN $9 ELSE serial END,
|
|
ams_type = CASE WHEN $10::boolean THEN $11 ELSE ams_type END,
|
|
access_code = COALESCE($12, access_code),
|
|
date_modified = now()
|
|
WHERE id = $1 RETURNING *`,
|
|
[id, has('nickname'), f.nickname, has('model'), f.model, has('host'), f.host,
|
|
has('serial'), f.serial, has('ams_type'), f.ams_type, clean(data.access_code, 64)]);
|
|
return publicPrinter(row);
|
|
}
|
|
|
|
// Removing a printer also marks its spools as no longer loaded anywhere.
|
|
async function remove(id) {
|
|
const { rows: [row] } = await db.query('DELETE FROM printers WHERE id = $1 RETURNING serial', [id]);
|
|
if (!row) return false;
|
|
if (row.serial) {
|
|
await db.query('UPDATE filament_spools SET ams_slot = NULL WHERE printer_serial = $1', [row.serial]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Keeps what a printer reported loaded — enough to draw its card, nothing that
|
|
// identifies a spool.
|
|
async function recordLoaded(id, trays) {
|
|
const loaded = (trays || []).map((t) => ({
|
|
unit: t.unit ?? null,
|
|
slot: t.slot ?? null,
|
|
empty: Boolean(t.empty),
|
|
material: t.material || null,
|
|
colour: t.colour || null,
|
|
tagged: Boolean(t.uuid),
|
|
}));
|
|
await db.query('UPDATE printers SET loaded = $2::jsonb, loaded_at = now() WHERE id = $1',
|
|
[id, JSON.stringify(loaded)]);
|
|
}
|
|
|
|
const measures = (amsType) => Boolean(AMS_TYPES[amsType]);
|
|
|
|
module.exports = { AMS_TYPES, list, listWithCodes, get, create, update, remove, recordLoaded, measures, publicPrinter };
|