Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…into main
  • Loading branch information
randomman552 committed May 5, 2022
2 parents 7d7861f + 9818735 commit df3101a
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 123 deletions.
95 changes: 3 additions & 92 deletions api/src/api/middleware/devices.ts
Original file line number Diff line number Diff line change
@@ -1,100 +1,11 @@
import { NextFunction, Request, Response } from "express";
import { Filter } from "mongodb";
import { DEVICE_COLLECTION_PREFIX } from "../constants";
import client from "../database";
import { Device as DeviceInterface, Position } from "../types";

/**
* Device class encapsulating all operations related to devices.
* Includes getters and setters to abstract away interaction with the database
*/
export class Device implements DeviceInterface {
public name: string
public position: Position

constructor(obj: DeviceInterface) {
this.name = obj.name;
this.position = obj.position;
}

get collection() {
const collectionName = `${DEVICE_COLLECTION_PREFIX}_${this.name}`;
return client.db().collection(collectionName);
}

async getData() {
return this.collection
.findOne(
{},
{
limit: 1,
sort: {
timestamp: -1
},
projection: {
_id: 0
}
}
)
}

/**
* Convinience method to get the history for the given device.
* @param from OPTIONAL: Start of time query range (in UNIX time)
* @param to OPTIONAL: End of time query range (in UNIX time)
* @returns Promise of history from this device
*/
async getHistory(from: number=0, to: number=Number.MAX_SAFE_INTEGER) {
const filter: Filter<Device> = {
timestamp: {
"$gte": from,
"$lte": to
}
}

return this.collection
.find(
filter,
{
limit: 10000,
sort: {
timestamp: -1
},
projection: {
_id: 0
}
}
)
.toArray();
}

async addData(data: any) {
data.timestamp = Date.now();
// Create index if it doesn't already exist
if (!(this.collection.indexExists("timestamp")))
this.collection.createIndex({timestamp: 1}, {name: "timestamp"});

return this.collection.insertOne(data);
}

async deleteData() {
return this.collection.drop();
}
}



// Required for non optional additions to 'Request'
declare module 'express-serve-static-core' {
interface Request {
device: Device
}
}
import { Device } from "../types";


// Middleware
/**
* Gets device details by deviceName parameter and adds it to the Request object.
* Gets device details by deviceName parameter and stores them in Response.locals.device
*/
export const deviceMiddleware = async (req: Request, res: Response, next: NextFunction) => {
const deviceName = req.params?.deviceName;
Expand All @@ -120,6 +31,6 @@ export const deviceMiddleware = async (req: Request, res: Response, next: NextFu
}

// Convert device to an object of the Device class
req.device = new Device(device);
res.locals.device = device;
next();
}
2 changes: 1 addition & 1 deletion api/src/api/middleware/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { deviceMiddleware, Device } from "./devices"
export { deviceMiddleware } from "./devices"
export { queryMiddleware } from "./query"
export { errorHandler, consoleLogErrors, mongodbLogErrors } from "./errors"
export { apiKeyAuth } from "./auth"
70 changes: 60 additions & 10 deletions api/src/api/routers/devices.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import express, { Request, Response } from "express";
import { Filter } from "mongodb";
import { DEVICE_COLLECTION_PREFIX } from "../constants";
import client from "../database";
import { deviceMiddleware, Device, apiKeyAuth } from "../middleware";
import { deviceMiddleware, apiKeyAuth } from "../middleware";
import { Device } from "../types";

export const router = express.Router()

function getDeviceCollection(deviceName: string) {
return client.db().collection(`${DEVICE_COLLECTION_PREFIX}_${deviceName}`)
}

// Define handlers
/**
* Endpoint handler to return a the details of all devices
Expand All @@ -25,7 +32,7 @@ const listDevices = async (req: Request, res: Response) => {
* Methods: GET
*/
const getDevice = async (req: Request, res: Response) => {
const device = req.device;
const device = res.locals.device;
res.status(200).json(device);
}

