Skip to content

Commit

Permalink
ardupilot.ts: deal with setting non-flat data
Browse files Browse the repository at this point in the history
  • Loading branch information
Williangalvani committed Dec 17, 2024
1 parent ec1c98f commit c6e5965
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 21 deletions.
31 changes: 12 additions & 19 deletions src/libs/actions/data-lake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,28 +52,21 @@ export const getDataLakeVariableData = (id: string): string | number | boolean |
return dataLakeVariableData[id]
}

export const setDataLakeVariableData = (id: string, data: object | string | number | boolean): void => {
const newData = data
if (data === null) {
return
}
if (dataLakeVariableData[id] === undefined) {
console.trace(`Cockpit action variable with id '${id}' does not exist. Creating it.`)
const type_of_variable = typeof data
if (type_of_variable === 'object') {
// TODO: support strings
}
if (type_of_variable !== 'string' && type_of_variable !== 'number') {
console.debug(`attempting to create a variable with type ${type_of_variable}. Skipping`)
return
export const setDataLakeVariableData = (
id: string,
data: object | string | number | boolean | Array<string | number>
): void => {
if (data === null) return

// Handle already-flat primitive types first
if (typeof data === 'string' || typeof data === 'number') {
if (dataLakeVariableInfo[id] === undefined) {
createDataLakeVariable(new DataLakeVariable(id, id, typeof data === 'string' ? 'string' : 'number'))
}
createDataLakeVariable(new DataLakeVariable(id, id, typeof data))
}
if (newData === undefined || typeof newData === 'object') {
dataLakeVariableData[id] = data
notifyDataLakeVariableListeners(id)
return
}
dataLakeVariableData[id] = newData
notifyDataLakeVariableListeners(id)
}

export const deleteDataLakeVariable = (id: string): void => {
Expand Down
16 changes: 14 additions & 2 deletions src/libs/vehicle/ardupilot/ardupilot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import type { MetadataFile } from '@/types/ardupilot-metadata'
import { type MissionLoadingCallback, type Waypoint, defaultLoadingCallback } from '@/types/mission'

import * as Vehicle from '../vehicle'
import { flattenData } from './data-flattener'
import { defaultMessageFrequency } from './defaults'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -296,8 +297,19 @@ export abstract class ArduPilotVehicle<Modes> extends Vehicle.AbstractVehicle<Mo
}

const messageName = mavlink_message.message.type
const entries = prepareMessageForDataLake(messageName, mavlink_message.message)
entries.forEach(([path, value]) => setDataLakeVariableData(path, value))

// Special handling for NAMED_VALUE_FLOAT messages
if (messageName === 'NAMED_VALUE_FLOAT') {
const name = (mavlink_message.message.name as string[]).join('').replace(/\0/g, '')
setDataLakeVariableData(`${messageName}/${name}`, mavlink_message.message.value)
return
}

// For all other messages, use the flattener
const flattened = flattenData(mavlink_message.message)
flattened.forEach(({ path, value }) => {
setDataLakeVariableData(path, value)
})

// Update our internal messages
this._messages.set(mavlink_message.message.type, { ...mavlink_message.message, epoch: new Date().getTime() })
Expand Down
99 changes: 99 additions & 0 deletions src/libs/vehicle/ardupilot/data-flattener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* Result of flattening a data structure
*/
export type FlattenedPair = {
/**
*
*/
path: string
/**
*
*/
value: string | number | boolean
/**
*
*/
type: 'string' | 'number' | 'boolean'
}

/**
* Type guard to check if a value is an array of numbers
* @param {unknown[]} data The data to check
* @returns {data is number[]} True if the array contains numbers
*/
function isNumberArray(data: unknown[]): data is number[] {
return typeof data[0] === 'number'
}

/**
* Type guard to check if a value is an array of strings
* @param {unknown[]} data The data to check
* @returns {data is string[]} True if the array contains strings
*/
function isStringArray(data: unknown[]): data is string[] {
return typeof data[0] === 'string'
}

/**
* Flattens complex data structures into simple types that can be stored in the data lake
* @param {Record<string, unknown>} data The data to flatten
* @returns {FlattenedPair[]} Array of flattened path/value pairs
*/
export function flattenData(data: Record<string, unknown>): FlattenedPair[] {
if (!('type' in data)) return []
const messageName = data.type as string

// Special handling for NAMED_VALUE_FLOAT messages
if (messageName === 'NAMED_VALUE_FLOAT') {
const name = (data.name as string[]).join('').replace(/\0/g, '')
return [
{
path: `${messageName}/${name}`,
type: 'number',
value: data.value as number,
},
...Object.entries(data)
.filter(([key]) => !['name', 'value', 'type'].includes(key))
.map(([key, value]) => ({
path: `${messageName}/${key}`,
type: typeof value as 'string' | 'number' | 'boolean',
value: value as string | number | boolean,
})),
]
}

// For all other messages
return Object.entries(data)
.filter(([key]) => key !== 'type')
.flatMap(([key, value]) => {
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
return [
{
path: `${messageName}/${key}`,
type: typeof value as 'string' | 'number' | 'boolean',
value,
},
]
}
if (Array.isArray(value)) {
if (value.length === 0) return []
if (isNumberArray(value)) {
return value.map((item, index) => ({
path: `${messageName}/${key}/${index}`,
type: 'number',
value: item,
}))
}
if (isStringArray(value)) {
return [
{
path: `${messageName}/${key}`,
type: 'string',
value: value.join(''),
},
]
}
}
return []
})
}

0 comments on commit c6e5965

Please sign in to comment.