Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implementation of MMU panel to support single-extruder-multi-material printing using the Happy Hare for any MMU/AFC. #2158

Open
wants to merge 32 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
350bc19
[Automated] Merged develop into target master
meteyou Dec 10, 2022
eac516e
[Automated] Merged develop into target master
meteyou Mar 12, 2023
3584c03
[Automated] Merged develop into target master
meteyou Apr 2, 2023
c6a3bbc
[Automated] Merged develop into target master
meteyou Jun 19, 2023
c07606a
[Automated] Merged develop into target master
meteyou Jun 19, 2023
4d241fc
[Automated] Merged develop into target master
meteyou Jul 24, 2023
1b763e2
[Automated] Merged develop into target master
meteyou Jul 30, 2023
c9dc251
[Automated] Merged develop into target master
meteyou Aug 12, 2023
54ce2e0
[Automated] Merged develop into target master
meteyou Aug 16, 2023
6884326
[Automated] Merged develop into target master
meteyou Oct 7, 2023
d3845f9
[Automated] Merged develop into target master
meteyou Oct 7, 2023
3ba23df
[Automated] Merged develop into target master
meteyou Dec 16, 2023
df7dc71
[Automated] Merged develop into target master
meteyou Dec 31, 2023
cea98a0
[Automated] Merged develop into target master
meteyou Feb 15, 2024
cc5e97a
[Automated] Merged develop into target master
meteyou Apr 28, 2024
c6c71ec
[Automated] Merged develop into target master
meteyou May 1, 2024
e639e08
[Automated] Merged develop into target master
meteyou May 4, 2024
4b44d44
[Automated] Merged develop into target master
meteyou Jul 14, 2024
2a20ad2
[Automated] Merged develop into target master
meteyou Dec 4, 2024
8b8b000
[Automated] Merged develop into target master
meteyou Dec 7, 2024
9ba039e
[Automated] Merged develop into target master
meteyou Dec 25, 2024
282c7b1
Initial implementation of MMU panel to support single-extruder-multi-…
meteyou Dec 25, 2024
f7e497e
Packaging for PR
moggieuk Feb 28, 2025
720ebfb
Fixed linter errors
moggieuk Mar 1, 2025
de25ec9
prettifier code formatting changes
moggieuk Mar 1, 2025
896d9a2
Merge remote-tracking branch 'upstream/develop' into develop
moggieuk Mar 1, 2025
3236efe
Fix linter requirements
moggieuk Mar 1, 2025
ceed390
Fixed prettifer corruption and sensor visability
moggieuk Mar 1, 2025
1334560
stop fighting prettifier. :-)
moggieuk Mar 1, 2025
40c61a3
Removed non existent import
moggieuk Mar 1, 2025
d2d7ef5
Fixed more linter warnings
moggieuk Mar 1, 2025
638a001
last two linter warnings
moggieuk Mar 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
682 changes: 682 additions & 0 deletions src/components/dialogs/MmuEditGateMapDialog.vue

Large diffs are not rendered by default.

616 changes: 616 additions & 0 deletions src/components/dialogs/MmuEditTtgMapDialog.vue

Large diffs are not rendered by default.

68 changes: 68 additions & 0 deletions src/components/dialogs/MmuGateDialogRow.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<template>
<tr
:class="rowClass"
@click="$emit('select-gate', details.index)"
@mouseover="$emit('mouseover', details.index)"
@mouseleave="$emit('mouseleave', details.index)">
<td>{{ details.index }}</td>
<td class="pr-0 py-2">
<mmu-spool
:gate-index="details.index"
:show-percent="false"
style="height: 60px; float: left"
class="mr-0" />
</td>
<td class="py-0" style="min-width: 274px; max-width: 274px">
<mmu-gate-summary :gate-index="details.index" :show-details="true" :show-gate="false" :compact="true" />
</td>
<td>
<span
class="es-group-icon"
:style="{ background: details.endlessSpoolGroup === selectedEsGroup ? 'limegreen' : 'none' }" />
</td>
</tr>
</template>

