Skip to content

Commit

Permalink
feat: adds Moonraker Job Queue Support (#448)
Browse files Browse the repository at this point in the history
Signed-off-by: Brandon Nance <[email protected]>
Co-authored-by: Pedro Lamas <[email protected]>
  • Loading branch information
bwnance and pedrolamas authored Dec 30, 2022
1 parent c03c2b8 commit 6cd1227
Show file tree
Hide file tree
Showing 28 changed files with 780 additions and 14 deletions.
38 changes: 32 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"qrcode.vue": "^1.7.0",
"register-service-worker": "^1.7.2",
"semver": "^7.3.8",
"sortablejs": "^1.15.0",
"uuid": "^9.0.0",
"vue": "^2.7.14",
"vue-class-component": "^7.2.6",
Expand All @@ -77,6 +78,7 @@
"@types/md5": "^2.3.2",
"@types/node": "^18.11.17",
"@types/semver": "^7.3.13",
"@types/sortablejs": "^1.15.0",
"@types/uuid": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^5.46.1",
"@typescript-eslint/parser": "^5.46.1",
Expand Down
54 changes: 54 additions & 0 deletions src/api/socketActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,60 @@ export const SocketActions = {
)
},

async serverJobQueueStatus () {
baseEmit(
'server.job_queue.status', {
dispatch: 'jobQueue/onJobQueueStatus',
wait: Waits.onJobQueue
}
)
},

async serverJobQueuePostJob (filenames: string[], reset?: boolean) {
baseEmit(
'server.job_queue.post_job', {
dispatch: 'jobQueue/onJobQueueStatus',
params: {
filenames,
reset
},
wait: Waits.onJobQueue
}
)
},

async serverJobQueueDeleteJobs (jobIds: string[]) {
const params = jobIds.length > 0 && jobIds[0] === 'all'
? { all: true }
: { job_ids: jobIds }

baseEmit(
'server.job_queue.delete_job', {
dispatch: 'jobQueue/onJobQueueStatus',
params,
wait: Waits.onJobQueue
}
)
},

async serverJobQueuePause () {
baseEmit(
'server.job_queue.pause', {
dispatch: 'jobQueue/onJobQueueStatus',
wait: Waits.onJobQueue
}
)
},

async serverJobQueueStart () {
baseEmit(
'server.job_queue.start', {
dispatch: 'jobQueue/onJobQueueStatus',
wait: Waits.onJobQueue
}
)
},

