diff --git a/actions/data4map.js b/actions/data4map.js index 1051e0f..d21aeda 100644 --- a/actions/data4map.js +++ b/actions/data4map.js @@ -4,11 +4,31 @@ import {logit} from "../utilities/logit.js" import * as mongo from "../databases/mongo.js" import {reportError, returnOnError} from "../utilities/reporterror.js" import * as ERR from "../utilities/errortexts.js" +import checkParams from "../utilities/checkparams.js"; +import {fetchFromInflux} from "./getsensorData.js"; +import {NOPROPSFOUND} from "../utilities/errortexts.js"; // Default distance for center search ( in km) const DEFAULT_DISTANCE = 10 +// Value to use fpr map +const value4map = [ + { typ: 'noise', value: 'noise_LA_max'}, + { typ: 'pm', value: 'P1'}, + { typ: 'thp', value: 'temperature'}, + { typ: 'radioactivity', value: 'counts_per_minute'}, +] + +// get value for map from tabel +const getValue4Map = (t) => { + for(let x of value4map) { + if(x.typ == t) { + return x.value + } + } + return '' +} // Relations between types and value type const vtype2measurement = { @@ -19,6 +39,7 @@ const vtype2measurement = { }; + // find first value type from measurement const getfieldfromtype = (typ) => { for (const [key, value] of Object.entries(vtype2measurement)) { @@ -30,27 +51,42 @@ const getfieldfromtype = (typ) => { } +// read the last entries from the influx database +const readLastDates = async (typ) => { + let ret = {values: [], err: null} + let query = ` + from(bucket: "sensor_data") +|> range(start: -2h) +|> filter(fn: (r) => r._measurement == "${typ}" and r._field == "${getValue4Map(typ)}") +|> last() +|> group() +|> map(fn: (r) => ({r with sid: int(v: r.sid)})) +|> sort(columns: ["sid"]) +|> keep(columns: ["_time","sid","_value"]) +` + return await fetchFromInflux(ret, query) +} + export const getData4map = async (params) => { let start = DateTime.now() - let retur = { err: null, data: { params: params} } + let ret = {err: null} + + // ***** This function will (at the moment) only be called by internal routines, so there is no need to check the parameters ! - // check parameters - if ((params.type === undefined) || (params.type === '')) { - return returnOnError(retur, ERR.NOTYP, getData4map.name) - } const typ = params.type let poly = [] let south = null, north = null, east = null, west = null, center = null let distance = DEFAULT_DISTANCE - if (!((params.box === undefined) || (params.box == ""))) { - const box = params.box - if (!((box === undefined) || (box === ' ') || (box === 'null'))) { - south = parseFloat(box.south) - north = parseFloat(box.north) - east = parseFloat(box.east) - west = parseFloat(box.west) - logit(`getData4map: S=${south} N=${north} E=${east} W=${west}`) + if(params.box !== undefined) { + let val = params.box.split(',') + for (let i = 0; i < val.length; i++) { + val[i] = parseFloat(val[i]) } + south = parseFloat(val[1]) + north = parseFloat(val[3]) + east = parseFloat(val[2]) + west = parseFloat(val[0]) + logit(`getData4map: S=${south} N=${north} E=${east} W=${west}`) } if (!((params.poly === undefined) || (params.poly === ' '))){ poly = JSON.parse(params.poly) @@ -96,43 +132,55 @@ export const getData4map = async (params) => { } } } - // depending of the type find the measurement field to fetch - const vtype = getfieldfromtype(typ) - - // fetch mapdata from mongodb try { - let { values, err } = await mongo.readMapdata(query, 0) + // fetch mapdata from mongodb + let { properties, err } = await mongo.getallProperties(mongo.properties_collection, query) if(err) { - return returnOnError(retur, ERR.NOSENSFOUND, getData4map.name) + return returnOnError(ret, ERR.NOPROPSFOUND, getData4map.name) } - for (let item of values) { + let v4map = getValue4Map(typ) + for (let sensor of properties) { + let id = sensor._id const oneAktData = { - location: item.location.loc.coordinates, - id: item._id, - name: item.name, - indoor: item.location.indoor, - value: Math.round(item.value[vtype] * 100) / 100 + location: sensor.location[0].loc.coordinates, + id: sensor._id, + name: sensor.name[0].name, + indoor: sensor.location[0].indoor, + lastseen: sensor.values.timestamp } let now = new Date().getTime() - let diff = now - item.timestamp - if (diff >= 7 * 24 * 3600 * 1000) { - oneAktData.value = -2 - } else if (diff >= 2 * 3600 * 1000) { - oneAktData.value = -1 + oneAktData.value = -1 + if(oneAktData.lastseen !== '') { + let diff = now - oneAktData.lastseen.getTime() + if (diff >= 365 * 24 * 3600 * 1000) { + oneAktData.value = -4 + } else if (diff >= 30 * 24 * 3600 * 1000) { + oneAktData.value = -3 + } else if (diff >= 7 * 24 * 3600 * 1000) { + oneAktData.value = -2 + } else if (diff >= 2 * 3600 * 1000) { + oneAktData.value = -1 + } else { + if (sensor.values !== undefined) { + oneAktData.value = Math.round(sensor.values[v4map] * 100) / 100 + } + } } - if (item.timestamp > lastDate) { - lastDate = item.timestamp + if (sensor.values.timestamp > lastDate) { + lastDate = sensor.values.timestamp } aktData.push(oneAktData) } logit(`Query duration: ${start.diffNow('seconds').toObject().seconds * -1} sec`) - retur.data.lastDate = lastDate - retur.data.valuetype = vtype - retur.data.count = aktData.length - retur.data.values = aktData - return retur + ret = { + err: null, + lastdate: lastDate, + count: aktData.length, + values: aktData + } + return ret } catch(e) { - return returnOnError(retur, `catch\n${e}`, getData4map.name) + return returnOnError(ret, `catch\n${e}`, getData4map.name) } } diff --git a/actions/getproperties.js b/actions/getproperties.js index 37804c6..920053c 100644 --- a/actions/getproperties.js +++ b/actions/getproperties.js @@ -14,6 +14,8 @@ if (mockdb) { } else { readProperties = mongo.readProperties } + +// Read properties for sensorid and properties for all other sensors on same location export const getOneProperty = async (params) => { let properties = {props: {}, err: null} let {opts, err} = checkParams(params, {mandatory:[{name:'sensorid', type: 'int'}], optional:[]}) @@ -22,31 +24,25 @@ export const getOneProperty = async (params) => { } let sensorEntries = []; try { - let pp = await readProperties({sid: opts.sensorid}); - if ((pp.values == null) || (pp.error)) { + let pp = await readProperties({sid: opts.sensorid}); // read for given sensorID + if ((pp.properties == null) || (pp.error)) { return returnOnError(properties, ERR.NOPROPSREAD.replace('xx', opts.sensorid), getOneProperty.name) } // now find sensors with same location - let query - if (pp.values.location_id !== undefined) { - query = {location_id: pp.values.location_id} - } else { - query = {id: pp.values.id} - } + let query = {"location.0.id": pp.properties.location[0].id} let others = await readProperties(query) if (others.error) { return returnOnError(properties, ERR.NOPROPSREAD.replace('xx',others.errortext), getOneProperty.name) } - if (others.values.length > 0) { - for (const x of others.values) { - if(x.name[0].name === undefined) { - sensorEntries.push({name: x.name, sid: x._id}) - } else { - sensorEntries.push({name: x.name[0].name, sid: x._id}) + if (others.properties.length > 0) { + for (const x of others.properties) { + if(x._id === pp.properties._id) { + continue } + sensorEntries.push({name: x.name[0].name, sid: x._id}) } } - properties.props = pp.values + properties.props = pp.properties properties.props.othersensors = sensorEntries; } catch (e) { return returnOnError(properties, e, getOneProperty.name) diff --git a/actions/getsensorData.js b/actions/getsensorData.js index 3254387..a54756a 100644 --- a/actions/getsensorData.js +++ b/actions/getsensorData.js @@ -2,13 +2,14 @@ import {DateTime} from "luxon" import * as influx from "../databases/influx.js" +import * as mongo from "../databases/mongo.js" import {returnOnError} from "../utilities/reporterror.js" import {csv2Json} from "../utilities/csv2json.js" -import checkParams from "../utilities/checkparams.js"; +import checkParams from "../utilities/checkparams.js" import * as ERR from "../utilities/errortexts.js" -import {getOneProperty} from "./getproperties.js"; -import {getNoiseData} from "../sensorspecials/noise.js"; -import {getRadioData} from "../sensorspecials/radioact.js"; +import {getOneProperty} from "./getproperties.js" +import {getNoiseData} from "../sensorspecials/noise.js" +import {getRadioData} from "../sensorspecials/radioact.js" // Possible params for the different sensor types const noiseParams = [ @@ -94,14 +95,14 @@ export const calcRange = (opts) => { // ********************************************* // getSensorData // -// Depending on the parameter 'sensorid', ditribute to the speciell routines for +// Depending on the parameter 'sensorid', distribute to the special routines for // the sensors // // params: // params: all parameters from the url // // return: -// Retirns from the special routines +// Returns from the special routines // ********************************************* export const getSensorData = async (params) => { let ret = {err: null} @@ -110,18 +111,18 @@ export const getSensorData = async (params) => { optional: [] }) if (err) { - return returnOnError(ret, err, getSensordata.name) + return returnOnError(ret, err, getSensorData.name) } // with the sensorid get the type of this sensor - let {props, err1} = await getOneProperty({sensorid: opts.sensorid}) - if (err1) { + let erg = await getOneProperty({sensorid: opts.sensorid}) + if (erg.err) { return returnOnError(ret, err1, getSensorData.name) } // distribute to the right routine for(let item of sensorTypeTable) { - if(item.typ === props.type) { - ret = item.func(params, item.possibleParams, props) // get the values from database + if(item.typ === erg.props.type) { + ret = item.func(params, item.possibleParams, erg.props) // get the values from database return ret } } @@ -131,8 +132,8 @@ export const getSensorData = async (params) => { - export const getActData = async (opts) => { - let ret = {data: {count: 0, values: []}, err: null} + export const getActData = async (opts, props) => { + let ret = {values: [], props: props, err: null} let sorting = '' if(opts.sort) { if (opts.sort === 1) { @@ -225,12 +226,11 @@ export const getLongAvg = async (params) => { |> drop(columns: ["_start","_measurement", "sid"]) |> pivot(rowKey:["_stop"], columnKey: ["_field"], valueColumn: "_value") ` - return fetchFromInflux(ret, query) + return await fetchFromInflux(ret, query) } -export const fetchFromInflux = async (data, query) => { - let ret = {err: null} +export const fetchFromInflux = async (ret, query) => { let { values, err} = await influx.influxRead(query) if(err) { if(err.toString().includes('400')) { @@ -240,8 +240,77 @@ export const fetchFromInflux = async (data, query) => { } } if (values.length === 0) { - return returnOnError(data, ERR.NODATA, fetchFromInflux.name) + return returnOnError(ret, ERR.NODATA, fetchFromInflux.name) } ret.values = csv2Json(values) return ret } + +// ********************************************* +// function getMAPaktData() { +// +// Get the actual data from the database within the bounds for the given sensor typ +// +// params: +// opt: opt.box => region for which to find sensor data +// opt.typ: type of sensor +// +// return +// JSON +// { avgs: [ +// { location: [ 9.00, 48.80 ], id: 29174, lastSeen: "2019-10-23T12:03:00.000Z", noise_max: "65" } +// { location: [ 9.10, 49.80 ], id: 28194, lastSeen: "2019-10-22T16:03:00.000Z", noise_max: "45" } +// ......... +// ], lastDate: "2019-10-29T15:05:59.000Z" } +// +// ********************************************* +export const getMAPaktData = async (opt) => { + let box = opt.box + if ((box == "") || (box == undefined)) { + return {"avgs": [], "lastDate": null} + } + let south = parseFloat(box[0][1]) + let north = parseFloat(box[1][1]) + let east = parseFloat(box[1][0]) + let west = parseFloat(box[0][0]) + let aktData = [] + let lastDate = 0 + let loc = { + location: { + $geoWithin: { + $box: [ + [west, south], + [east, north] + ] + } + }, + typ: opt.typ, + } + let docs = await mongo.readMapdata(loc,0) +// console.log(docs) + for (let i = 0;i < docs.length; i++) { + let item = docs[i] + let oneAktData = {} + oneAktData['location'] = item.location.coordinates + oneAktData['id'] = item._id // ID des Sensors holen + oneAktData['lastSeen'] = item.values.datetime + oneAktData['indoor'] = item.indoor + let dati = item.values.datetime + let dt = new Date(dati) + if ((now - dt) >= 31 * 24 * 3600 * 1000) { // älter als 1 Monat -> + oneAktData['noise_max'] = -2 // -2 zurückgeben + } else if ((now - dt) >= 3600 * 1000) { // älter als 1 Stunde -> + oneAktData['noise_max'] = -1 // -1 zurückgeben + } else { + oneAktData['noise_max'] = -5 // bedutet -> nicht anzeigen + if (item.values.hasOwnProperty('noise_LA_max')) { + oneAktData['noise_max'] = item.values.noise_LA_max.toFixed(0) // und merken + } + if (dati > lastDate) { + lastDate = dati + } + } + aktData.push(oneAktData) // dies ganzen Werte nun in das Array + } + return {"avgs": aktData, "lastDate": lastDate} // alles bearbeitet _> Array senden +} diff --git a/bin/www.js b/bin/www.js index 76d519e..dfea5c6 100644 --- a/bin/www.js +++ b/bin/www.js @@ -13,7 +13,7 @@ import http from 'http' * Get port from environment and store in Express. */ -const port = normalizePort(process.env.PORT || '3000'); +const port = normalizePort(process.env.PORT || '3004'); app.set('port', port); /** diff --git a/databases/mongo.js b/databases/mongo.js index c9034e0..203f6fd 100644 --- a/databases/mongo.js +++ b/databases/mongo.js @@ -23,6 +23,8 @@ if (MONGOAUTH === 'true') { MONGO_URL = 'mongodb://'+MONGOUSRP+'@' + MONGOHOST + ':' + MONGOPORT + '/?authSource=' + MONGOBASE; // URL to mongo database } +export const properties_collection = 'pptest' + let statistics = {} let client @@ -56,13 +58,13 @@ const listDatabases = async (client) => { // Read properties from the database export const readProperties = async (query, limit = 0) => { - let ret = {err: null, values: null} + let ret = {err: null, properties: null} client = await connectMongo() try { if ("sid" in query) { // if sid is given, read property for sid - ret.values = await client.db(MONGOBASE).collection("properties").findOne({_id: query.sid}) + ret.properties = await client.db(MONGOBASE).collection(properties_collection).findOne({_id: query.sid}) } else { // otherwise read props corresponding to query - ret.values = await client.db(MONGOBASE).collection("properties").find(query).limit(limit).toArray() + ret.properties = await client.db(MONGOBASE).collection(properties_collection).find(query).limit(limit).toArray() } } catch (e) { ret.err = e @@ -76,10 +78,10 @@ export const readProperties = async (query, limit = 0) => { // read mapdata from database export const readMapdata = async (query, limit) => { - let ret = {err: null, values: []} + let ret = {err: null, mapdata: []} client = await connectMongo() try { - ret.values = await client.db(MONGOBASE).collection("mapdata").find(query).limit(limit).toArray() + ret.mapdata = await client.db(MONGOBASE).collection("mapdata").find(query).limit(limit).toArray() } catch (e) { ret.err = e } @@ -91,17 +93,28 @@ export const readMapdata = async (query, limit) => { } -export const getallProperties = async (client) => { - return await client.db(MONGOBASE).collection("properties") - .find().toArray() - +export const getallProperties = async (coll, query) => { + let ret = {err: null, properties: []} + client = await connectMongo() + try { + ret.properties = await client.db(MONGOBASE).collection(coll) + .find(query).toArray() + } catch (e) { + ret.err = e + } + finally { + await closeMongo(client) + client = null + } + return ret } + export const getOneproperty = async (sid) => { let ret = {error: false} client = await connectMongo() try { - ret.properties = await client.db(MONGOBASE).collection("properties") + ret.properties = await client.db(MONGOBASE).collection("properties_collection") .findOne({_id: sid}) } catch (e) { ret = {error: true, errortext: e} @@ -136,7 +149,7 @@ export const doAggregate = async (coll, query, conn, clos) => { export const writeOneproperty = async (client, prop) => { try { - let result = await client.db(MONGOBASE).collection("properties") + let result = await client.db(MONGOBASE).collection("properties_collection") .insertOne(prop) } catch(e) { if (e.code === 11000) { @@ -152,7 +165,7 @@ export const writeProperties = async (client, props) => { let start = DateTime.now(); if (props.new.length !== 0) { try { - let result = await client.db(MONGOBASE).collection("properties") + let result = await client.db(MONGOBASE).collection("properties_collection") .insertMany(props.new) } catch (e) { console.error(e) @@ -161,7 +174,7 @@ export const writeProperties = async (client, props) => { if (props.loc.length !== 0) { try { for (let item of props.loc) { - let result = await client.db(MONGOBASE).collection("properties") + let result = await client.db(MONGOBASE).collection("properties_collection") .updateOne({_id: item._id}, { $set: {location_id: item.location_id}, $push: {location: { $each: [item.location[0]], $position: 0}} @@ -174,7 +187,7 @@ export const writeProperties = async (client, props) => { if (props.sname.lenth !== 0) { try { for (let item of props.sname) { - let result = await client.db(MONGOBASE).collection("properties") + let result = await client.db(MONGOBASE).collection("properties_collection") .updateOne({_id: item._id}, { $set: {name: item.name, since: item.since} }) @@ -250,3 +263,60 @@ export const readAKWs = async (options) => { } return ret } + +/* +async function getMAPaktData(db,opt) { + let box = opt.box; + if ((box == "") || (box == undefined)) { + return {"avgs": [], "lastDate": null}; + } + var south = parseFloat(box[0][1]); + var north = parseFloat(box[1][1]); + var east = parseFloat(box[1][0]); + var west = parseFloat(box[0][0]); + var collection = db.collection('mapdata'); // use the mapdata collection + var aktData = []; + var now = moment(); + var lastDate = 0; + let loc; + loc = { + location: { + $geoWithin: { + $box: [ + [west, south], + [east, north] + ] + } + }, + name: { $regex : "^Laerm|^DNMS"}, + } + let docs = await collection.find(loc).toArray(); +// console.log(docs); + for (var i = 0; i < docs.length; i++) { + var item = docs[i]; + var oneAktData = {}; + oneAktData['location'] = item.location.coordinates; + oneAktData['id'] = item._id; // ID des Sensors holen + oneAktData['lastSeen'] = item.values.datetime; + oneAktData['indoor'] = item.indoor; + var dati = item.values.datetime; + var dt = new Date(dati); + if ((now - dt) >= 31 * 24 * 3600 * 1000) { // älter als 1 Monat -> + oneAktData['noise_max'] = -2; // -2 zurückgeben + } else if ((now - dt) >= 3600 * 1000) { // älter als 1 Stunde -> + oneAktData['noise_max'] = -1; // -1 zurückgeben + } else { + oneAktData['noise_max'] = -5; // bedutet -> nicht anzeigen + if (item.values.hasOwnProperty('noise_LA_max')) { + oneAktData['noise_max'] = item.values.noise_LA_max.toFixed(0); // und merken + } + if (dati > lastDate) { + lastDate = dati; + } + } + aktData.push(oneAktData); // dies ganzen Werte nun in das Array + } + return {"avgs": aktData, "lastDate": lastDate}; // alles bearbeitet _> Array senden +}; + + */ diff --git a/sensorspecials/noise.js b/sensorspecials/noise.js index f00691b..465b744 100644 --- a/sensorspecials/noise.js +++ b/sensorspecials/noise.js @@ -2,11 +2,12 @@ // rxf 2023-03-05 import {returnOnError} from "../utilities/reporterror.js"; -import { getActData, getAvgData, getLongAvg, fetchFromInflux, calcRange } from "../actions/getsensorData.js" +import { getActData, getAvgData, getLongAvg, fetchFromInflux, calcRange} from "../actions/getsensorData.js" import checkParams from "../utilities/checkparams.js"; import {getOneProperty} from "../actions/getproperties.js"; import * as ERR from "../utilities/errortexts.js" import {DateTime} from 'luxon' +import {getData4map} from "../actions/data4map.js"; const setoptionfromtable = (opt,tabval) => { let ret = opt @@ -41,7 +42,7 @@ export const getNoiseData = async (params, possibles, props) => { let {start, stop} = calcRange(opts) // calc time range opts.start = start opts.stop = stop - let erg = await x.func(opts) // get the data + let erg = await x.func(opts, props) // get the data ret.sid = opts.sensorid ret.start = opts.start.slice(7) ret.span = opts.span @@ -49,8 +50,10 @@ export const getNoiseData = async (params, possibles, props) => { ret.average = x.avg ret.peak = opts.peak } - ret.count = erg.length - ret.values = erg + ret.count = erg.values.length + ret.values = erg.values + ret.err = erg.err + ret.props = erg.props return ret } } @@ -82,9 +85,9 @@ export const getNoiseData = async (params, possibles, props) => { // .... // // ********************************************* -const getLiveData = async (opt) => { +const getLiveData = async (opt, props) => { let retur = {sid: opt.sensorid, span: opt.span, start: opt.datetime, count: 0, values: [], err: null}; - return await getActData(opt) + return await getActData(opt, props) } @@ -421,10 +424,6 @@ const getAPIprops = (opt) => { } -const getMAPaktData = (opt) => { - -} - const whatTable = [ {'what':'live', 'span': 1, 'daystart': false, avg: null, 'func': getLiveData}, {'what':'havg', 'span': 30, 'daystart': true, avg: 'hour', 'func': gethavgData}, @@ -432,7 +431,6 @@ const whatTable = [ {'what':'daynight', 'span': 30, 'daystart': true, avg: null, 'func': getdaynightData}, {'what':'lden', 'span': 30, 'daystart': true, avg: null, 'func': getLdenData}, {'what':'props', 'span': 0, 'daystart': true, avg: null, 'func': getAPIprops}, - {'what':'mapdata', 'span': 0, 'daystart': false, avg: null, 'func': getMAPaktData}, ]; diff --git a/utilities/commands.js b/utilities/commands.js index 1eb2a83..6bb9d49 100644 --- a/utilities/commands.js +++ b/utilities/commands.js @@ -6,6 +6,7 @@ import * as getAKWs from "../actions/getAKWData.js" import * as holAddr from "../actions/getaddress.js" import * as radioact from "../sensorspecials/radioact.js" import * as noise from "../sensorspecials/noise.js" +import {getData4map} from "../actions/data4map.js"; const cmdTable = [ {cmd: 'getactdata', func: getData.getActData}, @@ -14,7 +15,8 @@ const cmdTable = [ {cmd: 'getoneproperty', func: getProps.getOneProperty}, {cmd: 'getakwdata', func: getAKWs.getakwdata}, {cmd: 'getaddress', func: holAddr.getAddress}, - {cmd: 'getsensordata', func: getData.getSensorData} + {cmd: 'getsensordata', func: getData.getSensorData}, + {cmd: 'getmapdata', func: getData4map} ] export default cmdTable diff --git a/utilities/errortexts.js b/utilities/errortexts.js index cb099c6..5551124 100644 --- a/utilities/errortexts.js +++ b/utilities/errortexts.js @@ -12,3 +12,5 @@ export const RESPSTATUS = `Returned status = ${'xx'}` export const NODATA = 'No data found' export const SYNTAXURL = 'Syntax error in calling url' export const WRONGTYPE = `Sensor ${'xx'} is not of type ${'yy'}` +export const NOLASTDATES = `Problems fetching last dates from database` +export const NOPROPSFOUND = `Properties collection not found`