diff --git a/src/components/configuration/DataLakeAlertsConfig.vue b/src/components/configuration/DataLakeAlertsConfig.vue new file mode 100644 index 0000000000..e443c36d95 --- /dev/null +++ b/src/components/configuration/DataLakeAlertsConfig.vue @@ -0,0 +1,622 @@ + + + diff --git a/src/libs/actions/data-lake-alerts.ts b/src/libs/actions/data-lake-alerts.ts new file mode 100644 index 0000000000..cdcbc131a7 --- /dev/null +++ b/src/libs/actions/data-lake-alerts.ts @@ -0,0 +1,488 @@ +import { v4 as uuid } from 'uuid' + +import { pushAlert } from '@/libs/alert-manager' +import { Alert, AlertLevel } from '@/types/alert' + +import { + DataLakeVariableType, + getDataLakeVariableData, + getDataLakeVariableInfo, + listenDataLakeVariable, + unlistenDataLakeVariable, +} from './data-lake' + +const dataLakeAlertsKey = 'cockpit-data-lake-alerts' + +/** + * Comparison operators for alerts + */ +export type ComparisonOperator = + | 'equals' // for all types + | 'notEquals' // for all types + | 'greaterThan' // for numbers + | 'greaterThanOrEqual' // for numbers + | 'lessThan' // for numbers + | 'lessThanOrEqual' // for numbers + +/** + * Get available comparison operators for a given variable type + * @param {DataLakeVariableType} type - The type of the variable + * @returns {Array<{value: ComparisonOperator, label: string}>} Available operators + */ +export const getComparisonOperatorsForType = ( + type: DataLakeVariableType +): Array<{ + /** + * The value of the operator + */ + value: ComparisonOperator + /** + * The label of the operator + */ + label: string +}> => { + switch (type) { + case 'boolean': + return [{ value: 'equals', label: 'equals' }] + case 'number': + return [ + { value: 'greaterThan', label: '>' }, + { value: 'greaterThanOrEqual', label: '>=' }, + { value: 'lessThan', label: '<' }, + { value: 'lessThanOrEqual', label: '<=' }, + { value: 'equals', label: '=' }, + { value: 'notEquals', label: '≠' }, + ] + case 'string': + return [ + { value: 'equals', label: 'equals' }, + { value: 'notEquals', label: 'not equals' }, + ] + default: + return [ + { value: 'equals', label: 'equals' }, + { value: 'notEquals', label: 'not equals' }, + ] + } +} + +/** + * Interface for a single alert condition + */ +export interface AlertCondition { + /** The ID of the data lake variable to monitor for this condition */ + variableId: string + /** The comparison operator to use */ + operator: ComparisonOperator + /** The value to compare against */ + compareValue: string | number | boolean +} + +/** + * Interface for a Data Lake alert configuration + */ +export interface DataLakeAlert { + /** Unique identifier for the alert */ + id: string + /** User-friendly name for the alert */ + name: string + /** Whether the alert is enabled */ + enabled: boolean + /** Array of conditions (all must be true - AND logic), each with its own variable */ + conditions: AlertCondition[] + /** The alert level when triggered */ + level: AlertLevel + /** Minimum time (in ms) between consecutive alerts */ + minInterval: number + /** Minimum duration (in ms) that conditions must be met before triggering (0 = disabled) */ + minDuration: number + /** Optional custom message */ + customMessage?: string +} + +/** Internal tracking for alert state */ +interface AlertState { + /** + * Map of variable IDs to their listener IDs + */ + listenerIds: Record + /** + * The time the alert was last triggered + */ + lastTriggeredTime: number + /** + * The time when conditions first became true (null if conditions are not met) + */ + conditionMetSince: number | null +} + +let globalAlerts: DataLakeAlert[] = [] +const alertStates: Record = {} + +/** + * Evaluate if a single condition is met + * @param {AlertCondition} condition - The condition to evaluate + * @returns {boolean} Whether the condition is met + */ +const evaluateSingleCondition = (condition: AlertCondition): boolean => { + const currentValue = getDataLakeVariableData(condition.variableId) + if (currentValue === undefined) return false + + const { operator, compareValue } = condition + + switch (operator) { + case 'equals': + return currentValue === compareValue + case 'notEquals': + return currentValue !== compareValue + case 'greaterThan': + return Number(currentValue) > Number(compareValue) + case 'greaterThanOrEqual': + return Number(currentValue) >= Number(compareValue) + case 'lessThan': + return Number(currentValue) < Number(compareValue) + case 'lessThanOrEqual': + return Number(currentValue) <= Number(compareValue) + default: + return false + } +} + +/** + * Evaluate if all alert conditions are met (AND logic) + * @param {DataLakeAlert} alert - The alert configuration + * @returns {boolean} Whether all conditions are met + */ +const evaluateAllConditions = (alert: DataLakeAlert): boolean => { + if (alert.conditions.length === 0) return false + return alert.conditions.every((condition) => evaluateSingleCondition(condition)) +} + +/** + * Get a human-readable description of the operator + * @param {ComparisonOperator} operator - The operator + * @returns {string} Human-readable description + */ +const getOperatorDescription = (operator: ComparisonOperator): string => { + switch (operator) { + case 'equals': + return 'equals' + case 'notEquals': + return 'does not equal' + case 'greaterThan': + return 'is greater than' + case 'greaterThanOrEqual': + return 'is greater than or equal to' + case 'lessThan': + return 'is less than' + case 'lessThanOrEqual': + return 'is less than or equal to' + default: + return operator + } +} + +/** + * Format a single condition for display (with variable name and current value) + * @param {AlertCondition} condition - The condition + * @returns {string} Formatted condition string + */ +const formatConditionForMessage = (condition: AlertCondition): string => { + const variableInfo = getDataLakeVariableInfo(condition.variableId) + const variableName = variableInfo?.name || condition.variableId + const currentValue = getDataLakeVariableData(condition.variableId) + return `${variableName} (${currentValue}) ${getOperatorDescription(condition.operator)} ${condition.compareValue}` +} + +/** + * Replace variable name placeholders in a custom message with their current values + * Supports {{variableName}} syntax where variableName is the display name of a variable + * @param {string} message - The custom message with placeholders + * @param {DataLakeAlert} alert - The alert configuration (used to find variable IDs) + * @returns {string} The message with placeholders replaced + */ +const replaceMessagePlaceholders = (message: string, alert: DataLakeAlert): string => { + // Find all {{variableName}} placeholders + const placeholderRegex = /\{\{([^}]+)\}\}/g + return message.replace(placeholderRegex, (match, variableName) => { + // Find a condition with a variable that matches this name + for (const condition of alert.conditions) { + const variableInfo = getDataLakeVariableInfo(condition.variableId) + if (variableInfo?.name === variableName) { + const value = getDataLakeVariableData(condition.variableId) + return value !== undefined ? String(value) : match + } + } + // If no matching variable found, leave the placeholder as-is + return match + }) +} + +/** + * Generate the alert message + * @param {DataLakeAlert} alert - The alert configuration + * @returns {string} The alert message + */ +const generateAlertMessage = (alert: DataLakeAlert): string => { + if (alert.customMessage) { + return replaceMessagePlaceholders(alert.customMessage, alert) + } + + const conditionsStr = alert.conditions.map(formatConditionForMessage).join(' AND ') + return `${alert.name}: ${conditionsStr}` +} + +/** + * Get unique variable IDs from an alert's conditions + * @param {DataLakeAlert} alert - The alert + * @returns {string[]} Array of unique variable IDs + */ +const getUniqueVariableIds = (alert: DataLakeAlert): string[] => { + return [...new Set(alert.conditions.map((c) => c.variableId))] +} + +/** + * Handler for when any monitored variable changes + * @param {DataLakeAlert} alert - The alert to evaluate + */ +const handleVariableChange = (alert: DataLakeAlert): void => { + if (!alert.enabled) return + + const state = alertStates[alert.id] + if (!state) return + + const now = Date.now() + const conditionsMet = evaluateAllConditions(alert) + + if (conditionsMet) { + // Track when conditions first became true + if (state.conditionMetSince === null) { + state.conditionMetSince = now + } + + const durationMet = alert.minDuration === 0 || now - state.conditionMetSince >= alert.minDuration + const intervalMet = now - state.lastTriggeredTime >= alert.minInterval + + if (durationMet && intervalMet) { + const message = generateAlertMessage(alert) + + pushAlert(new Alert(alert.level, message)) + state.lastTriggeredTime = now + // Reset the condition met time after triggering + state.conditionMetSince = null + } + } else { + // Conditions no longer met, reset the timer + state.conditionMetSince = null + } +} + +/** + * Set up listeners for an alert (one per unique variable in conditions) + * @param {DataLakeAlert} alert - The alert to set up + */ +const setupAlertListener = (alert: DataLakeAlert): void => { + // Clean up existing listeners if any + cleanupAlertListener(alert.id) + + const variableIds = getUniqueVariableIds(alert) + const listenerIds: Record = {} + + // Set up a listener for each unique variable + for (const variableId of variableIds) { + const listenerId = listenDataLakeVariable(variableId, () => { + // Re-fetch the alert from globalAlerts to get the latest state + const currentAlert = globalAlerts.find((a) => a.id === alert.id) + if (currentAlert) { + handleVariableChange(currentAlert) + } + }) + listenerIds[variableId] = listenerId + } + + alertStates[alert.id] = { + listenerIds, + lastTriggeredTime: 0, + conditionMetSince: null, + } +} + +/** + * Clean up listeners for an alert + * @param {string} alertId - The ID of the alert + */ +const cleanupAlertListener = (alertId: string): void => { + const state = alertStates[alertId] + if (!state) return + + // Unsubscribe from all variables + for (const [variableId, listenerId] of Object.entries(state.listenerIds)) { + unlistenDataLakeVariable(variableId, listenerId) + } + + delete alertStates[alertId] +} + +/** + * Load alerts from localStorage + */ +const loadAlerts = (): void => { + try { + const savedAlerts = localStorage.getItem(dataLakeAlertsKey) + if (savedAlerts) { + globalAlerts = JSON.parse(savedAlerts) as DataLakeAlert[] + // Set up listeners for all alerts + globalAlerts.forEach((alert) => { + setupAlertListener(alert) + }) + } + } catch (error) { + console.error('Failed to load data lake alerts:', error) + globalAlerts = [] + } +} + +/** + * Save alerts to localStorage + */ +const saveAlerts = (): void => { + try { + localStorage.setItem(dataLakeAlertsKey, JSON.stringify(globalAlerts)) + } catch (error) { + console.error('Failed to save data lake alerts:', error) + } +} + +/** + * Get all data lake alerts + * @returns {DataLakeAlert[]} All configured alerts + */ +export const getAllDataLakeAlerts = (): DataLakeAlert[] => { + return [...globalAlerts] +} + +/** + * Get a specific alert by ID + * @param {string} id - The alert ID + * @returns {DataLakeAlert | undefined} The alert if found + */ +export const getDataLakeAlert = (id: string): DataLakeAlert | undefined => { + return globalAlerts.find((a) => a.id === id) +} + +/** + * Create a new data lake alert + * @param {Omit} alertConfig - The alert configuration (without ID) + * @returns {DataLakeAlert} The created alert + */ +export const createDataLakeAlert = (alertConfig: Omit): DataLakeAlert => { + const alert: DataLakeAlert = { + ...alertConfig, + id: uuid(), + } + + globalAlerts.push(alert) + setupAlertListener(alert) + saveAlerts() + + return alert +} + +/** + * Update an existing alert + * @param {DataLakeAlert} updatedAlert - The updated alert configuration + */ +export const updateDataLakeAlert = (updatedAlert: DataLakeAlert): void => { + const index = globalAlerts.findIndex((a) => a.id === updatedAlert.id) + if (index === -1) { + console.error(`Alert with id '${updatedAlert.id}' not found`) + return + } + + // Clean up old listeners and set up new ones + cleanupAlertListener(updatedAlert.id) + + globalAlerts[index] = updatedAlert + setupAlertListener(updatedAlert) + + saveAlerts() +} + +/** + * Delete an alert + * @param {string} id - The ID of the alert to delete + */ +export const deleteDataLakeAlert = (id: string): void => { + const index = globalAlerts.findIndex((a) => a.id === id) + if (index === -1) return + + cleanupAlertListener(id) + globalAlerts.splice(index, 1) + saveAlerts() +} + +/** + * Toggle the enabled state of an alert + * @param {string} id - The alert ID + * @param {boolean} enabled - The new enabled state + */ +export const setDataLakeAlertEnabled = (id: string, enabled: boolean): void => { + const alert = globalAlerts.find((a) => a.id === id) + if (!alert) return + + alert.enabled = enabled + saveAlerts() +} + +/** + * Get the default compare value for a given variable type + * @param {DataLakeVariableType} type - The variable type + * @returns {string | number | boolean} The default compare value + */ +export const getDefaultCompareValue = (type: DataLakeVariableType): string | number | boolean => { + switch (type) { + case 'boolean': + return true + case 'number': + return 0 + case 'string': + return '' + default: + return '' + } +} + +/** + * Get the default operator for a given variable type + * @param {DataLakeVariableType} type - The variable type + * @returns {ComparisonOperator} The default operator + */ +export const getDefaultOperator = (type: DataLakeVariableType): ComparisonOperator => { + switch (type) { + case 'boolean': + return 'equals' + case 'number': + return 'greaterThan' + case 'string': + return 'equals' + default: + return 'equals' + } +} + +/** + * Create a default condition for a given variable + * @param {string} variableId - The variable ID + * @param {DataLakeVariableType} type - The variable type + * @returns {AlertCondition} A default condition + */ +export const createDefaultCondition = (variableId: string, type: DataLakeVariableType): AlertCondition => { + return { + variableId, + operator: getDefaultOperator(type), + compareValue: getDefaultCompareValue(type), + } +} + +// Initialize by loading saved alerts +loadAlerts() diff --git a/src/libs/alert-manager.ts b/src/libs/alert-manager.ts new file mode 100644 index 0000000000..916295c67c --- /dev/null +++ b/src/libs/alert-manager.ts @@ -0,0 +1,154 @@ +import { v4 as uuid } from 'uuid' + +import { Alert, AlertLevel } from '@/types/alert' + +/** + * Callback type for alert listeners + */ +type AlertListener = (alert: Alert, index: number) => void + +/** + * Internal state for the alert manager + */ +const alerts: Alert[] = [] +const alertListeners: Record = {} + +/** + * Get all alerts + * @returns {Alert[]} All alerts + */ +export const getAllAlerts = (): Alert[] => { + return [...alerts] +} + +/** + * Get a sorted copy of all alerts by time created + * @returns {Alert[]} Sorted alerts + */ +export const getSortedAlerts = (): Alert[] => { + return [...alerts].sort((a, b) => a.time_created.getTime() - b.time_created.getTime()) +} + +/** + * Push a new alert + * @param {Alert} alert - The alert to push + */ +export const pushAlert = (alert: Alert): void => { + alerts.push(alert) + + // Log to console based on level + switch (alert.level) { + case AlertLevel.Success: + console.log(alert.message) + break + case AlertLevel.Error: + console.error(alert.message) + break + case AlertLevel.Info: + console.info(alert.message) + break + case AlertLevel.Warning: + console.warn(alert.message) + break + case AlertLevel.Critical: + console.error(alert.message) + break + default: + console.warn(`Unknown alert level. Message: ${alert.message}`) + break + } + + // Notify listeners + const alertIndex = alerts.length - 1 + Object.values(alertListeners).forEach((listener) => { + try { + listener(alert, alertIndex) + } catch (error) { + console.error('Error in alert listener:', error) + } + }) +} + +/** + * Push a success alert + * @param {string} message - The alert message + * @param {Date} timeCreated - Optional time created + */ +export const pushSuccessAlert = (message: string, timeCreated: Date = new Date()): void => { + pushAlert(new Alert(AlertLevel.Success, message, timeCreated)) +} + +/** + * Push an error alert + * @param {string} message - The alert message + * @param {Date} timeCreated - Optional time created + */ +export const pushErrorAlert = (message: string, timeCreated: Date = new Date()): void => { + pushAlert(new Alert(AlertLevel.Error, message, timeCreated)) +} + +/** + * Push an info alert + * @param {string} message - The alert message + * @param {Date} timeCreated - Optional time created + */ +export const pushInfoAlert = (message: string, timeCreated: Date = new Date()): void => { + pushAlert(new Alert(AlertLevel.Info, message, timeCreated)) +} + +/** + * Push a warning alert + * @param {string} message - The alert message + * @param {Date} timeCreated - Optional time created + */ +export const pushWarningAlert = (message: string, timeCreated: Date = new Date()): void => { + pushAlert(new Alert(AlertLevel.Warning, message, timeCreated)) +} + +/** + * Push a critical alert + * @param {string} message - The alert message + * @param {Date} timeCreated - Optional time created + */ +export const pushCriticalAlert = (message: string, timeCreated: Date = new Date()): void => { + pushAlert(new Alert(AlertLevel.Critical, message, timeCreated)) +} + +/** + * Subscribe to alert events + * @param {AlertListener} listener - The listener function + * @returns {string} The listener ID (used for unsubscribing) + */ +export const subscribeToAlerts = (listener: AlertListener): string => { + const listenerId = uuid() + alertListeners[listenerId] = listener + return listenerId +} + +/** + * Unsubscribe from alert events + * @param {string} listenerId - The listener ID to unsubscribe + */ +export const unsubscribeFromAlerts = (listenerId: string): void => { + delete alertListeners[listenerId] +} + +/** + * Get the current number of alerts + * @returns {number} The number of alerts + */ +export const getAlertsCount = (): number => { + return alerts.length +} + +/** + * Get an alert by index + * @param {number} index - The alert index + * @returns {Alert | undefined} The alert if found + */ +export const getAlertByIndex = (index: number): Alert | undefined => { + return alerts[index] +} + +// Push initial alert +pushAlert(new Alert(AlertLevel.Success, 'Cockpit started')) diff --git a/src/libs/blueos/definitions.ts b/src/libs/blueos/definitions.ts new file mode 100644 index 0000000000..6288ca7fd1 --- /dev/null +++ b/src/libs/blueos/definitions.ts @@ -0,0 +1,21 @@ +// Register BlueOS variables in the data lake +export const blueOsVariables = { + cpuTemp: { + id: 'blueos/cpu/tempC', + name: 'BlueOS CPU Temperature', + type: 'number', + description: 'The average temperature of the BlueOS CPU cores in °C.', + }, + cpuUsageAverage: { + id: 'blueos/cpu/usageAverage', + name: 'BlueOS CPU Usage', + type: 'number', + description: 'The average usage of the BlueOS CPU cores in %.', + }, + cpuFrequencyAverage: { + id: 'blueos/cpu/frequencyAverage', + name: 'BlueOS CPU Frequency', + type: 'number', + description: 'The average frequency of the BlueOS CPU cores in Hz.', + }, +} diff --git a/src/stores/alert.ts b/src/stores/alert.ts index 31ea8a371c..ba62da6388 100644 --- a/src/stores/alert.ts +++ b/src/stores/alert.ts @@ -1,17 +1,30 @@ import { defineStore } from 'pinia' -import { computed, reactive, ref, watch } from 'vue' +import { computed, onUnmounted, reactive, ref } from 'vue' import { useBlueOsStorage } from '@/composables/settingsSyncer' +import { + getAlertsCount, + getAllAlerts, + pushAlert as managerPushAlert, + pushCriticalAlert as managerPushCriticalAlert, + pushErrorAlert as managerPushErrorAlert, + pushInfoAlert as managerPushInfoAlert, + pushSuccessAlert as managerPushSuccessAlert, + pushWarningAlert as managerPushWarningAlert, + subscribeToAlerts, + unsubscribeFromAlerts, +} from '@/libs/alert-manager' import { Alert, AlertLevel } from '../types/alert' export const useAlertStore = defineStore('alert', () => { - const alerts = reactive([new Alert(AlertLevel.Success, 'Cockpit started')]) + // Reactive alerts array that syncs with the manager + const alerts = reactive(getAllAlerts()) + + // Settings stored in BlueOS storage (Vue composable) const enableVoiceAlerts = useBlueOsStorage('cockpit-enable-voice-alerts', true) const neverShowArmedMenuWarning = useBlueOsStorage('cockpit-never-show-armed-menu-warning', false) const skipArmedMenuWarningThisSession = ref(false) - // eslint-disable-next-line jsdoc/require-jsdoc - const availableAlertSpeechVoices = reactive([]) const selectedAlertSpeechVoiceName = useBlueOsStorage( 'cockpit-selected-alert-speech-voice', undefined @@ -25,53 +38,56 @@ export const useAlertStore = defineStore('alert', () => { ]) const alertVolume = useBlueOsStorage('cockpit-alert-volume', 1) + // Speech synthesis state + // eslint-disable-next-line jsdoc/require-jsdoc + const availableAlertSpeechVoices = reactive([]) + const lastSpokenAlertIndex = ref(0) + const sortedAlerts = computed(() => { - return alerts.sort((a, b) => a.time_created.getTime() - b.time_created.getTime()) + return [...alerts].sort((a, b) => a.time_created.getTime() - b.time_created.getTime()) }) + // Wrapper functions that delegate to the manager const pushAlert = (alert: Alert): void => { - alerts.push(alert) - - switch (alert.level) { - case AlertLevel.Success: - console.log(alert.message) - break - case AlertLevel.Error: - console.error(alert.message) - break - case AlertLevel.Info: - console.info(alert.message) - break - case AlertLevel.Warning: - console.warn(alert.message) - break - case AlertLevel.Critical: - console.error(alert.message) - break - default: - unimplemented(`A new alert level was added but we have not updated - this part of the code. Regardless of that, here's the alert message: ${alert.message}`) - break - } + managerPushAlert(alert) } const pushSuccessAlert = (message: string, time_created: Date = new Date()): void => { - pushAlert(new Alert(AlertLevel.Success, message, time_created)) + managerPushSuccessAlert(message, time_created) } + const pushErrorAlert = (message: string, time_created: Date = new Date()): void => { - pushAlert(new Alert(AlertLevel.Error, message, time_created)) + managerPushErrorAlert(message, time_created) } + const pushInfoAlert = (message: string, time_created: Date = new Date()): void => { - pushAlert(new Alert(AlertLevel.Info, message, time_created)) + managerPushInfoAlert(message, time_created) } + const pushWarningAlert = (message: string, time_created: Date = new Date()): void => { - pushAlert(new Alert(AlertLevel.Warning, message, time_created)) + managerPushWarningAlert(message, time_created) } + const pushCriticalAlert = (message: string, time_created: Date = new Date()): void => { - pushAlert(new Alert(AlertLevel.Critical, message, time_created)) + managerPushCriticalAlert(message, time_created) } - // Alert speech syntesis routine + // Subscribe to manager alerts to keep reactive array in sync and handle speech + const alertListenerId = subscribeToAlerts((alert, alertIndex) => { + // Keep reactive array in sync + if (alerts.length < getAlertsCount()) { + alerts.push(alert) + } + + // Handle speech synthesis + const alertLevelEnabled = enabledAlertLevels.value.find((enabledAlert) => enabledAlert.level === alert.level) + const shouldMute = + !enableVoiceAlerts.value || + ((alertLevelEnabled === undefined || !alertLevelEnabled.enabled) && !alert.message.startsWith('#')) + speak(alert.message, alertIndex, shouldMute) + }) + + // Alert speech synthesis routine const synth = window.speechSynthesis // We need to cache these otherwise they get garbage collected... @@ -113,9 +129,6 @@ export const useAlertStore = defineStore('alert', () => { availableAlertSpeechVoices.map((v) => ({ value: v.name, name: `${v.name} (${v.lang})` })) ) - // Track the index of the last alert that finished being spoken - const lastSpokenAlertIndex = ref(0) - /** * Speaks a text out loud using the browsers TTS engine * @param {string} text - The text to speak @@ -147,14 +160,9 @@ export const useAlertStore = defineStore('alert', () => { synth.speak(utterance) } - watch(alerts, () => { - const lastAlertIndex = alerts.length - 1 - const lastAlert = alerts[lastAlertIndex] - const alertLevelEnabled = enabledAlertLevels.value.find((enabledAlert) => enabledAlert.level === lastAlert.level) - const shouldMute = - !enableVoiceAlerts.value || - ((alertLevelEnabled === undefined || !alertLevelEnabled.enabled) && !lastAlert.message.startsWith('#')) - speak(lastAlert.message, lastAlertIndex, shouldMute) + // Cleanup subscription when store is unmounted + onUnmounted(() => { + unsubscribeFromAlerts(alertListenerId) }) return { diff --git a/src/stores/mainVehicle.ts b/src/stores/mainVehicle.ts index ef8116ff1e..2628a0c819 100644 --- a/src/stores/mainVehicle.ts +++ b/src/stores/mainVehicle.ts @@ -20,6 +20,7 @@ import { getVehicleName, setKeyDataOnCockpitVehicleStorage, } from '@/libs/blueos' +import { blueOsVariables } from '@/libs/blueos/definitions' import * as Connection from '@/libs/connection/connection' import { ConnectionManager } from '@/libs/connection/connection-manager' import type { Package } from '@/libs/connection/m2r/messages/mavlink2rest' @@ -624,17 +625,6 @@ export const useMainVehicleStore = defineStore('main-vehicle', () => { updateVehicleId() - // Register BlueOS variables in the data lake - const blueOsVariables = { - cpuTemp: { id: 'blueos/cpu/tempC', name: 'CPU Temperature', type: 'number' }, - cpuUsageAverage: { id: 'blueos/cpu/usageAverage', name: 'BlueOS CPU Usage (average)', type: 'number' }, - cpuFrequencyAverage: { - id: 'blueos/cpu/frequencyAverage', - name: 'BlueOS CPU Frequency (average)', - type: 'number', - }, - } - const cpuUsageVariableId = (cpuName: string): string => `blueos/${cpuName}/usage` const cpuFrequencyVariableId = (cpuName: string): string => `blueos/${cpuName}/frequency` diff --git a/src/views/ConfigurationAlertsView.vue b/src/views/ConfigurationAlertsView.vue index a8d8151977..27bc75b174 100644 --- a/src/views/ConfigurationAlertsView.vue +++ b/src/views/ConfigurationAlertsView.vue @@ -4,17 +4,35 @@