/**
* Loads the metadata for a given filepath.
* Expects the full path including root.
Expand Down
7 changes: 7 additions & 0 deletions src/components/widgets/filesystem/FileSystem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
@preheat="handlePreheat"
@preview-gcode="handlePreviewGcode"
@view-thumbnail="handleViewThumbnail"
@enqueue="handleEnqueue"
/>

<file-editor-dialog
Expand Down Expand Up @@ -875,6 +876,12 @@ export default class FileSystem extends Mixins(StateMixin, FilesMixin, ServicesM
}
}
handleEnqueue (file: AppFileWithMeta) {
if (this.disabled) return
const filepath = (file.path) ? `${file.path}/${file.filename}` : `${file.filename}`
SocketActions.serverJobQueuePostJob([filepath])
}
/**
* ===========================================================================
* Drag handling.
Expand Down
14 changes: 14 additions & 0 deletions src/components/widgets/filesystem/FileSystemContextMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@
</v-list-item-icon>
<v-list-item-title>{{ $t('app.general.btn.print') }}</v-list-item-title>
</v-list-item>
<v-list-item
v-if="canPrint && supportsJobQueue"
link
@click="$emit('enqueue', file)"
>
<v-list-item-icon>
<v-icon>$enqueueJob</v-icon>
</v-list-item-icon>
<v-list-item-title>{{ $t("app.general.btn.add_to_queue") }}</v-list-item-title>
</v-list-item>
<v-list-item
v-if="canPreheat"
link
Expand Down Expand Up @@ -182,5 +192,9 @@ export default class FileSystemContextMenu extends Mixins(StateMixin, FilesMixin
const layoutName = this.$store.getters['layout/getSpecificLayoutName']
return (this.$store.getters['layout/isEnabledInLayout'](layoutName, 'gcode-preview-card') && this.root === 'gcodes')
}
get supportsJobQueue (): boolean {
return this.$store.getters['server/componentSupport']('job_queue')
}
}
</script>
118 changes: 118 additions & 0 deletions src/components/widgets/job-queue/JobQueue.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<template>
<div>
<job-queue-toolbar
v-if="selected.length === 0"
:headers="headers"
@remove-all="handleRemoveAll"
@refresh="handleRefresh"
/>

<job-queue-bulk-actions
v-else
@remove="handleRemove(selected)"
/>

<job-queue-browser
v-model="selected"
:headers="visibleHeaders"
:dense="dense"
:bulk-actions="bulkActions"
@row-click="handleRowClick"
/>

<job-queue-context-menu
v-if="contextMenuState.open"
v-model="contextMenuState.open"
:job="contextMenuState.job"
:position-x="contextMenuState.x"
:position-y="contextMenuState.y"
@remove="handleRemove"
/>
</div>
</template>

<script lang="ts">
import { SocketActions } from '@/api/socketActions'
import { QueuedJob } from '@/store/jobQueue/types'
import { Component, Prop, Vue } from 'vue-property-decorator'
import JobQueueToolbar from './JobQueueToolbar.vue'
import JobQueueBulkActions from './JobQueueBulkActions.vue'
import JobQueueBrowser from './JobQueueBrowser.vue'
import JobQueueContextMenu from './JobQueueContextMenu.vue'
import { AppTableHeader } from '@/types'
@Component({
components: {
JobQueueToolbar,
JobQueueBulkActions,
JobQueueBrowser,
JobQueueContextMenu
}
})
export default class JobQueue extends Vue {
contextMenuState: any = {
open: false,
x: 0,
y: 0,
job: null
}
selected: QueuedJob[] = []
@Prop({ type: Boolean, default: false })
readonly dense!: boolean
@Prop({ type: Boolean, default: false })
readonly bulkActions!: boolean
get headers (): AppTableHeader[] {
const headers = [
{ text: '', value: 'handle', sortable: false, width: '24px' },
{ text: this.$tc('app.general.table.header.name'), value: 'filename', sortable: false },
{ text: this.$tc('app.general.table.header.time_added'), value: 'time_added', configurable: true, sortable: false },
{ text: this.$tc('app.general.table.header.time_in_queue'), value: 'time_in_queue', configurable: true, sortable: false }
]
const key = 'job_queue'
return this.$store.getters['config/getMergedTableHeaders'](headers, key)
}
get visibleHeaders (): AppTableHeader[] {
return this.headers.filter(header => header.visible || header.visible === undefined)
}
handleRowClick (item: QueuedJob, e: MouseEvent) {
if (!this.contextMenuState.open) {
// Open the context menu
this.contextMenuState.x = e.clientX
this.contextMenuState.y = e.clientY
this.contextMenuState.job = item
this.$nextTick(() => {
this.contextMenuState.open = true
})
}
}
async handleRemoveAll () {
const res = await this.$confirm(
this.$tc('app.job_queue.msg.confirm'),
{ title: this.$tc('app.general.label.confirm'), color: 'card-heading', icon: '$error' }
)
if (res) {
SocketActions.serverJobQueueDeleteJobs(['all'])
}
}
handleRefresh () {
SocketActions.serverJobQueueStatus()
}
handleRemove (jobs: QueuedJob | QueuedJob[]) {
const jobIds = Array.isArray(jobs)
? jobs.map(job => job.job_id)
: [jobs.job_id]
SocketActions.serverJobQueueDeleteJobs(jobIds)
}
}
</script>
Loading

0 comments on commit 6cd1227

Please sign in to comment.