implementation
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2023-03-10 20:55:40 +01:00
parent f81e2c9324
commit 7e67950966
14 changed files with 720 additions and 27 deletions

View File

@@ -1,10 +1,16 @@
import express, { NextFunction, Request, Response } from 'express';
import nodesched from 'node-schedule';
import morgan from 'morgan';
import router from './router';
import wxService from './services/wx.service';
const { PORT = 3000 } = process.env;
const app = express();
app.set('trust proxy', true);
app.use(morgan('combined'));
app.use('/api', router.router);
const frontendRoot = '/opt/frontend/dist';
@@ -19,6 +25,9 @@ app.use((err, req: Request, res: Response, next: NextFunction) => {
res.status(500).json({ msg: 'an error occurred' });
});
nodesched.scheduleJob('regenerate data', '*/30 * * * * *', wxService.wrappedGenerateData)
wxService.wrappedGenerateData();
app.listen(PORT, () => {
console.log(
`application is listening on port ${PORT}`,

View File

@@ -1,14 +0,0 @@
import { Request, Response } from 'express';
import fooService from '../services/foo.service';
function getFoo(req: Request, res: Response) {
res.json({
foo: true,
msg: fooService.getFooDetails(),
data: req.body,
});
}
export default {
getFoo,
};

View File

@@ -0,0 +1,31 @@
import express from 'express';
import regionsService from '../services/regions.service';
export async function getRegions(req: express.Request, res: express.Response, next: express.NextFunction) {
try {
res.json(regionsService.getRegions());
} catch (e) {
next(e);
}
}
export async function getRegion(req: express.Request, res: express.Response, next: express.NextFunction) {
try {
const { region } = req.params;
const regionData = regionsService.getRegion(region);
if(!regionData) {
return next();
}
res.json(regionData);
} catch (e) {
next(e);
}
}
export default {
getRegions,
getRegion,
};

View File

@@ -0,0 +1,15 @@
import express from 'express';
import wxService from '../services/wx.service';
export async function getRegionWx(req: express.Request, res: express.Response, next: express.NextFunction) {
try {
const { region } = req.params;
res.json(wxService.getWx(region));
} catch (e) {
next(e);
}
}
export default {
getRegionWx,
};

View File

@@ -1,10 +1,13 @@
import { Request, Response, Router } from 'express';
import fooController from './controllers/foo.controller';
import regionController from './controllers/region.controller';
import wxController from './controllers/wx.controller';
const router = Router();
router.get('/foo', fooController.getFoo);
// aaaaa
router.get('/regions', regionController.getRegions);
router.get('/regions/:region', regionController.getRegion);
router.get('/regions/:region/wx', wxController.getRegionWx);
router.use((req: Request, res: Response) => {
// 404

View File

@@ -0,0 +1,26 @@
import fs from 'fs';
export interface WxConfig {
regions: WxRegion[];
}
export interface WxRegion {
identifier: string;
fixes: WxFix[];
}
export interface WxFix {
name: string;
lat: number;
lon: number;
}
export function getConfig(): WxConfig {
const data = JSON.parse(fs.readFileSync('/opt/wx-config.json').toString());
return data;
}
export default {
getConfig,
}

View File

@@ -1,7 +0,0 @@
function getFooDetails() {
return 'foo';
}
export default {
getFooDetails,
};

View File

@@ -0,0 +1,18 @@
import configService, { WxConfig, WxRegion } from "./config.service";
export function getRegions(): WxConfig["regions"] {
return configService.getConfig().regions;
}
export function getRegion(identifier: string): WxRegion | undefined {
const regions = getRegions();
const region = regions.find(rg => rg.identifier == identifier);
return region;
}
export default {
getRegions,
getRegion,
};

View File

@@ -0,0 +1,134 @@
import axios from "axios";
import { WxFix } from "./config.service";
import regionsService from "./regions.service";
const cachedData: {[key: string]: WxData} = {};
const qnhLevelMapping = {
200: 390,
250: 340,
300: 300,
400: 240,
500: 180,
600: 140,
700: 100,
850: 50
};
const necessaryDatapoints = [
'winddirection',
'windspeed',
'temperature',
];
const requestedData: string[] = [
'temperature_2m',
'windspeed_10m',
'winddirection_10m',
];
for (const qnh of Object.keys(qnhLevelMapping)) {
for (const necessaryDatapoint of necessaryDatapoints) {
requestedData.push(`${necessaryDatapoint}_${qnh}hPa`);
}
}
interface WxLevelData {
"T(K)": string;
windspeed: string;
windhdg: string;
};
export interface WxFixData {
coords: {
lat: string;
long: string;
};
levels: {
[key: string]: WxLevelData;
}
}
export interface WxData {
info: {
date: string;
datestring: string;
};
data: {
[key: string]: WxFixData;
}
}
export async function getDataAtFix(fix: WxFix, index: number): Promise<WxFixData> {
const response = await axios.get(`https://api.open-meteo.com/v1/forecast?latitude=${fix.lat}&longitude=${fix.lon}&windspeed_unit=kn&forecast_days=1&hourly=${requestedData.join(',')}`);
const hourlyData = response.data.hourly;
const data: WxFixData = {
coords: {
lat: String(fix.lat),
long: String(fix.lon)
},
levels: {}
}
data.levels["0"] = {
"T(K)": String(Number(hourlyData?.[`temperature_2m`]?.[index]) + 273.15),
"windspeed": String(hourlyData?.[`windspeed_10m`]?.[index]),
"windhdg": String(hourlyData?.[`winddirection_10m`]?.[index]),
};
for(const [qnh, fl] of Object.entries(qnhLevelMapping)) {
const temp = Number(hourlyData?.[`temperature_${qnh}hPa`]?.[index]) + 273.15;
const dir = hourlyData?.[`windspeed_${qnh}hPa`]?.[index];
const speed = hourlyData?.[`winddirection_${qnh}hPa`]?.[index];
data.levels[String(fl)] = {
"T(K)": String(temp),
"windspeed": String(speed),
"windhdg": String(dir),
}
}
return data;
}
export async function generateData() {
const regions = regionsService.getRegions();
for (const region of regions) {
const now = new Date();
const regionData: WxData = {
info: {
date: now.toISOString(),
datestring: `${now.getUTCDate()}${now.getUTCHours()}`,
},
data: {}
}
for (const fix of region.fixes) {
regionData.data[fix.name] = await getDataAtFix(fix, now.getUTCHours());
}
cachedData[region.identifier] = regionData;
}
return cachedData;
}
export function wrappedGenerateData() {
generateData().catch((...params) => console.error(...params));
}
export function getWx(region: string): WxData | null {
const data = cachedData[region];
return data;
}
export default {
getWx,
generateData,
wrappedGenerateData,
}