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 18, 2024
1 parent 4ca0e7a commit cb52d94
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 4 deletions.
1 change: 0 additions & 1 deletion src/libs/actions/data-lake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ export const setDataLakeVariableData = (
notifyDataLakeVariableListeners(id)
}


export const deleteDataLakeVariable = (id: string): void => {
delete dataLakeVariableInfo[id]
delete dataLakeVariableData[id]
Expand Down
18 changes: 15 additions & 3 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 @@ -295,9 +296,20 @@ export abstract class ArduPilotVehicle<Modes> extends Vehicle.AbstractVehicle<Mo
return
}

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

// Special handling for NAMED_VALUE_FLOAT messages
if (messageType === 'NAMED_VALUE_FLOAT') {
const name = (mavlink_message.message.name as string[]).join('').replace(/\0/g, '')
setDataLakeVariableData(`${messageType}/${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
104 changes: 104 additions & 0 deletions src/libs/vehicle/ardupilot/data-flattener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* Result of flattening a data structure
*/
export type FlattenedPair = {
/**
* Full path to the data, including message type and field name
* e.g. "ATTITUDE/roll" or "NAMED_VALUE_FLOAT/GpsHDOP"
*/
path: string
/**
* The actual value of the field after flattening
* - Primitive values are kept as-is
* - String arrays are joined
* - Number arrays create multiple entries
*/
value: string | number | boolean
/**
* The type of the flattened value
* Used to create the appropriate DataLakeVariable
*/
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 cb52d94

Please sign in to comment.