Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Anemometer functionality to be displayed on the dashboard #169

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions client/src/api/common/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export enum Sensor {
SteeringAngle = 'steeringAngle',
CO2 = 'co2',
Accelerometer = 'accelerometer',
WindSpeed = 'windSpeed',
windDirection = 'windDirection',
Gyroscope = 'gyroscope',
ReedVelocity = 'reedVelocity',
ReedDistance = 'reedDistance',
Expand Down
16 changes: 16 additions & 0 deletions client/src/components/v3/status/AnemometerStatus.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { addArgs, createStory } from 'utils/stories';
import { SensorDataT, SensorsT } from 'types/data';
import WMStatus, { WMStatusProps } from './WMStatus';

export default {
component: WMStatus,
title: 'components/v3/status/WirelessModuleStatus',
};

const Template = addArgs<WMStatusProps>((props) => <WMStatus {...props} />);

const sensorData = (type: string, value: SensorsT): SensorDataT => ({
type,
value,
});
218 changes: 218 additions & 0 deletions client/src/components/v3/status/AnemometerStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import React from 'react';
import { Accordion, Button, Card, Col, Table } from 'react-bootstrap';
import OnlineStatusPill from 'components/common/OnlineStatusPill';
import { WMStatus as WMStatusT } from 'types/data';
import { isOnline, roundNum } from 'utils/data';
import { camelCaseToStartCase } from 'utils/string';

export type WMStatusProps = WMStatusT;

export default function WMStatus(props: WMStatusProps) {
const { moduleName, online } = props;

const statusPill = (
<span>
<b>{moduleName}</b> <OnlineStatusPill isOnline={online} />
</span>
);

function extractData(type: string, data: any) {
interface strMap {
[key: string]: string;
}
interface numMap2 {
[key: string]: number;
}

const units: strMap = {
speed: 'km/h',
satellites: '',
pdop: '',
latitude: '°N',
longitude: '°E',
altitude: 'm',
course: '°',
datetime: '',
temperature: '°C',
humidity: '%',
steeringAngle: '°',
co2: 'ppm',
power: 'W',
cadence: 'rpm',
heartRate: 'bpm',
reedVelocity: 'km/h',
reedDistance: 'km',
antSpeed: 'km/h',
antDistance: 'km',
};

const decimals: numMap2 = {
speed: 2,
satellites: 0,
pdop: 2,
latitude: 5,
longitude: 5,
altitude: 1,
course: 1,
temperature: 1,
humidity: 0,
steeringAngle: 1,
co2: 0,
power: 0,
cadence: 0,
heartRate: 0,
reedVelocity: 2,
reedDistance: 2,
antSpeed: 2,
antDistance: 2,
x: 2,
y: 2,
z: 2,
};

/**
* receives a value and its unit and format them appropriately
*
* @param name type's name
* @param value type's value
* @param unit the unit
* @returns string containing the value and its unit
*/
function formatValue(name: string, value: any, unit: any) {
let displayValue;
let val = value;
if (val !== null && val !== undefined) {
if (name === 'Date' || name === 'Time') {
displayValue =
name === 'Date' ? value.substring(0, 10) : value.substring(11, 19);
} else {
const dec = decimals[name];
if (unit === 'km') {
val /= 1000;
} else if (unit === 'km/h') {
val *= 3.6;
}
displayValue = roundNum(val, dec);
}
} else {
displayValue = '-';
}
const displayUnit = unit ? ` ${unit}` : '';

return `${displayValue}${displayUnit}`;
}

let output = <></>;
if (type === 'anemometer') {
const anemRows: any[] = [];
Object.entries(data).forEach((arr) => {
anemRows.push({ name: arr[0], value: arr[1] });
console.log(data);
});
output = (
<Table borderless>
<tbody>
{anemRows.map(({ name, value }) => (
<tr
key={name}
style={{
width: '150px',
borderBottomWidth: 1,
borderBottomColor: 'gray',
borderBottomStyle: 'solid',
}}
>
<td>{camelCaseToStartCase(name.toLowerCase())}</td>
<td>
<div style={{ float: 'right' }}>
{formatValue(name, value, '')}
</div>
</td>
</tr>
))}
</tbody>
</Table>
);
} else {
output = (
<div style={{ float: 'right' }}>
{formatValue(type, JSON.stringify(data), units[type])}
</div>
);
}
return output;
}

let info = <> </>;

if (isOnline(props)) {
const { data, batteryVoltage } = props;
info = (
<>
<Table hover>
<tbody>
{/* Battery Voltage */}
<tr>
<td>
<strong>Battery Voltage</strong>
</td>
<td>{batteryVoltage ? batteryVoltage.toFixed(2) : '-'} V</td>
</tr>

{/* Sensors List of Names */}
<tr>
<td>
<strong>Sensors</strong>
</td>
<td>
{data
.map(({ type }) => camelCaseToStartCase(type.toLowerCase()))
.join(', ')}
</td>
</tr>
</tbody>
</Table>

{/* Sensor Data Toggle Section */}
<Accordion className="mt-2">
<Card>
<Accordion.Toggle
as={Button}
variant="outline-success"
eventKey="0"
>
Sensor Data
</Accordion.Toggle>
<Accordion.Collapse eventKey="0">
<Card.Body>
<Table bordered hover>
<tbody>
{data.map(({ type, value }) => (
<tr key={`${moduleName} ${type}`}>
<td>
<strong>
{camelCaseToStartCase(type.toLowerCase())}
</strong>
</td>
<td>{extractData(type, value)}</td>
</tr>
))}
</tbody>
</Table>
</Card.Body>
</Accordion.Collapse>
</Card>
</Accordion>
</>
);
}

return (
<Col md xl="12" className="my-2">
{statusPill}

{/* Only show more information if the anemometer is online */}
{info}
</Col>
);
}
30 changes: 30 additions & 0 deletions client/src/components/v3/status/AnemometerStatusContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { Card, Row } from 'react-bootstrap';

import AnemometerStatus from 'components/v3/status/AnemometerStatus';
import { useModuleStatus } from 'api/common/data';

/**
* Container for Anemometer Statuses
*
* @returns Component
*/
export default function AnemometerStatusContainer() {
const speed = useModuleStatus(1, 'Wind Speed');
const direction = useModuleStatus(3, 'Wind Direction');

return (
<Card>
<Card.Body>
<Card.Title>Anemometer</Card.Title>
<Row>
{/* Front WM Status */}
<AnemometerStatus {...speed} />

{/* Back WM Status */}
<AnemometerStatus {...direction} />
</Row>
</Card.Body>
</Card>
);
}
29 changes: 29 additions & 0 deletions client/src/components/v3/status/WMStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,35 @@ export default function WMStatus(props: WMStatusProps) {
</tbody>
</Table>
);
} else if (type === 'anemometer') {
const anemRows: any[] = [];
Object.entries(data).forEach((arr) => {
anemRows.push({ name: arr[0], value: arr[1] });
});
output = (
<Table borderless>
<tbody>
{anemRows.map(({ name, value }) => (
<tr
key={name}
style={{
width: '150px',
borderBottomWidth: 1,
borderBottomColor: 'gray',
borderBottomStyle: 'solid',
}}
>
<td>{camelCaseToStartCase(name.toLowerCase())}</td>
<td>
<div style={{ float: 'right' }}>
{formatValue(name, value, '')}
</div>
</td>
</tr>
))}
</tbody>
</Table>
);
} else if (type === 'gyroscope') {
const gyroRows: any[] = [];
Object.entries(data).forEach((arr) => {
Expand Down
12 changes: 12 additions & 0 deletions client/src/types/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ export const GyroscopeRT = Record({
z: Number,
});

export const AnemometerRT = Record({
/** Wind Speed value */
windSpeed: Number,

/** Wind Direction value */
windDirection: Number,
});

/** Value runtype of reedVelocity sensor data */
export const ReedVelocityRT = Number;

Expand Down Expand Up @@ -92,6 +100,7 @@ export const SensorsRT = Union(
PowerRT,
CadenceRT,
HeartRateRT,
AnemometerRT,
);

/** Sensor data as incoming from MQTT */
Expand All @@ -117,6 +126,9 @@ export type CO2T = Static<typeof CO2RT>;
/** Value type of accelerometer sensor data */
export type AccelerometerT = Static<typeof AccelerometerRT>;

/** Value type of aneomometer sensor data */
export type AnemometerT = Static<typeof AnemometerRT>;

/** Value type of gyroscope sensor data */
export type GyroscopeT = Static<typeof GyroscopeRT>;

Expand Down
7 changes: 7 additions & 0 deletions client/src/views/v3/StatusView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Row, Col } from 'react-bootstrap';
import ContentPage from 'components/common/ContentPage';
import CameraStatusContainer from 'components/v3/status/CameraStatusContainer';
import WMStatusContainer from 'components/v3/status/WMStatusContainer';
import AnemometerStatusContainer from 'components/v3/status/AnemometerStatusContainer';

/**
* Status View component
Expand All @@ -23,6 +24,12 @@ export default function StatusView(): JSX.Element {
<WMStatusContainer />
</Col>
</Row>
<Row>
{/* Anemometer Status */}
<Col xl className="mb-2">
<AnemometerStatusContainer />
</Col>
</Row>
</ContentPage>
);
}