Expand All @@ -35,8 +42,22 @@ const getDevice = async (req: Request, res: Response) => {
* Methods: GET
*/
const getData = async (req: Request, res: Response) => {
const device = req.device;
const data = await device.getData();
const device = res.locals.device;

// Query database for most recent data entry
const collection = getDeviceCollection(device.name);
const data = await collection.findOne(
{},
{
limit: 1,
sort: {
timestamp: -1
},
projection: {
_id: 0
}
}
)

res.status(200).json(data);
}
Expand All @@ -47,13 +68,34 @@ const getData = async (req: Request, res: Response) => {
* Methods: GET
*/
const getHistoricalData = async (req: Request, res: Response) => {
const device = req.device;
const device = res.locals.device;

// Get query parameters
const from = Number(req.query.from) || 0;
const to = Number(req.query.to) || Date.now();

const history = await device.getHistory(from, to);
// Build timestamp filter
const filter: Filter<Device> = {
timestamp: {
"$gte": from,
"$lte": to
}
}

// Query data history
const collection = getDeviceCollection(device.name);
const history = await collection.find(
filter,
{
limit: 10000,
sort: {
timestamp: -1
},
projection: {
_id: 0
}
}
).toArray();

res.status(200).json(history);
}
Expand All @@ -64,10 +106,17 @@ const getHistoricalData = async (req: Request, res: Response) => {
* Methods: PUT
*/
const addData = async (req: Request, res: Response) => {
const device = req.device;
const device = res.locals.device;
const data = req.body;
data.timestamp = Date.now();

const collection = getDeviceCollection(device.name);
// Create index if it doesn't already exist
if (!(collection.indexExists("timestamp")))
collection.createIndex({timestamp: 1}, {name: "timestamp"});

await collection.insertOne(data);

device.addData(data)
res.status(202).json();
}

Expand All @@ -77,8 +126,9 @@ const addData = async (req: Request, res: Response) => {
* Methods: DELETE
*/
const deleteData = async (req: Request, res: Response) => {
const device = req.device;
await device.deleteData()
const device = res.locals.device;
const collection = getDeviceCollection(device.name);
await collection.drop();
res.status(200).json();
}

Expand Down
12 changes: 7 additions & 5 deletions api/src/api/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@ export enum LogLevel {

export interface APIError extends Error {
code: number,
timestamp?: number,
level?: LogLevel,
timestamp?: number
level?: LogLevel
url?: string
}


export interface Position {
x: number,
y: number,
x: number
y: number
z: number
}

export interface Device {
name: string,
name: string
type: string
floor: number
position: Position
}
2 changes: 1 addition & 1 deletion visualiser/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { useAPI, useAPISubscription } from "./useAPI"
export { useDevices, useDevice, useDeviceData, useDeviceHistory } from "./useDevice"
export { useDevices, useDeviceInfo, useDeviceData, useDeviceHistory } from "./useDevice"
export type { Device, Data, Position } from "./useDevice"
4 changes: 3 additions & 1 deletion visualiser/src/hooks/useDevice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ export interface Position {

export interface Device {
name: string
type: string
position: Position
floor: number
}

export interface Data extends Object {
Expand All @@ -33,7 +35,7 @@ export function useDevices() {
* @param deviceName The name of the device to query
* @returns
*/
export function useDevice(deviceName: string|undefined) {
export function useDeviceInfo(deviceName: string|undefined) {
const url = `/api/devices/${deviceName}`;
return useAPI<Device>(url)?.body;
}
Expand Down
4 changes: 4 additions & 0 deletions visualiser/src/styles/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ code {
.text-capitalize {
text-transform: capitalize;
}

p {
margin: 0;
}
6 changes: 3 additions & 3 deletions visualiser/src/views/DataView/DataView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "./DataView.scss"
import { DeviceDetails, FloorSelector, GraphContainer, GraphOptions } from "./components";
import { useDeviceData, useDeviceHistory } from "../../hooks";
import { useDeviceData, useDeviceHistory, useDeviceInfo } from "../../hooks";
import { useState } from "react";
import { useSelectedDevice, useSelectedFloor } from "../../three";

Expand All @@ -13,7 +13,7 @@ export function DataView({ hidden }: DataViewProps) {
const deviceName = useSelectedDevice();
const [floor, setFloor] = useSelectedFloor();


const deviceInfo = useDeviceInfo(deviceName);
const deviceData = useDeviceData(deviceName);
const deviceHistory = useDeviceHistory(deviceName);

Expand All @@ -34,7 +34,7 @@ export function DataView({ hidden }: DataViewProps) {

<DeviceDetails
onViewHistory={(deviceName, field) => { setGraphOptions({deviceName, field}) }}
deviceName={deviceName}
device={deviceInfo}
data={deviceData}
/>

Expand Down
Loading

0 comments on commit df3101a

Please sign in to comment.