From f999e6b202813eb45978b5853fee1082a24f7f2e Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Fri, 17 May 2024 22:52:14 +0200 Subject: [PATCH 1/4] feat: add support for moonraker sensor history_fields Signed-off-by: Stefan Dej --- .../dialogs/HistoryDetailsDialog.vue | 170 ------------- .../dialogs/HistoryListPanelDetailsDialog.vue | 16 ++ .../panels/History/HistoryListEntryJob.vue | 5 + .../panels/History/HistoryListRow.vue | 226 ------------------ .../panels/History/HistoryListRowCell.vue | 61 ----- src/components/panels/HistoryListPanel.vue | 36 +++ src/store/server/history/types.ts | 9 + src/store/server/index.ts | 2 + src/store/server/sensor/actions.ts | 26 ++ src/store/server/sensor/getters.ts | 5 + src/store/server/sensor/index.ts | 23 ++ src/store/server/sensor/mutations.ts | 18 ++ src/store/server/sensor/types.ts | 14 ++ src/store/socket/actions.ts | 4 + src/store/variables.ts | 1 + 15 files changed, 159 insertions(+), 457 deletions(-) delete mode 100644 src/components/dialogs/HistoryDetailsDialog.vue delete mode 100644 src/components/panels/History/HistoryListRow.vue delete mode 100644 src/components/panels/History/HistoryListRowCell.vue create mode 100644 src/store/server/sensor/actions.ts create mode 100644 src/store/server/sensor/getters.ts create mode 100644 src/store/server/sensor/index.ts create mode 100644 src/store/server/sensor/mutations.ts create mode 100644 src/store/server/sensor/types.ts diff --git a/src/components/dialogs/HistoryDetailsDialog.vue b/src/components/dialogs/HistoryDetailsDialog.vue deleted file mode 100644 index dfa3c2c34..000000000 --- a/src/components/dialogs/HistoryDetailsDialog.vue +++ /dev/null @@ -1,170 +0,0 @@ - - - diff --git a/src/components/dialogs/HistoryListPanelDetailsDialog.vue b/src/components/dialogs/HistoryListPanelDetailsDialog.vue index 16f7f33f8..8236db9c8 100644 --- a/src/components/dialogs/HistoryListPanelDetailsDialog.vue +++ b/src/components/dialogs/HistoryListPanelDetailsDialog.vue @@ -106,6 +106,22 @@ export default class HistoryListPanelDetailsDialog extends Mixins(BaseMixin) { }, ] + if ('auxiliary_data' in this.job) { + this.job.auxiliary_data?.forEach((data) => { + let value = data.value.toString() + if (!Array.isArray(data.value)) { + value = `${Math.round(data.value * 1000) / 1000} ${data.units}` + } + if (value === '') value = '--' + + entries.push({ + name: data.description, + value, + exists: true, + }) + }) + } + return entries.filter((entry) => entry.exists) } diff --git a/src/components/panels/History/HistoryListEntryJob.vue b/src/components/panels/History/HistoryListEntryJob.vue index 29cd970b6..aa8da85d9 100644 --- a/src/components/panels/History/HistoryListEntryJob.vue +++ b/src/components/panels/History/HistoryListEntryJob.vue @@ -300,6 +300,11 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { //@ts-ignore let value = col.value in item ? item[col.value] : null if (value === null) value = col.value in item.metadata ? item.metadata[col.value] : null + if (col.value.startsWith('history_field_')) { + const fieldName = col.value.replace('history_field_', '') + const field = item.auxiliary_data?.find((field: any) => field.name === fieldName) + if (field && !Array.isArray(field.value)) return `${Math.round(field.value * 1000) / 1000} ${field.units}` + } if (value === null) return '--' if (col.value === 'slicer') value += '
' + item.metadata.slicer_version diff --git a/src/components/panels/History/HistoryListRow.vue b/src/components/panels/History/HistoryListRow.vue deleted file mode 100644 index 93214a4c6..000000000 --- a/src/components/panels/History/HistoryListRow.vue +++ /dev/null @@ -1,226 +0,0 @@ - - diff --git a/src/components/panels/History/HistoryListRowCell.vue b/src/components/panels/History/HistoryListRowCell.vue deleted file mode 100644 index c651b08a0..000000000 --- a/src/components/panels/History/HistoryListRowCell.vue +++ /dev/null @@ -1,61 +0,0 @@ - - diff --git a/src/components/panels/HistoryListPanel.vue b/src/components/panels/HistoryListPanel.vue index 99aff2093..d65f352da 100644 --- a/src/components/panels/HistoryListPanel.vue +++ b/src/components/panels/HistoryListPanel.vue @@ -409,6 +409,16 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { }, ] + this.moonrakerSensors.forEach((sensor) => { + headers.push({ + text: sensor.desc, + value: sensor.name, + align: 'left', + configable: true, + visible: false, + }) + }) + headers.forEach((header) => { if (header.visible && this.hideColums.includes(header.value)) { header.visible = false @@ -420,6 +430,32 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { return headers } + get moonrakerSensors() { + const config = this.$store.state.server.config?.config ?? {} + const sensors = Object.keys(config).filter((key) => key.startsWith('sensor ')) + const historyFields: { desc: string; unit: string; provider: string; name: string; parameter: string }[] = [] + + sensors.forEach((configName) => { + const sensor = config[configName] ?? {} + + Object.keys(sensor) + .filter((key) => key.startsWith('history_field_')) + .forEach((key) => { + const historyField = sensor[key] + + historyFields.push({ + desc: historyField.desc, + unit: historyField.units, + provider: configName, + parameter: historyField.parameter, + name: key, + }) + }) + }) + + return historyFields + } + get tableFields() { return this.filteredHeaders.filter( (col: any) => !['filename', 'status'].includes(col.value) && col.value !== '' diff --git a/src/store/server/history/types.ts b/src/store/server/history/types.ts index 4d8cd56fc..f3f0350f9 100644 --- a/src/store/server/history/types.ts +++ b/src/store/server/history/types.ts @@ -50,6 +50,15 @@ export interface ServerHistoryStateJob { status: string start_time: number total_duration: number + auxiliary_data?: ServerHistoryStateJobAxiliaryData[] +} + +export interface ServerHistoryStateJobAxiliaryData { + description: string + name: string + provider: string + units: string + value: number | number[] } export interface HistoryListRowJob extends ServerHistoryStateJob { diff --git a/src/store/server/index.ts b/src/store/server/index.ts index 1fde111a3..5e90dc7dc 100644 --- a/src/store/server/index.ts +++ b/src/store/server/index.ts @@ -12,6 +12,7 @@ import { timelapse } from '@/store/server/timelapse' import { jobQueue } from '@/store/server/jobQueue' import { announcements } from '@/store/server/announcements' import { spoolman } from '@/store/server/spoolman' +import { sensor } from '@/store/server/sensor' // create getDefaultState export const getDefaultState = (): ServerState => { @@ -62,5 +63,6 @@ export const server: Module = { jobQueue, announcements, spoolman, + sensor, }, } diff --git a/src/store/server/sensor/actions.ts b/src/store/server/sensor/actions.ts new file mode 100644 index 000000000..28addc301 --- /dev/null +++ b/src/store/server/sensor/actions.ts @@ -0,0 +1,26 @@ +import Vue from 'vue' +import { ActionTree } from 'vuex' +import { ServerSensorState } from '@/store/server/sensor/types' +import { RootState } from '@/store/types' + +export const actions: ActionTree = { + reset({ commit }) { + commit('reset') + }, + + init() { + Vue.$socket.emit('server.sensors.list', {}, { action: 'server/sensor/getSensors' }) + }, + + getSensors({ commit, dispatch }, payload) { + commit('setSensors', payload.sensors) + + dispatch('socket/removeInitModule', 'server/sensor/init', { root: true }) + }, + + updateSensors({ commit }, payload) { + Object.keys(payload).forEach((key) => { + commit('updateSensor', { key, value: payload[key] }) + }) + }, +} diff --git a/src/store/server/sensor/getters.ts b/src/store/server/sensor/getters.ts new file mode 100644 index 000000000..b0b84ee3c --- /dev/null +++ b/src/store/server/sensor/getters.ts @@ -0,0 +1,5 @@ +import { GetterTree } from 'vuex' +import { ServerSensorState } from '@/store/server/sensor/types' + +// eslint-disable-next-line +export const getters: GetterTree = {} diff --git a/src/store/server/sensor/index.ts b/src/store/server/sensor/index.ts new file mode 100644 index 000000000..c60aea2c9 --- /dev/null +++ b/src/store/server/sensor/index.ts @@ -0,0 +1,23 @@ +import { Module } from 'vuex' +import { ServerSensorState } from '@/store/server/sensor/types' +import { actions } from '@/store/server/sensor/actions' +import { mutations } from '@/store/server/sensor/mutations' +import { getters } from '@/store/server/sensor/getters' + +export const getDefaultState = (): ServerSensorState => { + return { + sensors: {}, + } +} + +// initial state +const state = getDefaultState() + +// eslint-disable-next-line +export const sensor: Module = { + namespaced: true, + state, + getters, + actions, + mutations, +} diff --git a/src/store/server/sensor/mutations.ts b/src/store/server/sensor/mutations.ts new file mode 100644 index 000000000..5f4df98f4 --- /dev/null +++ b/src/store/server/sensor/mutations.ts @@ -0,0 +1,18 @@ +import Vue from 'vue' +import { getDefaultState } from './index' +import { MutationTree } from 'vuex' +import { ServerSensorState } from '@/store/server/sensor/types' + +export const mutations: MutationTree = { + reset(state) { + Object.assign(state, getDefaultState()) + }, + + setSensors(state, payload) { + Vue.set(state, 'sensors', payload) + }, + + updateSensor(state, payload) { + Vue.set(state.sensors, payload.key, payload.value) + }, +} diff --git a/src/store/server/sensor/types.ts b/src/store/server/sensor/types.ts new file mode 100644 index 000000000..e745b04d3 --- /dev/null +++ b/src/store/server/sensor/types.ts @@ -0,0 +1,14 @@ +export interface ServerSensorState { + sensors: { + [key: string]: ServerSensorStateSensor + } +} + +export interface ServerSensorStateSensor { + friendly_name: string + id: string + type: string + values: { + [key: string]: number + } +} diff --git a/src/store/socket/actions.ts b/src/store/socket/actions.ts index 50b66b16b..0a207d48b 100644 --- a/src/store/socket/actions.ts +++ b/src/store/socket/actions.ts @@ -135,6 +135,10 @@ export const actions: ActionTree = { dispatch('server/spoolman/getActiveSpoolId', payload.params[0], { root: true }) break + case 'notify_sensor_update': + dispatch('server/sensor/updateSensors', payload.params[0], { root: true }) + break + default: window.console.debug(payload) } diff --git a/src/store/variables.ts b/src/store/variables.ts index f3676d781..4db839504 100644 --- a/src/store/variables.ts +++ b/src/store/variables.ts @@ -35,6 +35,7 @@ export const initableServerComponents = [ 'jobQueue', 'announcements', 'spoolman', + 'sensor', ] /* From 149941a80892c7994058d7d00bccdc70bf98e0f5 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sat, 18 May 2024 10:41:04 +0200 Subject: [PATCH 2/4] feat: add history_fields to CSV export Signed-off-by: Stefan Dej --- src/components/panels/HistoryListPanel.vue | 53 ++++++++++++++++------ 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/components/panels/HistoryListPanel.vue b/src/components/panels/HistoryListPanel.vue index d65f352da..3525e19ea 100644 --- a/src/components/panels/HistoryListPanel.vue +++ b/src/components/panels/HistoryListPanel.vue @@ -580,6 +580,12 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { row.push('status') this.tableFields.forEach((col) => { + if (col.value.startsWith('history_field_')) { + const sensorName = col.value.replace('history_field_', '') + row.push(sensorName) + return + } + row.push(col.value) }) @@ -648,18 +654,9 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { row.push('job') row.push(job.status) - this.tableFields - .filter((header) => header.value !== 'slicer') - .forEach((col) => { - row.push(this.outputValue(col, job, csvSeperator)) - }) - - if (this.tableFields.find((header) => header.value === 'slicer')?.visible) { - let slicerString = 'slicer' in job.metadata && job.metadata.slicer ? job.metadata.slicer : '--' - if ('slicer_version' in job.metadata && job.metadata.slicer_version) - slicerString += ' ' + job.metadata.slicer_version - row.push(slicerString) - } + this.tableFields.forEach((col) => { + row.push(this.outputValue(col, job, csvSeperator)) + }) content.push(row) }) @@ -670,7 +667,7 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { const csvContent = 'data:text/csv;charset=utf-8,' + content.map((entry) => - entry.map((field) => (field.indexOf(csvSeperator) === -1 ? field : `"${field}"`)).join(csvSeperator) + entry.map((field) => (field?.indexOf(csvSeperator) === -1 ? field : `"${field}"`)).join(csvSeperator) ).join('\n') const link = document.createElement('a') @@ -687,6 +684,36 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { let value = col.value in job ? job[col.value] : null if (value === null) value = col.value in job.metadata ? job.metadata[col.value] : null + if (col.value === 'slicer') { + let slicerString = 'slicer' in job.metadata && job.metadata.slicer ? job.metadata.slicer : '--' + if ('slicer_version' in job.metadata && job.metadata.slicer_version) + slicerString += ' ' + job.metadata.slicer_version + + if (csvSeperator !== null && value.includes(csvSeperator)) return '"' + slicerString + '"' + + return slicerString + } + + if (col.value.startsWith('history_field_')) { + const sensorName = col.value.replace('history_field_', '') + const sensor = job.auxiliary_data?.find((sensor) => sensor.name === sensorName) + + let value = sensor?.value?.toString() + + // return value, when it is not an array + if (sensor && !Array.isArray(sensor.value)) { + value = `${Math.round(sensor.value * 1000) / 1000} ${sensor.units}` + } + + // return empty string, when value is null + if (!value) return '--' + + // escape fields with the csvSeperator in the content + if (csvSeperator !== null && value?.includes(csvSeperator)) return `"${value}"` + + return value + } + switch (col.outputType) { case 'date': return this.formatDateTime(value * 1000) From 99c76510b17ff68fbb0be28f589603852f08d135 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Sat, 18 May 2024 10:50:57 +0200 Subject: [PATCH 3/4] fix: fix csv output for history_fields Signed-off-by: Stefan Dej --- src/components/panels/HistoryListPanel.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/panels/HistoryListPanel.vue b/src/components/panels/HistoryListPanel.vue index 3525e19ea..10cd90fba 100644 --- a/src/components/panels/HistoryListPanel.vue +++ b/src/components/panels/HistoryListPanel.vue @@ -702,7 +702,7 @@ export default class HistoryListPanel extends Mixins(BaseMixin) { // return value, when it is not an array if (sensor && !Array.isArray(sensor.value)) { - value = `${Math.round(sensor.value * 1000) / 1000} ${sensor.units}` + value = sensor.value?.toLocaleString(this.browserLocale, { useGrouping: false }) ?? 0 } // return empty string, when value is null From 315e6d491ce12c994acdcf824d15165f94faf644 Mon Sep 17 00:00:00 2001 From: Stefan Dej Date: Wed, 22 May 2024 21:55:34 +0200 Subject: [PATCH 4/4] refactor: fix typo in ServerHistoryStateJobAuxiliaryData type Signed-off-by: Stefan Dej --- src/store/server/history/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/store/server/history/types.ts b/src/store/server/history/types.ts index f3f0350f9..4cebfb17a 100644 --- a/src/store/server/history/types.ts +++ b/src/store/server/history/types.ts @@ -50,10 +50,10 @@ export interface ServerHistoryStateJob { status: string start_time: number total_duration: number - auxiliary_data?: ServerHistoryStateJobAxiliaryData[] + auxiliary_data?: ServerHistoryStateJobAuxiliaryData[] } -export interface ServerHistoryStateJobAxiliaryData { +export interface ServerHistoryStateJobAuxiliaryData { description: string name: string provider: string