From eb7075710e719021fdd4f6b0c75e710bfeebdfd6 Mon Sep 17 00:00:00 2001 From: Mathis Mensing Date: Thu, 25 Jan 2024 19:52:55 +0100 Subject: [PATCH 01/10] feat(spoolman): initial multi-tool support implementation Signed-off-by: Mathis Mensing --- .../widgets/spoolman/SpoolSelectionDialog.vue | 32 +++++- .../widgets/spoolman/SpoolmanCard.vue | 100 +++++++++++++++++- .../widgets/toolhead/ToolChangeCommands.vue | 24 ++++- src/locales/de.yaml | 5 +- src/locales/en.yaml | 5 +- src/store/spoolman/getters.ts | 6 +- src/store/spoolman/state.ts | 3 +- src/store/spoolman/types.ts | 10 ++ 8 files changed, 167 insertions(+), 18 deletions(-) diff --git a/src/components/widgets/spoolman/SpoolSelectionDialog.vue b/src/components/widgets/spoolman/SpoolSelectionDialog.vue index a350a13664..e2b86f9ea5 100644 --- a/src/components/widgets/spoolman/SpoolSelectionDialog.vue +++ b/src/components/widgets/spoolman/SpoolSelectionDialog.vue @@ -13,7 +13,7 @@ > $changeFilament - {{ $tc('app.spoolman.title.spool_selection') }} + {{ $tc('app.spoolman.title.spool_selection', targetMacro ? 2 : 1, { macro: targetMacro }) }} @@ -175,7 +175,7 @@ {{ filename ? '$printer' : '$send' }} - {{ filename ? $t('app.general.btn.print') : $t('app.spoolman.btn.select') }} + {{ filename ? $t('app.general.btn.print') : $tc('app.spoolman.btn.select', targetMacro ? 2 : 1, { macro: targetMacro }) }} @@ -193,7 +193,7 @@ import { Component, Mixins, Watch } from 'vue-property-decorator' import StateMixin from '@/mixins/state' import { SocketActions } from '@/api/socketActions' -import type { Spool } from '@/store/spoolman/types' +import type { MacroWithSpoolId, Spool } from '@/store/spoolman/types' import BrowserMixin from '@/mixins/browser' import QRReader from '@/components/widgets/spoolman/QRReader.vue' import type { CameraConfig } from '@/store/cameras/types' @@ -219,6 +219,10 @@ export default class SpoolSelectionDialog extends Mixins(StateMixin, BrowserMixi onOpen () { if (this.open) { this.selectedSpoolId = this.$store.state.spoolman.activeSpool ?? null + if (this.targetMacro) { + const macro: MacroWithSpoolId | undefined = this.$store.getters['macros/getMacroByName'](this.targetMacro.toLowerCase()) + this.selectedSpoolId = macro?.variables.spool_id ?? null + } if (this.currentFileName) { // prefetch file metadata @@ -314,6 +318,10 @@ export default class SpoolSelectionDialog extends Mixins(StateMixin, BrowserMixi return this.$store.getters['files/getFile'](filepath ? `gcodes/${filepath}` : 'gcodes', filename) } + get targetMacro (): string | undefined { + return this.$store.state.spoolman.dialog.targetMacro + } + get cameras () { const cameras = this.$store.getters['cameras/getEnabledCameras'] .filter((camera: CameraConfig) => camera.service !== 'iframe') @@ -431,6 +439,23 @@ export default class SpoolSelectionDialog extends Mixins(StateMixin, BrowserMixi } } + if (this.targetMacro) { + // set spool_id via SET_GCODE_VARIABLE + const command = `SET_GCODE_VARIABLE MACRO=${this.targetMacro} VARIABLE=spool_id VALUE=${this.selectedSpool ?? 'None'}` + + /* if (this.$store.getters['printer/getPrinterConfig']('save_variables')) { + // persist selected spool across restarts + // requires persistent gcode_macro variable support in save_variables + // ref: https://github.com/matmen/klipper/tree/feat/persistent-macro-variables + command = `SAVE_VARIABLE MACRO=${this.targetMacro} VARIABLE=SPOOL_ID VALUE=${this.selectedSpool ?? 'None'}` + } */ + + await SocketActions.printerGcodeScript(command) + + this.open = false + return + } + await SocketActions.serverSpoolmanPostSpoolId(this.selectedSpool ?? undefined) if (this.filename) { await SocketActions.printerPrintStart(this.filename) @@ -439,6 +464,7 @@ export default class SpoolSelectionDialog extends Mixins(StateMixin, BrowserMixi this.$router.push({ path: '/' }) } } + this.open = false } diff --git a/src/components/widgets/spoolman/SpoolmanCard.vue b/src/components/widgets/spoolman/SpoolmanCard.vue index fbeba57a47..9c66ea564b 100644 --- a/src/components/widgets/spoolman/SpoolmanCard.vue +++ b/src/components/widgets/spoolman/SpoolmanCard.vue @@ -7,13 +7,84 @@ > $filament @@ -134,8 +205,9 @@ diff --git a/src/components/widgets/toolhead/ToolChangeCommands.vue b/src/components/widgets/toolhead/ToolChangeCommands.vue index 748b3b3668..cfd790919b 100644 --- a/src/components/widgets/toolhead/ToolChangeCommands.vue +++ b/src/components/widgets/toolhead/ToolChangeCommands.vue @@ -22,8 +22,15 @@ v-on="on" @click="sendGcode(macro.name)" > + + $filament + __SPOOL_ID` variable. + +You can use the following macro to restore the previous selection after a restart: +{% raw %} +```yaml +[delayed_gcode RESTORE_SELECTED_SPOOLS] +initial_duration: 0.1 +gcode: + {% set svv = printer.save_variables.variables %} + {% for object in printer %} + {% if object.startswith('gcode_macro ') and printer[object].spool_id is defined %} + {% set macro = object.replace('gcode_macro ', '') %} + {% set var = (macro + '__SPOOL_ID')|lower %} + {% if svv[var] is defined %} + SET_GCODE_VARIABLE MACRO={macro} VARIABLE=spool_id VALUE={svv[var]} + {% endif %} + {% endif %} + {% endfor %} +``` +{% endraw %} From 38248c16a9f63efe4fcbde63bd384b3c027caccc Mon Sep 17 00:00:00 2001 From: Mathis Mensing Date: Fri, 26 Jan 2024 23:51:59 +0100 Subject: [PATCH 04/10] docs(spoolman): use sh lang instead of yml Signed-off-by: Mathis Mensing --- docs/features/spoolman.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/spoolman.md b/docs/features/spoolman.md index 9ac2145abd..7c0e3b948b 100644 --- a/docs/features/spoolman.md +++ b/docs/features/spoolman.md @@ -69,7 +69,7 @@ on spool selection, saving the selected spool to the `__SPOOL_ID` va You can use the following macro to restore the previous selection after a restart: {% raw %} -```yaml +```sh [delayed_gcode RESTORE_SELECTED_SPOOLS] initial_duration: 0.1 gcode: From a6a6273873e4de14daf402a60ebff17eda1fd385 Mon Sep 17 00:00:00 2001 From: Mathis Mensing Date: Sat, 27 Jan 2024 23:15:26 +0100 Subject: [PATCH 05/10] feat(spoolman): update tracked spool when selected tool is active Signed-off-by: Mathis Mensing --- src/components/widgets/spoolman/SpoolSelectionDialog.vue | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/widgets/spoolman/SpoolSelectionDialog.vue b/src/components/widgets/spoolman/SpoolSelectionDialog.vue index ac95b7c80d..f982ad7a8c 100644 --- a/src/components/widgets/spoolman/SpoolSelectionDialog.vue +++ b/src/components/widgets/spoolman/SpoolSelectionDialog.vue @@ -453,6 +453,12 @@ export default class SpoolSelectionDialog extends Mixins(StateMixin, BrowserMixi await SocketActions.printerGcodeScript(commands.join('\n')) + const macro: MacroWithSpoolId | undefined = this.$store.getters['macros/getMacroByName'](this.targetMacro.toLowerCase()) + if (macro?.variables.active) { + // selected tool is active, update active spool + await SocketActions.serverSpoolmanPostSpoolId(this.selectedSpool ?? undefined) + } + this.open = false return } From c5667967758519be1779aeea5956202d8df81c2e Mon Sep 17 00:00:00 2001 From: Mathis Mensing Date: Sun, 28 Jan 2024 14:54:29 +0100 Subject: [PATCH 06/10] fix(spoolman): remove leftover event log Signed-off-by: Mathis Mensing --- src/store/spoolman/actions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/store/spoolman/actions.ts b/src/store/spoolman/actions.ts index 1545f46181..470a736403 100644 --- a/src/store/spoolman/actions.ts +++ b/src/store/spoolman/actions.ts @@ -158,7 +158,6 @@ export const actions: ActionTree = { return } - consola.debug(`${logPrefix} received spoolman message:`, data) switch (data.resource) { case 'spool': dispatch('onSpoolChange', data) From 31bfdbade3de90b7f06316103eb7afd6560404d9 Mon Sep 17 00:00:00 2001 From: Mathis Mensing Date: Mon, 29 Jan 2024 18:48:06 +0100 Subject: [PATCH 07/10] fix: disable select spool button when disconnected Signed-off-by: Mathis Mensing --- src/components/widgets/spoolman/SpoolmanCard.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/widgets/spoolman/SpoolmanCard.vue b/src/components/widgets/spoolman/SpoolmanCard.vue index 9c66ea564b..26a5da45fb 100644 --- a/src/components/widgets/spoolman/SpoolmanCard.vue +++ b/src/components/widgets/spoolman/SpoolmanCard.vue @@ -29,6 +29,7 @@ v-bind="attrs" small class="ms-1 my-1" + :disabled="!isConnected" v-on="on" > {{ $t('app.spoolman.label.change_spool') }} From 8b349c1a0b3114da7247a9d2ec175c681ea96e2e Mon Sep 17 00:00:00 2001 From: Mathis Mensing Date: Mon, 29 Jan 2024 19:21:59 +0100 Subject: [PATCH 08/10] fix: sort macros by full name Signed-off-by: Mathis Mensing --- src/components/widgets/spoolman/SpoolmanCard.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/widgets/spoolman/SpoolmanCard.vue b/src/components/widgets/spoolman/SpoolmanCard.vue index 26a5da45fb..f8300f3661 100644 --- a/src/components/widgets/spoolman/SpoolmanCard.vue +++ b/src/components/widgets/spoolman/SpoolmanCard.vue @@ -233,10 +233,11 @@ export default class SpoolmanCard extends Mixins(StateMixin) { } get targetableMacros (): MacroWithSpoolId[] { + const collator = new Intl.Collator() return this.$store.getters['macros/getMacros'] - .filter((macro: Macro) => 'spool_id' in (macro.variables ?? {})) + .filter((macro: Macro) => macro.variables && 'spool_id' in macro.variables) .map((macro: Macro) => ({ ...macro, name: macro.name.toUpperCase() })) - .sort((a: Macro, b: Macro) => parseInt(a.name.substring(1)) - parseInt(b.name.substring(1))) + .sort((a: Macro, b: Macro) => collator.compare(a.name, b.name)) } getSpoolById (id: number): Spool | undefined { From f72e1e57735afaf40c5dab6de4a9caf2500927e7 Mon Sep 17 00:00:00 2001 From: Mathis Mensing Date: Tue, 30 Jan 2024 21:48:29 +0100 Subject: [PATCH 09/10] feat(spoolman): add stroke around filament icon Signed-off-by: Mathis Mensing --- src/components/widgets/spoolman/SpoolSelectionDialog.vue | 4 ++-- src/components/widgets/spoolman/SpoolmanCard.vue | 3 +++ src/components/widgets/toolhead/ToolChangeCommands.vue | 2 +- src/scss/misc.scss | 9 +++++++++ 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/components/widgets/spoolman/SpoolSelectionDialog.vue b/src/components/widgets/spoolman/SpoolSelectionDialog.vue index f982ad7a8c..fdcab17718 100644 --- a/src/components/widgets/spoolman/SpoolSelectionDialog.vue +++ b/src/components/widgets/spoolman/SpoolSelectionDialog.vue @@ -112,8 +112,8 @@
{{ item.id === selectedSpool ? '$markedCircle' : '$filament' }} diff --git a/src/components/widgets/spoolman/SpoolmanCard.vue b/src/components/widgets/spoolman/SpoolmanCard.vue index f8300f3661..f7b22e9b9c 100644 --- a/src/components/widgets/spoolman/SpoolmanCard.vue +++ b/src/components/widgets/spoolman/SpoolmanCard.vue @@ -55,6 +55,7 @@ > $filament @@ -78,6 +79,7 @@ > $filament @@ -181,6 +183,7 @@ v-if="activeSpool" :color="getSpoolColor(activeSpool)" size="110px" + class="spool-icon" > $filament diff --git a/src/components/widgets/toolhead/ToolChangeCommands.vue b/src/components/widgets/toolhead/ToolChangeCommands.vue index cfd790919b..48175394c3 100644 --- a/src/components/widgets/toolhead/ToolChangeCommands.vue +++ b/src/components/widgets/toolhead/ToolChangeCommands.vue @@ -24,7 +24,7 @@ > $filament diff --git a/src/scss/misc.scss b/src/scss/misc.scss index 7045e5b9cf..a095ef501e 100644 --- a/src/scss/misc.scss +++ b/src/scss/misc.scss @@ -63,3 +63,12 @@ input[type=number] { .constrained-width.quad { max-width: map-get($container-max-widths, 'xl') * 2; } + +.spool-icon { + stroke: rgba(0,0,0, 0.25); + stroke-width: .025rem; +} + +.theme--dark .spool-icon { + stroke: rgba(0,0,0, 0.5); +} From 6123fa94221dd46ce50509aaf243f46254f6504f Mon Sep 17 00:00:00 2001 From: Mathis Mensing Date: Wed, 31 Jan 2024 20:26:04 +0100 Subject: [PATCH 10/10] Update src/components/widgets/spoolman/SpoolmanCard.vue Co-authored-by: Pedro Lamas Signed-off-by: Mathis Mensing --- src/components/widgets/spoolman/SpoolmanCard.vue | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/widgets/spoolman/SpoolmanCard.vue b/src/components/widgets/spoolman/SpoolmanCard.vue index f7b22e9b9c..fc1b394dc1 100644 --- a/src/components/widgets/spoolman/SpoolmanCard.vue +++ b/src/components/widgets/spoolman/SpoolmanCard.vue @@ -236,11 +236,15 @@ export default class SpoolmanCard extends Mixins(StateMixin) { } get targetableMacros (): MacroWithSpoolId[] { - const collator = new Intl.Collator() - return this.$store.getters['macros/getMacros'] - .filter((macro: Macro) => macro.variables && 'spool_id' in macro.variables) - .map((macro: Macro) => ({ ...macro, name: macro.name.toUpperCase() })) - .sort((a: Macro, b: Macro) => collator.compare(a.name, b.name)) + const macros = this.$store.getters['macros/getMacros'] as Macro[] + + return macros + .filter((macro): macro is MacroWithSpoolId => macro.variables != null && 'spool_id' in macro.variables) + .map(macro => ({ + ...macro, + name: macro.name.toUpperCase() + })) + .sort((a, b) => a.name.localeCompare(b.name)) } getSpoolById (id: number): Spool | undefined {