Skip to content

Commit

Permalink
feat(spoolman): live update support (#1301)
Browse files Browse the repository at this point in the history
Signed-off-by: Mathis Mensing <[email protected]>
Co-authored-by: Pedro Lamas <[email protected]>
  • Loading branch information
matmen and pedrolamas authored Jan 16, 2024
1 parent 8726577 commit c6bcff8
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 7 deletions.
4 changes: 0 additions & 4 deletions src/components/widgets/spoolman/SpoolmanCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,6 @@ export default class SpoolmanCard extends Mixins(StateMixin) {
this.$store.commit('spoolman/setDialogState', { show: true })
}
get supportsSpoolman () {
return this.$store.getters['spoolman/getSupported']
}
get activeSpool (): Spool | null {
return this.$store.getters['spoolman/getActiveSpool']
}
Expand Down
142 changes: 140 additions & 2 deletions src/store/spoolman/actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import type { ActionTree } from 'vuex'
import type { SpoolmanState } from './types'
import type {
Spool,
SpoolmanState,
WebsocketBasePayload,
WebsocketFilamentPayload,
WebsocketSpoolPayload,
WebsocketVendorPayload
} from './types'
import type { RootState } from '../types'
import { SocketActions } from '@/api/socketActions'
import { consola } from 'consola'

const logPrefix = '[SPOOLMAN]'

export const actions: ActionTree<SpoolmanState, RootState> = {
/**
Expand All @@ -23,7 +33,135 @@ export const actions: ActionTree<SpoolmanState, RootState> = {
commit('setActiveSpool', payload.spool_id)
},

async onAvailableSpools ({ commit }, payload) {
async onSpoolChange ({ commit, getters }, { type, payload }: WebsocketSpoolPayload) {
const spools = [...getters.getAvailableSpools as Spool[]]

switch (type) {
case 'added': {
spools.push(payload)

break
}

case 'updated': {
const index = spools.findIndex(spool => spool.id === payload.id)

if (index >= 0) {
spools[index] = payload
}

break
}

case 'deleted': {
const index = spools.findIndex(spool => spool.id === payload.id)

if (index >= 0) {
spools.splice(index, 1)
}

break
}
}

commit('setAvailableSpools', spools)
},

async onFilamentChange ({ commit, getters }, { type, payload }: WebsocketFilamentPayload) {
if (type !== 'updated') {
// we only care about updated filament types
return
}

const spools = [...getters.getAvailableSpools]
for (const spool of spools) {
if (spool.filament.id === payload.id) {
spools[spools.indexOf(spool)] = {
...spool,
filament: payload
}
}
}

commit('setAvailableSpools', spools)
},

async onVendorChange ({ commit, getters }, { type, payload }: WebsocketVendorPayload) {
if (type !== 'updated') {
// we only care about updated vendors
return
}

const spools = [...getters.getAvailableSpools]
for (const spool of spools) {
if (spool.filament.vendor?.id === payload.id) {
spools[spools.indexOf(spool)] = {
...spool,
filament: {
...spool.filament,
vendor: payload
}
}
}
}

commit('setAvailableSpools', spools)
},

async onAvailableSpools ({ commit, getters, dispatch }, payload) {
commit('setAvailableSpools', [...payload])
if (getters.getSupported) { dispatch('initializeWebsocketConnection') }
},

async initializeWebsocketConnection ({ state, rootState, dispatch }) {
if (rootState.server.config.spoolman?.server) {
if (state.socket?.readyState === WebSocket.OPEN) {
// we already have a working WS conn
return
}

// init websocket to listen for updates
const spoolmanUrl = new URL(rootState.server.config.spoolman.server)
spoolmanUrl.pathname += `${spoolmanUrl.pathname.endsWith('/') ? '' : '/'}api/v1/`
if (spoolmanUrl.protocol === 'https:') {
spoolmanUrl.protocol = 'wss:'
} else {
spoolmanUrl.protocol = 'ws:'
}

state.socket = new WebSocket(spoolmanUrl)
state.socket.onerror = err => consola.warn(`${logPrefix} received websocket error`, err)
state.socket.onmessage = event => {
let data
try {
data = JSON.parse(event.data) as WebsocketBasePayload
} catch (err) {
consola.error(`${logPrefix} failed to decode websocket message`, err, event.data)
return
}

consola.debug(`${logPrefix} received spoolman message:`, data)
switch (data.resource) {
case 'spool':
dispatch('onSpoolChange', data)
break

case 'filament':
dispatch('onFilamentChange', data)
break

case 'vendor':
dispatch('onVendorChange', data)
break

default:
consola.warn(`${logPrefix} ignoring websocket message with type ${data.resource}`)
}
}
} else {
// destroy ws
state.socket?.close()
state.socket = undefined
}
}
}
6 changes: 5 additions & 1 deletion src/store/spoolman/mutations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { MutationTree } from 'vuex'
import { defaultState } from './state'
import type { Spool, SpoolmanState, SpoolSelectionDialogState } from '@/store/spoolman/types'
import type {
Spool,
SpoolmanState,
SpoolSelectionDialogState
} from '@/store/spoolman/types'

export const mutations: MutationTree<SpoolmanState> = {
/**
Expand Down
23 changes: 23 additions & 0 deletions src/store/spoolman/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,32 @@ export interface SpoolmanState {
activeSpool?: number;
supported: boolean;
dialog: SpoolSelectionDialogState;
socket?: WebSocket;
}

export interface SpoolSelectionDialogState {
show: boolean;
filename?: string;
}

export interface WebsocketBasePayload {
type: 'added' | 'updated' | 'deleted';
resource: string;
date: string;
payload: Record<string, any>;
}

export interface WebsocketSpoolPayload extends WebsocketBasePayload {
resource: 'spool';
payload: Spool;
}

export interface WebsocketFilamentPayload extends WebsocketBasePayload {
resource: 'filament';
payload: Filament;
}

export interface WebsocketVendorPayload extends WebsocketBasePayload {
resource: 'vendor';
payload: Vendor;
}

0 comments on commit c6bcff8

Please sign in to comment.