From 696ef65da39fea9d4a3d932540e8c7b4841e58de Mon Sep 17 00:00:00 2001 From: Ludwig Richter Date: Mon, 1 Mar 2021 01:02:38 +0100 Subject: [PATCH] feat(widget): Add deep object extraction and fix styles --- src/model/sample-user-config.ts | 118 ++++++++++++------ src/widgets/9dof/widget.tsx | 10 +- src/widgets/graph-widget/graph.tsx | 12 +- .../graph-widget/hooks/use-callbacks.ts | 2 +- .../hooks/use-dark-color-scheme.ts | 24 ++++ .../graph-widget/lib/build-data-sample.ts | 18 +-- src/widgets/graph-widget/lib/extract-value.ts | 31 +++++ src/widgets/graph-widget/widget.tsx | 14 ++- src/widgets/index.ts | 2 +- 9 files changed, 169 insertions(+), 62 deletions(-) create mode 100644 src/widgets/graph-widget/hooks/use-dark-color-scheme.ts create mode 100644 src/widgets/graph-widget/lib/extract-value.ts diff --git a/src/model/sample-user-config.ts b/src/model/sample-user-config.ts index 0538ac3..f0d8cf5 100644 --- a/src/model/sample-user-config.ts +++ b/src/model/sample-user-config.ts @@ -9,20 +9,78 @@ const accLineGraph: WidgetProps = { channel: NineDOF, descriptors: [ { - key: 'acc.x', - title: 'Acc X', + key: 'result[0].acc.x', + title: 'Accelerometer X', color: '#d21800', isDotted: true }, { - key: 'acc.y', - title: 'Acc Y', + key: 'result[0].acc.y', + title: 'Accelerometer Y', color: '#00ec05', isDotted: true }, { - key: 'acc.y', - title: 'Acc Z', + key: 'result[0].acc.z', + title: 'Accelerometer Z', + color: '#0075ff', + isDotted: true + } + ] + } + ] +}; + +const gyroLineGraph: WidgetProps = { + isArea: false, + connections: [ + { + channel: NineDOF, + descriptors: [ + { + key: 'result[0].gyro.x', + title: 'Gyroscope X', + color: '#d21800', + isDotted: true + }, + { + key: 'result[0].gyro.y', + title: 'Gyroscope Y', + color: '#00ec05', + isDotted: true + }, + { + key: 'result[0].gyro.z', + title: 'Gyroscope Z', + color: '#0075ff', + isDotted: true + } + ] + } + ] +}; + +const magLineGraph: WidgetProps = { + isArea: false, + connections: [ + { + channel: NineDOF, + descriptors: [ + { + key: 'result[0].mag.x', + title: 'Magnetometer X', + color: '#d21800', + isDotted: true + }, + { + key: 'result[0].mag.y', + title: 'Magnetometer Y', + color: '#00ec05', + isDotted: true + }, + { + key: 'result[0].mag.z', + title: 'Magnetometer Z', color: '#0075ff', isDotted: true } @@ -36,45 +94,35 @@ export const userConfig: UserConfig = { dashboards: [ { title: 'Overview', - columns: 4, - rows: 4, + columns: 12, + rows: 12, widgets: [ - { - widgetName: 'sampleWidget', - width: 4, - height: 1, - title: 'Widget 1' - }, { widgetName: '9dof', - width: 2, - height: 2, - title: 'Widget 2' + width: 3, + height: 4, + title: 'Current values' }, { - widgetName: 'lineGraphWidget', - width: 2, - height: 1, - title: 'Widget 3', + widgetName: 'graphWidget', + width: 3, + height: 4, + title: 'Accelerometer', initialProps: accLineGraph }, { - widgetName: 'Widget4', - width: 1, - height: 1, - title: 'Widget 4' - }, - { - widgetName: 'Widget5', - width: 1, - height: 2, - title: 'Widget 5' + widgetName: 'graphWidget', + width: 3, + height: 4, + title: 'Gyroscope', + initialProps: gyroLineGraph }, { - widgetName: 'Widget6', - width: 1, - height: 3, - title: 'Widget 6' + widgetName: 'graphWidget', + width: 3, + height: 4, + title: 'Magnetometer', + initialProps: magLineGraph } ] } diff --git a/src/widgets/9dof/widget.tsx b/src/widgets/9dof/widget.tsx index 2f2ed29..0e68049 100644 --- a/src/widgets/9dof/widget.tsx +++ b/src/widgets/9dof/widget.tsx @@ -26,7 +26,7 @@ export function Widget() { const items = [ { sensor: 'Accelerometer', ...latestData?.acc }, { sensor: 'Gyroscope', ...latestData?.gyro }, - { sensor: 'Magnometer', ...latestData?.mag } + { sensor: 'Magnetometer', ...latestData?.mag } ]; console.log(items); @@ -44,7 +44,13 @@ export function Widget() { {(item: any) => ( - {(key: any) => {item[key]}} + {(key: any) => ( + + {typeof item[key] === 'number' + ? Math.round(item[key] * 1000) / 1000 + : item[key]} + + )} )} diff --git a/src/widgets/graph-widget/graph.tsx b/src/widgets/graph-widget/graph.tsx index 80739d2..6f8f0ba 100644 --- a/src/widgets/graph-widget/graph.tsx +++ b/src/widgets/graph-widget/graph.tsx @@ -10,6 +10,7 @@ import { Legend } from 'recharts'; import { DataSample, DataSetDescriptor } from './model'; +import { useDarkColorScheme } from './hooks/use-dark-color-scheme'; export interface GraphProps { data: DataSample[]; @@ -18,15 +19,17 @@ export interface GraphProps { } export function Graph({ data, descriptors, isArea }: GraphProps) { + const isDark = useDarkColorScheme(); + return ( {isArea ? ( {/* TODO: Implement area chart */} ) : ( - - - + + + {descriptors.map(descriptor => ( @@ -35,7 +38,8 @@ export function Graph({ data, descriptors, isArea }: GraphProps) { dataKey={descriptor.key} name={descriptor.title} stroke={descriptor.color} - dot={descriptor.isDotted} + dot={false} + isAnimationActive={false} /> ))} diff --git a/src/widgets/graph-widget/hooks/use-callbacks.ts b/src/widgets/graph-widget/hooks/use-callbacks.ts index 650a89b..b86a442 100644 --- a/src/widgets/graph-widget/hooks/use-callbacks.ts +++ b/src/widgets/graph-widget/hooks/use-callbacks.ts @@ -26,7 +26,7 @@ export function useCallbacks( // build current time diff from start const time = (new Date().getTime() - initialDate.getTime()) / 1000; const dataSample = buildDataSample(descriptors, message, time); - setData(prevState => [...prevState, dataSample]); + setData(prevState => [...prevState.slice(-20), dataSample]); } catch (err) { setError(err); } diff --git a/src/widgets/graph-widget/hooks/use-dark-color-scheme.ts b/src/widgets/graph-widget/hooks/use-dark-color-scheme.ts new file mode 100644 index 0000000..729dd03 --- /dev/null +++ b/src/widgets/graph-widget/hooks/use-dark-color-scheme.ts @@ -0,0 +1,24 @@ +import { StateSelector } from 'zustand'; +import { + ColorSchemeState, + useColorScheme +} from '@wuespace/telestion-client-common'; + +// color scheme selector +const selector: StateSelector< + ColorSchemeState, + ColorSchemeState['colorScheme'] +> = state => state.colorScheme; + +export function useDarkColorScheme(): boolean { + const colorScheme = useColorScheme(selector); + + switch (colorScheme) { + case 'system': + return matchMedia('(prefers-color-scheme: dark)').matches; + case 'light': + return false; + case 'dark': + return true; + } +} diff --git a/src/widgets/graph-widget/lib/build-data-sample.ts b/src/widgets/graph-widget/lib/build-data-sample.ts index 15ad7a1..8f35814 100644 --- a/src/widgets/graph-widget/lib/build-data-sample.ts +++ b/src/widgets/graph-widget/lib/build-data-sample.ts @@ -1,6 +1,6 @@ import { ReceiveMessage } from '@wuespace/telestion-client-types'; import { DataSample, DataSetDescriptor } from '../model'; -import { isObj } from './utils'; +import { extractValue } from './extract-value'; export function buildDataSample( descriptors: DataSetDescriptor[], @@ -10,21 +10,7 @@ export function buildDataSample( return descriptors.reduce( (acc, descriptor) => { if (message) { - if (!isObj(message.body)) { - throw new TypeError( - `Invalid message body received. (expected: object, received: ${message.body})` - ); - } - - const value = message.body[descriptor.key]; - - if (typeof value !== 'number') { - throw new TypeError( - `Invalid value received. (expected: number, received: ${value}` - ); - } - - acc[descriptor.key] = value; + acc[descriptor.key] = extractValue(message.body, descriptor.key); } return acc; diff --git a/src/widgets/graph-widget/lib/extract-value.ts b/src/widgets/graph-widget/lib/extract-value.ts new file mode 100644 index 0000000..c8b4b69 --- /dev/null +++ b/src/widgets/graph-widget/lib/extract-value.ts @@ -0,0 +1,31 @@ +import { JsonSerializable } from '@wuespace/telestion-client-types'; +import { isObj } from './utils'; + +export function extractValue(obj: JsonSerializable, key: string): number { + // replace [$1] with .$1 and split on . + const accessors = key.replace(/\[(\w+)\]/g, '.$1').split('.'); + + let reduce = obj; + + for (let i = 0; i < accessors.length; i++) { + if (!isObj(reduce)) { + throw new TypeError( + `Invalid message body received. (iteration: ${i}, accessor: ${ + accessors[i] + }, expected: object, received: ${JSON.stringify(reduce)}` + ); + } + + reduce = reduce[accessors[i]]; + } + + if (typeof reduce !== 'number') { + throw new TypeError( + `Invalid value received. (access expected: number, received: ${JSON.stringify( + reduce + )}` + ); + } + + return reduce; +} diff --git a/src/widgets/graph-widget/widget.tsx b/src/widgets/graph-widget/widget.tsx index 7e3e40c..cf40940 100644 --- a/src/widgets/graph-widget/widget.tsx +++ b/src/widgets/graph-widget/widget.tsx @@ -23,11 +23,19 @@ export function Widget({ return ( - - + + {title} - + + + + ); diff --git a/src/widgets/index.ts b/src/widgets/index.ts index 7745deb..58c7708 100644 --- a/src/widgets/index.ts +++ b/src/widgets/index.ts @@ -13,7 +13,7 @@ import { widget as graphWidget } from './graph-widget'; export const projectWidgets: Widget[] = [ // ARRAY_FIRST_ELEMENT_INSERT_MARK - graphWidget, + graphWidget as Widget, ninedof, sampleWidget ];