<script lang="ts">
import Component from 'vue-class-component'
import { Mixins, Prop } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import MmuMixin from '@/components/mixins/mmu'
@Component({})
export default class MmuGateDialogRow extends Mixins(BaseMixin, MmuMixin) {
@Prop({ required: true }) declare readonly details: MmuGateDetails
@Prop({ required: true }) declare readonly selectedGate: number | null
@Prop({ required: true }) declare readonly selectedEsGroup: number | null
get rowClass(): string[] {
let classes = ['cursor-pointer']
if (this.details.index === this.selectedGate) classes.push('selected-row')
if (this.details.status === this.GATE_EMPTY) classes.push('disabled-row')
return classes
}
}
</script>

<style scoped>
.v-data-table__table {
table-layout: fixed;
}
.selected-row {
background: #595959;
}
.disabled-row {
opacity: 0.7;
}
.es-group-icon {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 25%;
border: 1px solid var(--v-secondary-lighten3);
vertical-align: middle;
}
</style>
504 changes: 504 additions & 0 deletions src/components/dialogs/MmuMaintenanceDialog.vue

Large diffs are not rendered by default.

292 changes: 292 additions & 0 deletions src/components/dialogs/MmuRecoverStateDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
<template>
<v-dialog v-model="showDialog" width="600" persistent :fullscreen="isMobile">
<panel
:title="$t('Panels.MmuPanel.RecoverState')"
:icon="mdiCogRefresh"
card-class="mmu-recover-state-dialog"
:margin-bottom="false">
<template #buttons>
<v-btn icon tile @click="close">
<v-icon>{{ mdiCloseThick }}</v-icon>
</v-btn>
</template>

<v-card-subtitle>
{{ $t('Panels.MmuPanel.MmuRecoverDialog.Intro') }}
</v-card-subtitle>

<v-card-text>
<v-row class="fixed-height">
<v-col class="col-1" />
<v-col class="col-5 d-flex justify-center">
<v-row class="d-flex flex-row">
<v-col class="d-flex justify-center flex-column">
<span class="settings-row-title">Tool</span>
<span class="settings-row-subtitle">
{{ $t('Panels.MmuPanel.MmuRecoverDialog.SetTool') }}
</span>
</v-col>
</v-row>
</v-col>
<v-col class="col-5 d-flex justify-end align-center">
<v-select
v-model="selectedTool"
:items="toolsList"
:error-messages="toolErrorMessage"
outlined
dense />
</v-col>
</v-row>
<v-divider class="my-2"></v-divider>
<v-row class="fixed-height">
<v-col class="col-1" />
<v-col class="col-5 d-flex justify-center">
<v-row class="d-flex flex-row">
<v-col class="d-flex justify-center flex-column">
<span class="settings-row-title">Gate</span>
<span class="settings-row-subtitle">
{{ $t('Panels.MmuPanel.MmuRecoverDialog.SetGate') }}
</span>
</v-col>
</v-row>
</v-col>
<v-col class="col-5 d-flex justify-end align-center">
<v-select
v-model="selectedGate"
:items="gatesList"
:error-messages="gateErrorMessage"
outlined
dense />
</v-col>
</v-row>
<v-divider class="my-2"></v-divider>
<v-row class="fixed-height">
<v-col class="col-1" />
<v-col class="col-5 d-flex justify-center">
<v-row class="d-flex flex-row">
<v-col class="d-flex justify-center flex-column">
<span class="settings-row-title">Filament Position</span>
<span class="settings-row-subtitle">
{{ $t('Panels.MmuPanel.MmuRecoverDialog.FilamentLoaded') }}
</span>
</v-col>
</v-row>
</v-col>
<v-col class="col-5 d-flex justify-end align-center">
<v-select
v-model="selectedPos"
:items="posList"
:error-messages="posErrorMessage"
outlined
dense />
</v-col>
</v-row>
</v-card-text>

<v-card-actions>
<v-spacer />
<v-btn text @click="close">{{ $t('Panels.MmuPanel.Cancel') }}</v-btn>
<v-btn color="primary" :disabled="okDisabled" text @click="commit">
{{ $t('Panels.MmuPanel.Ok') }}
</v-btn>
</v-card-actions>
</panel>
</v-dialog>
</template>

