diff --git a/config.sl1.js b/config.sl1.js index a81518ab..0901ec7f 100644 --- a/config.sl1.js +++ b/config.sl1.js @@ -15,6 +15,7 @@ module.exports = (env, args) => { WITH_START_PRINT_AFTER_UPLOAD: true, WITH_LOGS: false, WITH_FONT: false, + WITH_V1_API: true, ...env, }; return webpackConfig(config, args); diff --git a/src/printer/common.js b/src/printer/common.js index efd67f26..2c008f54 100644 --- a/src/printer/common.js +++ b/src/printer/common.js @@ -2,9 +2,10 @@ import { LinkState, translateState } from "../state"; const SEPARATOR = " - "; -export const getPrinterLabel = (context) => { - return buildTitle([context.printer?.location, context.printer?.name]); -}; +export const getPrinterLabel = (context) => buildTitle([ + context.printer?.location || context.printer?.hostname, + context.printer?.name +]); export const buildTitle = (titleItems) => { return [...titleItems] diff --git a/src/printer/components/cameras.js b/src/printer/components/cameras.js index 9f47cd01..551dc7cb 100644 --- a/src/printer/components/cameras.js +++ b/src/printer/components/cameras.js @@ -411,7 +411,6 @@ const createCameraSettingsModal = (cameraId, resolve) => { inputTriggerScheme.value = translateTriggerScheme(data.trigger_scheme); setVisible(inputFocus.parentNode, hasFocus); - console.log(`DEBUG: has focus (${hasFocus})`, data) if (hasFocus) { inputFocus.value = Math.round(data.focus * 100); } diff --git a/src/printer/components/dataFormat.js b/src/printer/components/dataFormat.js index d0003579..94e08a7e 100644 --- a/src/printer/components/dataFormat.js +++ b/src/printer/components/dataFormat.js @@ -20,19 +20,6 @@ const str_GB = translate("unit.gb"); const str_true = translate("prop.true"); const str_false = translate("prop.false"); -/** - * Format the value data with format specificated. - * @param {string} format - one of ["int", "number", "layer", "temp", "fan", "resin", "cover", "date", "progress", "timeEst", "time", "expo", "boolean"] - * @param {any} value - */ -const formatData = (format, value) => { - if (process.env.PRINTER_TYPE === "sla") { - return slaFormatData(format, value); - } else { - return fdmFormatData(format, value); - } -}; - /** * It formats a number using fixed-point notation with one digit after the decimal point. * ex: 123.456 => 123.4 @@ -193,14 +180,15 @@ function formatBoolean(value) { } /** - * Format the value data with format specificated for sla type. - * @param {string} format - one of ["int", "number", "layer", "temp", "fan", "resin", "cover", "date", "progress", "timeEst", "time", "expo", "boolean"] + * Format the value data with format specificated. + * @param {string} format - one of ["number", "temp", "fan", "pos", "date", "progress", "timeEst", "time"] * @param {any} value */ -const slaFormatData = (format, value) => { +const formatData = (format, value) => { if (value === undefined || (value === null && format !== "progress")) { return translate("prop.na"); } + switch (format) { case "int": return parseInt(value); @@ -208,60 +196,22 @@ const slaFormatData = (format, value) => { return numberFormat(value); case "layer": return numberFormat(value, false) + " mm"; - case "temp": - return numberFormat(value) + " °C"; - case "fan": - return numberFormat(value) + ` ${str_rpm}`; - case "resin": - return numberFormat(value) + ` ${str_ml}`; - case "cover": - return value - ? translate("prop.cover-closed") - : translate("prop.cover-opened"); - case "date": - return dateFormat(value); - case "progress": - return numberFormat((value || 0) * 100, true, 0) + "%"; - case "timeEst": - return formatEstimatedTime(value); - case "time": - return formatTime(value); - case "est-time": - return "~ " + formatTime(value); - case "expo": - return formatExposure(value); case "totalLayer": return totalLayers(value); case "material": return value || translate("prop.na"); - case "size": - return formatSize(value); - case "boolean": - return formatBoolean(value); - default: - return value; - } -}; - -/** - * Format the value data with format specificated for fdm type. - * @param {string} format - one of ["number", "temp", "fan", "pos", "date", "progress", "timeEst", "time"] - * @param {any} value - */ -const fdmFormatData = (format, value) => { - if (value === undefined || (value === null && format !== "progress")) { - return translate("prop.na"); - } - - switch (format) { - case "number": - return numberFormat(value); case "temp": return numberFormat(value) + " °C"; case "temp_int": return numberFormat(value, 0) + "°C"; case "fan": return numberFormat(value) + ` ${str_rpm}`; + case "resin": + return numberFormat(value) + ` ${str_ml}`; + case "cover": + return value + ? translate("prop.cover-closed") + : translate("prop.cover-opened"); case "print": return numberFormat(value || 0, true, 0) + "%"; case "pos": diff --git a/src/printer/components/files.js b/src/printer/components/files.js index 8223fb54..7a2518a6 100644 --- a/src/printer/components/files.js +++ b/src/printer/components/files.js @@ -202,6 +202,9 @@ const updateFiles = (opts = {}) => { headers: { "If-None-Match": lastETag }, }) .then((result) => { + if (result.code === 304) { + return + } if (url !== getCurrentApiPath()) { // user navigated to other folder return; diff --git a/src/printer/components/job.js b/src/printer/components/job.js index 9d0e622d..6dd65386 100644 --- a/src/printer/components/job.js +++ b/src/printer/components/job.js @@ -2,22 +2,18 @@ // Copyright (C) 2021 Prusa Research a.s. - www.prusa3d.com // SPDX-License-Identifier: GPL-3.0-or-later -import joinPaths from "../../helpers/join_paths"; import updateProperties from "./updateProperties"; import { cancelJob, cancelPreview, pauseJob, resumeJob, startJob } from "./jobActions"; import { deleteFile, downloadFile } from "./fileActions"; -import { getImage, getJson } from "../../auth"; -import { handleError } from "./errors"; -import { renderProgressImg, updateProgressImg } from "./progressImage"; +import { renderProgressImg } from "./progressImage"; import { setEnabled, setHidden, setVisible, showLoading, hideLoading } from "../../helpers/element"; import { updateProgressBar } from "./progressBar"; import { translate } from "../../locale_provider"; -import changeExposureTimesQuestion from "../sla/exposure"; -import { resinRefill } from "../sla/refill"; +import changeExposureTimesQuestion from "../views/exposure"; +import { resinRefill } from "../views/refill"; import { JobPendingStates, LinkState, OperationalStates } from "../../state"; import { setButtonLoading, unsetButtonLoading } from "../../helpers/button"; import printer from ".."; -import { context } from "../fdm"; let pendingCommand = null; let pendingDownload = null; @@ -102,14 +98,13 @@ function updateComponent(context, isFilePreview) { function setupRefill(stateText) { const preview = document.getElementById("preview-wrapper") const refill = document.getElementById("refill-wrapper") - if (stateText == "Feed me" || stateText == "Pour in resin") { - if (stateText == "Pour in resin") { + if ([LinkState.REFILL, LinkState.POUR_IN_RESIN].includes(stateText)) { + if (stateText == LinkState.POUR_IN_RESIN) { translate("msg.sla-pour-resin", { query: "#sla-refill-text" }); } setHidden(preview); setVisible(refill); - } - else { + } else { setHidden(refill); setVisible(preview); } @@ -173,18 +168,13 @@ function setupButtons(state, dataSource, jobId) { setupDownloadButton(state, file, jobId); if (!!jobId) { - if (process.env.PRINTER_TYPE === "fdm") { - setupPauseButton(state, jobId, "#job #pause"); - setupResumeButton(state, jobId); - } + setupPauseButton(state, jobId, "#job #pause"); + setupResumeButton(state, jobId); if (process.env.PRINTER_TYPE === "sla") { - const jobFile = jobResult?.job?.file; - if (jobFile) - setupExposureButton(state, jobFile, changeExposureTimesQuestion); - setupPauseButton(state, jobId, "#job #refill"); - setupSlaResumeButton(state, "#job #continue"); - setupSlaResumeButton(state, "#job #back"); + setupExposureButton(state, file, changeExposureTimesQuestion); + // setupSlaResumeButton(state, "#job #resume"); + //setupSlaResumeButton(state, "#job #back"); } } } @@ -252,7 +242,7 @@ function setupPauseButton(state, jobId, selector) { function setupResumeButton(state, jobId) { const btn = document.querySelector("#job #resume"); - const isPaused = state === LinkState.PAUSED; + const isPaused = [LinkState.PAUSED, LinkState.POUR_IN_RESIN].includes(state); setVisible(btn, isPaused); setEnabled(btn, !pendingCommand && isPaused); @@ -260,8 +250,8 @@ function setupResumeButton(state, jobId) { btn.onclick = () => { setEnabled(btn, false); pendingCommand = {code: "resume", state: state}; - resumeJob(jobId) - .catch(() => pendingCommand = null); + const command = (state !== LinkState.REFILL) ? resumeJob(jobId) : resinRefill(); + command.catch(() => pendingCommand = null); } } } @@ -269,13 +259,13 @@ function setupResumeButton(state, jobId) { function setupSlaResumeButton(state, selector) { const btn = document.querySelector(selector); if (selector.includes("#back")) - setVisible(btn, state.flags.paused && state.text === "Feed me"); + setVisible(btn, state === LinkState.REFILL); else - setVisible(btn, state.flags.paused); + setVisible(btn, state === LinkState.PAUSED); if (btn) { - if (state.text == "Feed me" && !selector.includes("#back")) { + if (state == LinkState.REFILL && !selector.includes("#back")) { btn.onclick = resinRefill; } else { btn.onclick = resumeJob; @@ -324,8 +314,8 @@ function setupDownloadButton(state, file, jobId) { function setupExposureButton(state, jobFile, changeExposureTimesQuestion) { const btn = document.querySelector("#job #exposure"); if (btn) { - setVisible(btn, state.text === "Pour in resin" || state.text === "Printing"); - setEnabled(btn, state.flags.operational); + setVisible(btn, [LinkState.POUR_IN_RESIN, LinkState.PRINTING].includes(state)); + setEnabled(btn, [...OperationalStates, LinkState.POUR_IN_RESIN].includes(state)); btn.onclick = () => changeExposureTimesQuestion(jobFile); } } diff --git a/src/printer/components/toast.js b/src/printer/components/toast.js index 20792d0d..8674740d 100644 --- a/src/printer/components/toast.js +++ b/src/printer/components/toast.js @@ -38,7 +38,9 @@ export const createToast = (title, message, type) => { function show({ title, message, type, onClose }) { const article = createToast(title, message, type); const close = () => { - toast_context.removeChild(article); + if (toast_context.contains(article)) { + toast_context.removeChild(article); + } onClose?.(); }; diff --git a/src/printer/fdm/context.js b/src/printer/context.js similarity index 88% rename from src/printer/fdm/context.js rename to src/printer/context.js index 491e43d8..8b859e29 100644 --- a/src/printer/fdm/context.js +++ b/src/printer/context.js @@ -1,7 +1,7 @@ -import { getImage, getJson } from "../../auth"; -import { handleError } from "../components/errors"; -import { LinkState } from "../../state"; -import { getEstimatedEnd } from "../common"; +import { getImage, getJson } from "../auth"; +import { handleError } from "./components/errors"; +import { LinkState } from "../state"; +import { getEstimatedEnd } from "./common"; export class Context { constructor() { @@ -64,14 +64,14 @@ export class Context { return getJson("/api/connection", { method: "GET" }) .then(res => { this.link.connect.settings = { - hostname: res.data.connect.hostname, - port: res.data.connect.port, - tls: res.data.connect.tls, + hostname: res.data.connect?.hostname, + port: res.data.connect?.port, + tls: res.data.connect?.tls, }; - this.link.connect.registration = res.data.connect.registration; + this.link.connect.registration = res.data.connect?.registration; this.link.printer.settings = { - port: res.data.current.port, - baudrate: res.data.current.baudrate, + port: res.data.current?.port, + baudrate: res.data.current?.baudrate, }; }); } @@ -105,12 +105,14 @@ export class Context { hostname: printer.hostname, port: printer.port, }; + this.fileExtensions = printer.project_extensions ?? process.env["FILE_EXTENSIONS"].split(",") } updateTelemetry(printer) { this.state = LinkState.fromApi(printer.state.toUpperCase()); this.telemetry = { temperature: { + // fdm nozzle: { current: printer.temp_nozzle, target: printer.target_nozzle, @@ -119,6 +121,16 @@ export class Context { current: printer.temp_bed, target: printer.target_bed, }, + // sla + ambient: { + current: printer.temp_ambient, + }, + cpu: { + current: printer.temp_cpu, + }, + uvLED: { + current: printer.temp_uv_led, + }, }, axis: { x: printer.axis_x, @@ -128,9 +140,15 @@ export class Context { flow: printer.flow, speed: printer.speed, fan: { + // fdm hotend: printer.fan_hotend, print: printer.fan_print, + // sla + blower: printer.fan_blower, + rear: printer.fan_rear, + uvLED: printer.fan_uv_led, }, + coverClosed: printer.cover_closed, }; // hide status if connect is not supported this.link.connect.message = printer.status_connect?.message ?? ""; @@ -344,6 +362,10 @@ const mapFile = (data) => ({ filamentType: data.meta?.filament_type, layerHeight: data.meta?.layer_height, estimatedPrintTime: data.meta?.estimated_print_time, + exposureTime: data.meta?.exposure_time, + exposureTimeCalibration: data.meta?.exposure_time_calibration, + exposureTimeFirst: data.meta?.exposure_time_first, + exposureUserProfile: data.meta?.exposure_user_profile, }, readOnly: data.read_only || data.ro }); diff --git a/src/printer/fdm/index.js b/src/printer/fdm/index.js deleted file mode 100644 index 67db4ed7..00000000 --- a/src/printer/fdm/index.js +++ /dev/null @@ -1,151 +0,0 @@ -// This file is part of the Prusa Link Web -// Copyright (C) 2021 Prusa Research a.s. - www.prusa3d.com -// SPDX-License-Identifier: GPL-3.0-or-later - -import * as graph from "../components/temperature_graph"; -import dashboard from "./dashboard.js"; -import files from "../components/files"; -import question from "../components/question.js"; -import { buildTitle, getPrinterLabel, getStatusForTitle } from "../common.js"; -import { updateProperties } from "../components/updateProperties.js"; -import { translate } from "../../locale_provider"; -import { LinkState, translateState } from "../../state"; -import updateConnectionStatus from "../components/updateConnectionStatus"; -import { currentRoute } from "../../router"; -import { Context } from "./context"; - -export const context = new Context(); - -const updatePrinterTitle = (obj) => { - const newTitle = () => { - const label = document.getElementById("title-printer"); - if (label) { - label.innerHTML = getPrinterName(); - } - }; - const load = obj.load; - obj.load = () => { - newTitle(); - load(context); - }; - return obj; -}; - -const getPrinterName = () => getPrinterLabel(context); - -const updatePrinterStatus = (state) => { - const linkState = state; - const elem = document.getElementById("printer-status"); - if (elem) { - elem.innerHTML = translateState(linkState); - } -}; - -const buildRouteTitle = (titleItems) => buildTitle([ - ...titleItems, - getPrinterName(), - process.env["APP_NAME"] -]); - -let currentModule = dashboard; -const fdm = { - routes: [ - { - path: "dashboard", - html: require("../../views/dashboard.html"), - module: updatePrinterTitle(dashboard), - getTitle: () => translate("home.link"), - }, - { - path: "question", - html: require("../../views/question.html"), - module: updatePrinterTitle(question), - }, - process.env.WITH_FILES ? - { - path: "files", - html: require("../../views/files.html"), - module: updatePrinterTitle(files), - getTitle: () => translate("proj.storage"), - } - : null, - process.env.WITH_SETTINGS ? - { - path: "settings", - html: require("../../views/settings.html"), - module: updatePrinterTitle(require("../components/settings.js").default), - getTitle: () => translate("settings.title"), - } - : null, - process.env.WITH_CONTROLS ? - { - path: "control", - html: require("../../views/control.html"), - module: updatePrinterTitle(require("../components/control.js").default), - getTitle: () => translate("control.link"), - } : null, - process.env.WITH_CAMERAS ? - { - path: "cameras", - html: require("../../views/cameras.html"), - module: updatePrinterTitle(require("../components/cameras.js").default), - getTitle: () => translate("cameras.link"), - } : null, - ].filter(route => route != null), - init: (apiResult) => { - context.update(apiResult); - initTemperatureGraph(); - }, - update: (apiResult) => { - context.update(apiResult); - - const page = currentRoute(); - const stateText = getStatusForTitle(context); - document.title = buildRouteTitle([ - stateText, - fdm.routes.find(route => route.path === page).getTitle() - ]); - - updateProperties("telemetry", context); - updatePrinterStatus(context.state); - updateTemperatureGraph(context.telemetry); - updateModule(); - }, - setConnected: (isConnected) => { - updateConnectionStatus({ - link: context.link, - isConnected, - }); - }, - setModule: (module) => { - currentModule = module; - }, - getContext: () => { - return context; - }, -}; - -const initTemperatureGraph = () => { - const maxTemp = 300; - - let map = new Map([ - ["temp-line-blue", []], - ["temp-line-orange", []], - ]); - - graph.init(map, maxTemp); - graph.render(); -}; - -const updateTemperatureGraph = (telemetry) => { - const now = new Date().getTime(); - graph.update("temp-line-blue", [now, telemetry.temperature.bed.current]); - graph.update("temp-line-orange", [now, telemetry.temperature.nozzle.current]); - graph.render(); -}; - -const updateModule = () => { - if (currentModule && currentModule.update) currentModule.update(context); -}; - -export default fdm; diff --git a/src/printer/index.js b/src/printer/index.js index 7464d4c3..330ab678 100644 --- a/src/printer/index.js +++ b/src/printer/index.js @@ -2,9 +2,174 @@ // Copyright (C) 2021 Prusa Research a.s. - www.prusa3d.com // SPDX-License-Identifier: GPL-3.0-or-later -const printer = (() => { - if (process.env.PRINTER_TYPE === "sla") return require("./sla"); - if (process.env.PRINTER_TYPE === "fdm") return require("./fdm"); -})().default; +import * as graph from "./components/temperature_graph"; +import dashboard from "./views/dashboard.js"; +import files from "./components/files"; +import question from "./components/question.js"; +import refill from "./views/refill.js"; +import { buildTitle, getPrinterLabel, getStatusForTitle } from "./common.js"; +import { updateProperties } from "./components/updateProperties.js"; +import { translate } from "../locale_provider"; +import { translateState } from "../state"; +import updateConnectionStatus from "./components/updateConnectionStatus"; +import { currentRoute } from "../router"; +import { Context } from "./context"; + +const context = new Context(); + +const updatePrinterTitle = (obj) => { + const newTitle = () => { + const label = document.getElementById("title-printer"); + if (label) { + label.innerHTML = getPrinterName(); + } + }; + const load = obj.load; + obj.load = () => { + newTitle(); + load(context); + }; + return obj; +}; + +const getPrinterName = () => getPrinterLabel(context); + +const updatePrinterStatus = (state) => { + const linkState = state; + const elem = document.getElementById("printer-status"); + if (elem) { + elem.innerHTML = translateState(linkState); + } +}; + +const buildRouteTitle = (titleItems) => + buildTitle([...titleItems, getPrinterName(), process.env["APP_NAME"]]); + +let currentModule = dashboard; +const printer = { + routes: [ + { + path: "dashboard", + html: require("../views/dashboard.html"), + module: updatePrinterTitle(dashboard), + getTitle: () => translate("home.link"), + }, + { + path: "question", + html: require("../views/question.html"), + module: updatePrinterTitle(question), + }, + process.env.PRINTER_TYPE === "sla" + ? { + path: "refill", + html: require("../views/refill.html"), + module: updatePrinterTitle(refill), + } + : null, + process.env.WITH_FILES + ? { + path: "files", + html: require("../views/files.html"), + module: updatePrinterTitle(files), + getTitle: () => translate("proj.storage"), + } + : null, + process.env.WITH_SETTINGS + ? { + path: "settings", + html: require("../views/settings.html"), + module: updatePrinterTitle( + require("./components/settings.js").default + ), + getTitle: () => translate("settings.title"), + } + : null, + process.env.WITH_CONTROLS + ? { + path: "control", + html: require("../views/control.html"), + module: updatePrinterTitle( + require("./components/control.js").default + ), + getTitle: () => translate("control.link"), + } + : null, + process.env.WITH_CAMERAS + ? { + path: "cameras", + html: require("../views/cameras.html"), + module: updatePrinterTitle( + require("./components/cameras.js").default + ), + getTitle: () => translate("cameras.link"), + } + : null, + ].filter((route) => route != null), + init: (apiResult) => { + context.update(apiResult); + initTemperatureGraph(); + }, + update: (apiResult) => { + context.update(apiResult); + + const page = currentRoute(); + const stateText = getStatusForTitle(context); + document.title = buildRouteTitle([ + stateText, + printer.routes.find((route) => route.path === page).getTitle(), + ]); + + updateProperties("telemetry", context); + updatePrinterStatus(context.state); + updateTemperatureGraph(context.telemetry); + updateModule(); + }, + setConnected: (isConnected) => { + updateConnectionStatus({ + link: context.link, + isConnected, + }); + }, + setModule: (module) => { + currentModule = module; + }, + getContext: () => { + return context; + }, +}; + +const initTemperatureGraph = () => { + let maxTemp = 300; + + const map = new Map([ + ["temp-line-blue", []], + ["temp-line-orange", []], + ]); + + if (process.env.PRINTER_TYPE === "sla") { + map.set("temp-line-yellow", []) + } + + graph.init(map, maxTemp); + graph.render(); +}; + +const updateTemperatureGraph = (telemetry) => { + const now = new Date().getTime(); + if (process.env.PRINTER_TYPE === "fdm") { + graph.update("temp-line-blue", [now, telemetry.temperature.bed.current]); + graph.update("temp-line-orange", [now, telemetry.temperature.nozzle.current]); + } + if (process.env.PRINTER_TYPE === "sla") { + graph.update("temp-line-blue", [now, telemetry.temperature.ambient.current]); + graph.update("temp-line-orange", [now, telemetry.temperature.cpu.current]); + graph.update("temp-line-yellow", [now, telemetry.temperature.uvLED.current]); + } + graph.render(); +}; + +const updateModule = () => { + if (currentModule && currentModule.update) currentModule.update(context); +}; export default printer; diff --git a/src/printer/sla/dashboard.js b/src/printer/sla/dashboard.js deleted file mode 100644 index b4cc7f91..00000000 --- a/src/printer/sla/dashboard.js +++ /dev/null @@ -1,26 +0,0 @@ -// This file is part of the Prusa Link Web -// Copyright (C) 2021 Prusa Research a.s. - www.prusa3d.com -// SPDX-License-Identifier: GPL-3.0-or-later - -import * as graph from "../components/temperature_graph"; -import upload from "../components/upload"; -import { translate } from "../../locale_provider"; -import * as job from "../components/job"; -import { LinkState } from "../../state"; -import sla from "."; -import { getStatusForTitle } from "../common"; - -const load = (context) => { - translate("home.link", { query: "#title-status-label" }); - upload.init("local", "", context.fileExtensions); - graph.render(); - update(context); -}; - -const update = (context) => { - const linkState = context.state; - job.update(context); - upload.update(linkState); -}; - -export default { load, update }; diff --git a/src/printer/sla/index.js b/src/printer/sla/index.js deleted file mode 100644 index 73fbf861..00000000 --- a/src/printer/sla/index.js +++ /dev/null @@ -1,219 +0,0 @@ -// This file is part of the Prusa Link Web -// Copyright (C) 2021 Prusa Research a.s. - www.prusa3d.com -// SPDX-License-Identifier: GPL-3.0-or-later - -import * as graph from "../components/temperature_graph"; -import dashboard from "./dashboard.js"; -import files from "../components/files"; -import question from "../components/question.js"; -import refill from "./refill.js"; -import { updateProperties } from "../components/updateProperties.js"; -import { translate } from "../../locale_provider"; -import { showLoading, hideLoading } from "../../helpers/element"; -import { getPrinterLabel, getStatusForTitle } from "../common.js"; -import updateConnectionStatus from "../components/updateConnectionStatus"; - -import { currentRoute } from "../../router"; - -const context = { - /** Result of `api/v1/status */ - status: undefined, - - /** Result of `api/version`. */ - version: undefined, - /** Result of `api/printer`. */ - printer: undefined, - /** Result of `api/job`.job */ - current: undefined, - /** Result of `api/connection`. */ - connection: undefined, - /** Supported file extensions. */ - fileExtensions: [], -}; - -const updatePrinterTitle = (obj) => { - const newHostname = () => { - const hostnameLabel = document.getElementById("title-printer"); - if (hostnameLabel) { - hostnameLabel.innerHTML = getPrinterName(); - } - }; - const load = obj.load; - obj.load = () => { - newHostname(); - load(context); - }; - return obj; -}; - -const getPrinterName = () => getPrinterLabel(context); - - -const updatePrinterStatus = (state) => { - if (state) { - const query = { query: "#printer-status" }; - if (state.flags.error || state.flags.closedOrError) - translate("ntf.error", query); - else if (!state.flags.operational) - translate("prop.st-busy", query); - else if (state.flags.paused) { - if (state.text === "Pour in resin") - translate("prop.st-pour-resin", query); - else if (state.text === "Feed me") - translate("prop.st-feedme", query); - else - translate("prop.st-paused", query); - } - else if (state.flags.printing) - translate("prop.st-printing", query); - else - translate("prop.st-idle", query); - } -}; - -const buildRouteTitle = (titleItems) => buildTitle([ - ...titleItems, - getPrinterName(), - process.env["APP_NAME"] -]); - -let currentModule = dashboard; -const sla = { - routes: [ - { - path: "dashboard", - html: require("../../views/dashboard.html"), - module: updatePrinterTitle(dashboard), - getTitle: () => translate("home.link"), - }, - { - path: "question", - html: require("../../views/question.html"), - module: updatePrinterTitle(question), - }, - { - path: "refill", - html: require("../../views/refill.html"), - module: updatePrinterTitle(refill), - }, - process.env.WITH_FILES ? - { - path: "files", - html: require("../../views/files.html"), - module: updatePrinterTitle(files), - getTitle: () => translate("proj.storage"), - } - : null, - process.env.WITH_SETTINGS ? - { - path: "settings", - html: require("../../views/settings.html"), - module: updatePrinterTitle(require("../components/settings.js").default), - getTitle: () => translate("settings.title"), - } - : null, - process.env.WITH_CONTROLS ? - { - path: "control", - html: require("../../views/control.html"), - module: updatePrinterTitle(require("../components/control.js").default), - getTitle: () => translate("control.link"), - } : null, - ].filter(route => route != null), - init: (apiResult) => { - updateContext(apiResult); - const exts = apiResult.profiles?.payload?.data?.profiles[0]?.projectExtensions; - context.fileExtensions = exts || process.env.FILE_EXTENSIONS; - initTemperatureGraph(); - }, - update: (apiResult) => { - const page = currentRoute(); - const stateText = getStatusForTitle(context); - document.title = buildRouteTitle([ - stateText, - fdm.routes.find(route => route.path === page).getTitle() - ]); - - updateContext(apiResult); - if (context.printer.state.flags.operational) - hideLoading(); - else - showLoading(); - updateProperties("telemetry", context.printer); - updatePrinterStatus(context.printer.state); - updateTemperatureGraph(context.printer); - updateModule(); - }, - setConnected: (isConnected) => { - updateConnectionStatus({ - connection: context.connection, - isConnected, - }); - }, - setModule: (module) => { - currentModule = module; - }, - getContext: () => { - return context; - }, -}; - -const updateContext = ({ status, connection, job, printer, version }) => { - if (status?.ok && status.payload) { - context.status = status.payload.data; - } - - /* - if (connection?.ok && connection.payload) { - context.connection = connection.payload.data; - } - if (job?.ok && job.payload) { - context.current = job.payload.data; - } - if (printer?.ok && printer.payload) { - context.printer = printer.payload.data; - } - */ - - if (version?.ok && version.payload) { - context.version = version.payload; - } -} - -const initTemperatureGraph = () => { - const maxTemp = 100; - - let map = new Map([ - ["temp-line-blue", []], - ["temp-line-orange", []], - ["temp-line-yellow", []], - ]); - - graph.init(map, maxTemp); - graph.render(); -}; - -const updateTemperatureGraph = (data) => { - const now = new Date().getTime(); - graph.update("temp-line-blue", [ - now, - data.temperature.chamber.actual, // Original Prusa SL1 uses Chamber for ambient temp - ]); - graph.update("temp-line-orange", [ - now, - data.temperature.tool0 - .actual /* TODO: API collision - Original Prusa SL1 uses - Extruderfor UV LED temp - current API provides only tool0 */, - ]); - graph.update("temp-line-yellow", [ - now, - data.temperature.bed.actual, // Original Prusa SL1 uses Bed for CPU temperature - ]); - graph.render(); -}; - -const updateModule = () => { - if (currentModule && currentModule.update) currentModule.update(context); -}; - -export default sla; diff --git a/src/printer/sla/temperature.js b/src/printer/sla/temperature.js deleted file mode 100644 index fcc8620c..00000000 --- a/src/printer/sla/temperature.js +++ /dev/null @@ -1,13 +0,0 @@ -// This file is part of the Prusa Link Web -// Copyright (C) 2021 Prusa Research a.s. - www.prusa3d.com -// SPDX-License-Identifier: GPL-3.0-or-later - -import * as graph from "../components/temperature_graph"; -import { translate } from "../../locale_provider"; - -const load = () => { - translate("temps.title", { query: "#title-status-label" }); - graph.render(); -}; - -export default { load }; diff --git a/src/printer/fdm/dashboard.js b/src/printer/views/dashboard.js similarity index 92% rename from src/printer/fdm/dashboard.js rename to src/printer/views/dashboard.js index babb5add..ede75f67 100644 --- a/src/printer/fdm/dashboard.js +++ b/src/printer/views/dashboard.js @@ -7,10 +7,7 @@ import upload from "../components/upload"; import cameras from "../components/cameras"; import { translate } from "../../locale_provider"; import * as job from "../components/job"; -import { LinkState } from "../../state"; import { getJson } from "../../auth"; -import fdm from "."; -import { getStatusForTitle } from "../common"; const load = (context) => { translate("home.link", { query: "#title-status-label" }); diff --git a/src/printer/sla/exposure.js b/src/printer/views/exposure.js similarity index 95% rename from src/printer/sla/exposure.js rename to src/printer/views/exposure.js index 86857368..cac743f0 100644 --- a/src/printer/sla/exposure.js +++ b/src/printer/views/exposure.js @@ -82,15 +82,15 @@ const setUpElements = (file, elements, div) => { const template = document.getElementById("exposure-item").content; div.className = "modal-exposure"; for (let expo in config) { - if (expo in file) { + if (expo in file.meta && file.meta[expo] !== undefined) { const elm = document.importNode(template, true); var minus = elm.getElementById("minus"); var plus = elm.getElementById("plus"); elm.getElementById("desc").innerHTML = config[expo].text; const value = elm.getElementById("value"); - value.dataset.value = file[expo].toFixed(0); + value.dataset.value = file.meta[expo].toFixed(0); if (expo == "exposureUserProfile") { - switch(parseInt(file[expo])) { + switch(parseInt(file.meta[expo])) { case 0: value.innerHTML = translate("exp-times.faster"); break; @@ -109,7 +109,7 @@ const setUpElements = (file, elements, div) => { plus = elm.getElementById("next"); minus.style.display = "block"; plus.style.display = "block"; - } else value.innerHTML = (file[expo] / 1000).toFixed(1); + } else value.innerHTML = (file.meta[expo] / 1000).toFixed(1); const [min, max] = config[expo].limit; const setMinus = setValue(expo, value, min, max, -config[expo].step); minus.onclick = setMinus; diff --git a/src/printer/sla/refill.js b/src/printer/views/refill.js similarity index 100% rename from src/printer/sla/refill.js rename to src/printer/views/refill.js diff --git a/src/printer/fdm/temperature.js b/src/printer/views/temperature.js similarity index 100% rename from src/printer/fdm/temperature.js rename to src/printer/views/temperature.js diff --git a/src/state.js b/src/state.js index a080293f..84745ebb 100644 --- a/src/state.js +++ b/src/state.js @@ -6,6 +6,8 @@ export const LinkState = { IDLE: "IDLE", READY: "READY", BUSY: "BUSY", + POUR_IN_RESIN: "POUR IN RESIN", + REFILL: "FEED ME", PRINTING: "PRINTING", PAUSED: "PAUSED", FINISHED: "FINISHED", @@ -23,6 +25,9 @@ export const LinkState = { case "STOPPED": return LinkState.STOPPED; case "ERROR": return LinkState.ERROR; case "ATTENTION": return LinkState.ATTENTION; + // sla specific + case "POUR IN RESIN": return LinkState.POUR_IN_RESIN; + case "FEED ME": return LinkState.REFILL; default: console.error(`Unsupported state: ${linkState}`); return LinkState.UNKNOWN; @@ -33,12 +38,13 @@ export const LinkState = { export const OperationalStates = [ LinkState.IDLE, LinkState.READY, - LinkState.FINISHED + LinkState.FINISHED, ]; export const JobPendingStates = [ LinkState.PRINTING, LinkState.PAUSED, + LinkState.POUR_IN_RESIN, ]; export const translateState = (state) => { @@ -52,6 +58,8 @@ export const translateState = (state) => { case LinkState.STOPPED: return translate("prop.st-stopped"); case LinkState.ERROR: return translate("prop.st-error"); case LinkState.ATTENTION: return translate("prop.st-attention"); + case LinkState.POUR_IN_RESIN: return translate("prop.st-pour-resin"); + case LinkState.REFILL: return translate("prop.st-feedme"); default: console.error(`Unsupported state: ${state}`); return translate("prop.st-unknown"); diff --git a/templates/components/telemetry.html b/templates/components/telemetry.html index 422b8fc6..fd494d3a 100644 --- a/templates/components/telemetry.html +++ b/templates/components/telemetry.html @@ -9,12 +9,12 @@ {% if env.PRINTER_TYPE == "sla" %} {% set telemetry_list = [ - {'text': 'CPU temperature', label: 'prop.temp-cpu', 'icon': 'temperature_color.svg', 'format': 'temp', "locations": ["temperature.bed.actual"], enabled: true }, - {'text': 'UV LED temperature', label: 'prop.temp-led', 'icon': 'temperature_color.svg', 'format': 'temp', "locations": ["temperature.tool0.actual"], enabled: true }, - {'text': 'ambient temperature', label: 'prop.temp-amb', 'icon': 'temperature_color.svg', 'format': 'temp', "locations": ["temperature.chamber.actual"], enabled: true }, - {'text': 'UV LED fan', label: 'prop.fan-led', 'icon': 'fan_color.svg', 'format': 'fan', "location": "telemetry.fanUvLed", enabled: true }, - {'text': 'blower fan', label: 'prop.fan-blower', 'icon': 'fan_color.svg', 'format': 'fan', "location": "telemetry.fanBlower", enabled: true }, - {'text': 'rear fan', label: 'prop.fan-rear', 'icon': 'fan_color.svg', 'format': 'fan', "location": "telemetry.fanRear", enabled: true }, + {'text': 'CPU temperature', label: 'prop.temp-cpu', 'icon': 'temperature_color.svg', 'format': 'temp', "locations": ["telemetry.temperature.cpu.current"], enabled: true }, + {'text': 'UV LED temperature', label: 'prop.temp-led', 'icon': 'temperature_color.svg', 'format': 'temp', "locations": ["telemetry.temperature.uvLED.current"], enabled: true }, + {'text': 'ambient temperature', label: 'prop.temp-amb', 'icon': 'temperature_color.svg', 'format': 'temp', "locations": ["telemetry.temperature.ambient.current"], enabled: true }, + {'text': 'UV LED fan', label: 'prop.fan-led', 'icon': 'fan_color.svg', 'format': 'fan', "location": "telemetry.fan.uvLED", enabled: true }, + {'text': 'blower fan', label: 'prop.fan-blower', 'icon': 'fan_color.svg', 'format': 'fan', "location": "telemetry.fan.blower", enabled: true }, + {'text': 'rear fan', label: 'prop.fan-rear', 'icon': 'fan_color.svg', 'format': 'fan', "location": "telemetry.fan.rear", enabled: true }, {'text': 'cover state', label: 'prop.cover', 'icon': 'cover_color.svg', 'format': 'cover', "location": "telemetry.coverClosed", enabled: true } ] %}