Skip to content
This repository has been archived by the owner on May 18, 2021. It is now read-only.

Commit

Permalink
feat(widget): Add deep object extraction and fix styles
Browse files Browse the repository at this point in the history
  • Loading branch information
fussel178 committed Mar 1, 2021
1 parent 8102c73 commit 696ef65
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 62 deletions.
118 changes: 83 additions & 35 deletions src/model/sample-user-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
]
}
Expand Down
10 changes: 8 additions & 2 deletions src/widgets/9dof/widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -44,7 +44,13 @@ export function Widget() {
<TableBody items={items}>
{(item: any) => (
<Row key={item.sensor + item.x + item.y + item.z}>
{(key: any) => <Cell>{item[key]}</Cell>}
{(key: any) => (
<Cell>
{typeof item[key] === 'number'
? Math.round(item[key] * 1000) / 1000
: item[key]}
</Cell>
)}
</Row>
)}
</TableBody>
Expand Down
12 changes: 8 additions & 4 deletions src/widgets/graph-widget/graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand All @@ -18,15 +19,17 @@ export interface GraphProps {
}

export function Graph({ data, descriptors, isArea }: GraphProps) {
const isDark = useDarkColorScheme();

return (
<ResponsiveContainer>
{isArea ? (
<AreaChart data={data}>{/* TODO: Implement area chart */}</AreaChart>
) : (
<LineChart data={data}>
<CartesianGrid strokeDasharray="4 4" />
<XAxis dataKey="time" />
<YAxis />
<XAxis dataKey="time" stroke={isDark ? '#e3e3e3' : '#4b4b4b'} />
<YAxis stroke={isDark ? '#e3e3e3' : '#4b4b4b'} />
<CartesianGrid strokeDasharray="4 8" />
<Tooltip />
<Legend />
{descriptors.map(descriptor => (
Expand All @@ -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}
/>
))}
</LineChart>
Expand Down
2 changes: 1 addition & 1 deletion src/widgets/graph-widget/hooks/use-callbacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
24 changes: 24 additions & 0 deletions src/widgets/graph-widget/hooks/use-dark-color-scheme.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
18 changes: 2 additions & 16 deletions src/widgets/graph-widget/lib/build-data-sample.ts
Original file line number Diff line number Diff line change
@@ -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[],
Expand All @@ -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;
Expand Down
31 changes: 31 additions & 0 deletions src/widgets/graph-widget/lib/extract-value.ts
Original file line number Diff line number Diff line change
@@ -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;
}
14 changes: 11 additions & 3 deletions src/widgets/graph-widget/widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,19 @@ export function Widget({

return (
<View width="100%" height="100%">
<Flex direction="column" width="100%" height="100%">
<Heading level={3} marginX="size-200">
<Flex
direction="column"
justifyContent="stretch"
width="100%"
height="100%"
>
<Heading flexGrow={0} level={3} marginX="size-200">
{title}
</Heading>
<Graph data={data} descriptors={descriptors} isArea={isArea} />
<View flexGrow={1} paddingEnd="size-200" overflow="hidden">
<Graph data={data} descriptors={descriptors} isArea={isArea} />
</View>
<View flexGrow={0} width="100%" height="size-200" />
</Flex>
</View>
);
Expand Down
2 changes: 1 addition & 1 deletion src/widgets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
];

0 comments on commit 696ef65

Please sign in to comment.