<script lang="ts">
import Component from 'vue-class-component'
import { Mixins, Prop, Watch } from 'vue-property-decorator'
import BaseMixin from '@/components/mixins/base'
import MmuMixin from '@/components/mixins/mmu'
import Panel from '@/components/ui/Panel.vue'
import { mdiCloseThick, mdiCogRefresh } from '@mdi/js'
@Component({
components: { Panel },
})
export default class MmuRecoverDialog extends Mixins(BaseMixin, MmuMixin) {
mdiCloseThick = mdiCloseThick
mdiCogRefresh = mdiCogRefresh
@Prop({ required: true }) readonly showDialog!: boolean
private localGate: number = -1
private localTool: number = -1
private localFilamentPos: number = -1
@Watch('showDialog')
onShowDialogChanged(newValue: boolean): void {
if (newValue) {
this.localGate = this.gate
this.localTool = this.tool
this.localFilamentPos = this.filamentPos
}
}
get selectedTool(): string {
if (this.localTool === this.TOOL_GATE_UNKNOWN) {
return 'Unknown'
} else if (this.localTool === this.TOOL_GATE_BYPASS) {
return 'Bypass'
}
return `T${this.localTool}`
}
set selectedTool(newTool: string): void {
const index = this.toolsList.findIndex((item) => item === newTool)
if (index == this.numGates) {
this.localTool = this.TOOL_GATE_BYPASS
} else {
this.localTool = index
}
}
get toolsList(): string[] {
const tools: string[] = []
for (let i = 0; i < this.numGates; i++) {
tools.push(`T${i}`)
}
if (this.hasBypass) tools.push('Bypass')
return tools
}
get toolErrorMessage(): string {
if (this.localTool === this.TOOL_GATE_UNKNOWN) {
return this.$t('Panels.MmuPanel.MmuRecoverDialog.NoTool')
} else if (this.localGate === this.TOOL_GATE_BYPASS && this.localTool !== this.TOOL_GATE_BYPASS) {
return this.$t('Panels.MmuPanel.MmuRecoverDialog.GateBypass')
}
return ''
}
get selectedGate(): string {
if (this.localGate === this.TOOL_GATE_UNKNOWN) {
return 'Unknown'
} else if (this.localGate === this.TOOL_GATE_BYPASS) {
return 'Bypass'
}
return `${this.gateIndexText(this.localGate)}`
}
set selectedGate(newGate: string): void {
const index = this.gatesList.findIndex((item) => item === newGate)
if (index == this.numGates) {
this.localGate = this.TOOL_GATE_BYPASS
} else {
this.localGate = index
}
}
get gatesList(): string[] {
const gates: string[] = []
for (let gate = 0; gate < this.numGates; gate++) {
gates.push(this.gateIndexText(gate))
}
if (this.hasBypass) gates.push('Bypass')
return gates
}
get gateErrorMessage(): string {
if (this.localGate === this.TOOL_GATE_UNKNOWN) {
return this.$t('Panels.MmuPanel.MmuRecoverDialog.NoGate')
} else if (this.localTool === this.TOOL_GATE_BYPASS && this.localGate !== this.TOOL_GATE_BYPASS) {
return this.$t('Panels.MmuPanel.MmuRecoverDialog.ToolBypass')
} else if (this.localGate >= 0 && this.ttgMap[this.localGate] !== this.localTool) {
const msg = this.$t('Panels.MmuPanel.MmuRecoverDialog.Remap', { tool: `T${this.localTool}` })
return `${this.$t('Panels.MmuPanel.MmuRecoverDialog.WarningPrefix')} ${msg}`
}
return ''
}
private gateIndexText(gateIndex: number): string {
const num_units = this.$store.state.printer?.mmu_machine?.num_units
if (num_units > 1) {
for (let i = 0; i < num_units; i++) {
const unitRef = `unit_${i}`
const unit = this.$store.state.printer?.mmu_machine?.[unitRef]
if (i > 0 && gateIndex >= unit.first_gate && gateIndex < unit.first_gate + unit.num_gates) {
return `${gateIndex} (unit #${i + 1})`
}
}
}
return `${gateIndex}`
}
get selectedPos(): string {
if (this.localFilamentPos === this.FILAMENT_POS_UNLOADED) {
return 'UNLOADED'
} else if (this.localFilamentPos === this.FILAMENT_POS_LOADED) {
return 'LOADED'
}
return 'UNKNOWN'
}
set selectedPos(newPos: string): void {
if (newPos === 'UNLOADED') {
this.localFilamentPos = this.FILAMENT_POS_UNLOADED
} else if (newPos === 'LOADED') {
this.localFilamentPos = this.FILAMENT_POS_LOADED
}
this.localFilamentPos = this.FILAMENT_POS_UNKNOWN
}
get posList(): string[] {
return ['UNKNOWN', 'UNLOADED', 'LOADED']
}
get posErrorMessage(): string {
if (this.localFilamentPos === this.FILAMENT_POS_UNKNOWN) {
return `${this.$t('Panels.MmuPanel.MmuRecoverDialog.WarningPrefix')} ${this.$t('Panels.MmuPanel.MmuRecoverDialog.NoPosition')}`
}
return ''
}
get okDisabled(): boolean {
let tError =
this.toolErrorMessage !== '' &&
!this.toolErrorMessage.startsWith(this.$t('Panels.MmuPanel.MmuRecoverDialog.WarningPrefix'))
let gError =
this.gateErrorMessage !== '' &&
!this.gateErrorMessage.startsWith(this.$t('Panels.MmuPanel.MmuRecoverDialog.WarningPrefix'))
let pError =
this.posErrorMessage !== '' &&
!this.posErrorMessage.startsWith(this.$t('Panels.MmuPanel.MmuRecoverDialog.WarningPrefix'))
return tError || gError || pError
}
close() {
this.$emit('close')
}
commit() {
let cmd = `MMU_RECOVER TOOL=${this.localTool} GATE=${this.localGate}`
if (this.localFilamentPos === this.FILAMENT_POS_UNLOADED) {
cmd += ' LOADED=0'
} else if (this.localFilamentPos === this.FILAMENT_POS_LOADED) {
cmd += ' LOADED=1'
}
this.doSend(cmd)
this.close()
}
}
</script>

<style scoped>
.settings-row-title {
display: block;
width: 100%;
font-weight: bold;
}
.settings-row-subtitle {
display: block;
font-size: 0.8em;
line-height: 1.3;
margin-top: 3px;
}
.fixed-height {
min-height: 100px;
}
</style>
12 changes: 11 additions & 1 deletion src/components/dialogs/SpoolmanChangeSpoolDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
<div>
<v-dialog v-model="showDialog" width="800" persistent :fullscreen="isMobile">
<panel
:title="$t('Panels.SpoolmanPanel.ChangeSpool')"
:title="
setActiveSpool ? $t('Panels.SpoolmanPanel.ChangeSpool') : $t('Panels.SpoolmanPanel.SelectSpool')
"
:icon="mdiAdjust"
card-class="spoolman-change-spool-dialog"
:margin-bottom="false">
Expand Down Expand Up @@ -85,6 +87,7 @@ export default class SpoolmanChangeSpoolDialog extends Mixins(BaseMixin) {

@Prop({ required: true }) declare readonly showDialog: boolean
@Prop({ required: false, default: null }) declare readonly tool?: string
@Prop({ required: false, default: true }) declare readonly setActiveSpool?: boolean

search = ''

Expand Down Expand Up @@ -188,6 +191,13 @@ export default class SpoolmanChangeSpoolDialog extends Mixins(BaseMixin) {
}

setSpool(spool: ServerSpoolmanStateSpool) {
// If dialog is used for selection only, bypass setting of active spool and propogate event
if (!this.setActiveSpool) {
this.$emit('select-spool', spool)
this.close()
return
}

this.$store.dispatch('server/spoolman/setActiveSpool', spool.id)

// Close the dialog if no tool is selected
Expand Down
Loading