Ertser Commit der test-Version

This commit is contained in:
2025-10-10 16:27:06 +00:00
commit 2308aa56a3
37 changed files with 10988 additions and 0 deletions

190
actions/data4map.js Normal file
View File

@@ -0,0 +1,190 @@
// Fetch the actual (= newest) data out of the dbase to show it on the map
import {DateTime} from "luxon"
import * as mongo from "../databases/mongo.js"
import { returnOnError } from "../utilities/reporterror.js"
// Default distance for center search ( in km)
const DEFAULT_DISTANCE = 10
// Value to use fpr map
const value4map = [
{ typ: 'noise', value: '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 = {
P1: 'pm', P2: 'pm', P0: 'pm',
temperature: 'thp', humidity: 'thp', pressure: 'thp',
LAeq: 'noise',
counts_per_minute: 'radioactivity'
};
// find first value type from measurement
const getfieldfromtype = (typ) => {
for (const [key, value] of Object.entries(vtype2measurement)) {
if (value === typ) {
return key
}
}
return ' '
}
// 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 var getData4map = async (params) => {
let start = DateTime.now()
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 !
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) {
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)
}
if (params.center !== undefined) {
center = params.center
if ((params.distance !== undefined) &&
(params.distance >= 1) && (params.distance <= 1000)) {
distance = params.distance
}
}
const aktData = []
let lastDate = 0
let query = {type: typ}
// if polyline or box were given, set query
if (poly.length !== 0) { // polyline given
query.location = {
$geoWithin: {
$geometry: {
type: "Polygon",
coordinates: [poly],
}
}
}
} else if (south !== null) { // box given
query["location.loc"] = {
$geoWithin: {
$box: [
[west, south],
[east, north]
]
}
}
} else if (center !== null) { // center point given
query["location.loc"] = {
$nearSphere: {
$geometry: {
type: "Point",
coordinates: center
},
$maxDistance: distance * 1000
}
}
}
try {
// fetch mapdata from mongodb
let { properties, err } = await mongo.getallProperties(mongo.properties_collection, query)
if(err) {
return returnOnError(ret, 'NOPROPSFOUND', getData4map.name)
}
let v4map = getValue4Map(typ)
for (let sensor of properties) {
let oneAktData = {}
if (sensor.values !== undefined) {
oneAktData = {
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()
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
}
}
let weeks = Math.round(diff / (7 * 24 * 3600 * 1000))
oneAktData.weeks = weeks
}
if (sensor.values.timestamp > lastDate) {
lastDate = sensor.values.timestamp
}
} else {
oneAktData.value = -5
oneAktData.weeks = 0
oneAktData.lastseen = ''
}
aktData.push(oneAktData)
}
ret = {
err: null,
options: {
lastdate: lastDate,
count: aktData.length,
data: 'map'
},
values: aktData
}
return ret
}
catch(e) {
return returnOnError(ret, `catch\n${e}`, getData4map.name)
}
}

62
actions/getAKWData.js Normal file
View File

@@ -0,0 +1,62 @@
// get data for one sensor
import {DateTime} from "luxon"
import { logit, logerror } from '../utilities/logit.js'
import * as mongo from "../databases/mongo.js"
import {reportError, returnOnError} from "../utilities/reporterror.js"
import {csv2Json} from "../utilities/csv2json.js"
// Fetch all akw data out of the dbase
// router.get('/getakwdata/', async function (req, res) {
export const getakwdata = async (options) => {
let data = {err: null, ...options, count: 0, values: []}
let erg = []
try {
let rawAKWs = await mongo.readAKWs(options)
if (rawAKWs.err) {
return returnOnError(date, rawAKWs.err, getakwdata.name)
}
for (let item of rawAKWs.values.akws) {
var oneAktData = {};
oneAktData['location'] = {
type: 'Point',
coordinates: [item.lon, item.lat]
};
oneAktData['name'] = item.Name;
oneAktData['active'] = item.Status == 'aktiv';
oneAktData['start'] = item.Baujahr;
oneAktData['end'] = item.Stillgeleg;
oneAktData['type'] = item.Status === 'aktiv' ? 'akw_a' : 'akw_s';
oneAktData['link'] = item.Wiki_Link;
erg.push(oneAktData); // dies ganzen Werte nun in das Array
}
for (let item of rawAKWs.values.th1_akws) {
let oneAktData = {};
let loc = item.geo.substr(6).split(' ');
let lon = parseFloat(loc[0]);
let lat = parseFloat(loc[1]);
oneAktData['location'] = {
type: 'Point',
coordinates: [lon, lat]
};
oneAktData['name'] = item.name;
oneAktData['typeText'] = item.types;
oneAktData['type'] = item.types == "Nuclear power plant" ? 'akw_a' : 'other';
oneAktData['link'] = item.item;
if (item.itemServiceretirement != undefined) {
oneAktData['ende'] = item.itemServiceretirement.substr(0, 4);
}
if (item.itemServiceentry != undefined) {
oneAktData['begin'] = item.itemServiceentry.substr(0, 4);
}
erg.push(oneAktData);
}
data.values = erg
data.count = erg.length
} catch (e) {
return returnOnError(data, e, getakwdata.name)
}
return data
}

80
actions/getaddress.js Normal file
View File

@@ -0,0 +1,80 @@
import {returnOnError} from "../utilities/reporterror.js"
import axios from 'axios'
import {logit} from "../utilities/logit.js"
import {getOneProperty} from "./getproperties.js"
import { response } from "express"
const NOMINATIM_URL = `https://nominatim.openstreetmap.org/reverse?lat=${'xx'}&lon=${'yy'}&format=json`
const NOMINATIM_CITY_URL = `https://nominatim.openstreetmap.org/?q="${'xx'}"&format=json`
export const getCityCoords = async (params) => {
let ret = {coords: [], city: params.city, err: null}
let url = NOMINATIM_CITY_URL.replace('xx', params.city)
// let url = 'https://nominatim.openstreetmap.org/?q="K%C3%B6ln"&format=json'
try {
const response = await axios(url)
if (response.status !== 200) {
return returnOnError(ret, 'RESPSTATUS', getCityCoord.name, response.status)
}
ret.coords = [response.data[0].lat,response.data[0].lon]
logit(JSON.stringify(ret.coords))
} catch (e) {
return returnOnError(ret, e, getCityCoords.name)
}
return ret
}
export const getAddress = async (params) => {
let ret = {address: "", err: null}
let {props, err} = await getOneProperty(params)
if (err) {
return returnOnError(ret, err, getAddress.name)
}
let coord = props.location[0].loc.coordinates
let url = NOMINATIM_URL.replace('xx', coord[1]).replace('yy', coord[0])
try {
const response = await axios(encodeURI(url),{
headers: {
'User-Agent': 'Firefox 132.0.1'
}
});
if (response.status !== 200) {
return returnOnError(ret, 'RESPSTATUS', getAddress.name, response.status)
}
let akt = response.data.address
// logit(JSON.stringify(akt))
const CITY = ['city', 'town', 'village', 'suburb', 'county']
let city = "unknown"
for (let c of CITY) {
if(akt[c] !== undefined) {
city = akt[c]
break
}
}
// ret.address = `${(akt.road ? akt.road : "")} ${(akt.postcode ? akt.postcode : "")} ${city}`;
ret.address = {
street: `${(akt.road ? akt.road : "")}`,
plz: `${(akt.postcode ? akt.postcode : "")}`,
city: `${city}`}
} catch (e) {
return returnOnError(ret, e, getAddress.name)
}
return ret
}
/*
let addr = "Addr";
try {
let ret = await $.get("api/getprops?sensorid=" + marker.options.name);
if(ret.values[0].address.city == null) {
addr += " unbekannt !"
} else {
let akt = ret.values[0].address;
addr = (akt.street ? akt.street : "") + "&nbsp;&nbsp;" + (akt.plz ? akt.plz : "") + " " + akt.city;
}
} catch (e) {
console.log("onMarker - getpops", e)
}
console.log("addr:", addr);
return addr;
*/

48
actions/getproperties.js Normal file
View File

@@ -0,0 +1,48 @@
// Fetch the properties for the given sensor
import * as mongo from "../databases/mongo.js"
import {returnOnError} from "../utilities/reporterror.js"
import checkParams from "../utilities/checkparams.js"
let readProperties = mongo.readProperties
let readChipData = mongo.readChipData
// Read properties for sensorid and properties for all other sensors on same location
export const getOneProperty = async (params) => {
let properties = {err: null, props: {}, chip: {}}
let {opts, err} = checkParams(params, {mandatory:[{name:'sensorid', type: 'int'}], optional:[]})
if (err) {
return returnOnError(properties, err, getOneProperty.name)
}
// read 'chip'-data (special for noise sensors)
const chipdata = await readChipData(opts.sensorid)
if (chipdata.err == undefined) {
properties.chip = chipdata
}
let sensorEntries = [];
try {
let pp = await readProperties({sid: opts.sensorid}); // read for given sensorID
if ((pp.properties == null) || (pp.error)) {
return returnOnError(properties, 'NOPROPSREAD', getOneProperty.name, opts.sensorid)
}
// now find sensors with same location
let query = {"location.0.id": pp.properties.location[0].id}
let others = await readProperties(query)
if (others.err) {
return returnOnError(properties, 'NOPROPSREAD', getOneProperty.name, others.errortext)
}
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.properties
properties.props.othersensors = sensorEntries;
} catch (e) {
return returnOnError(properties, e, getOneProperty.name)
}
return properties
}

308
actions/getsensorData.js Normal file
View File

@@ -0,0 +1,308 @@
// get data for one sensor
const DBASE = process.env.DBASE || 'mongo'
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 {getOneProperty} from "./getproperties.js"
import {getNoiseData} from "../sensorspecials/noise.js"
import {getgeigerData} from "../sensorspecials/geigeract.js"
// Possible params for the different sensor types
const noiseParams = [
{name:'data', type: 'string', default: 'live'},
{name: 'span', type: 'int', default: 1},
{name: 'daystart', type: 'bool', default: null},
{name: 'peak', type: 'int', default: 70},
{name: 'since', type: 'date', default: '1900-01-01T00:00:00Z'},
{name: 'box', type: 'array', default: null},
{name: 'out', type: 'string', default: ''},
{name: 'csv', type: 'bool', default: false},
{name: 'long', type: 'bool', default: false},
{name: 'sort', type: 'int', default: 1},
{name: 'last_seen', type: 'date', default: '1900-01-01T00:00:00Z'},
{name: 'datetime', type: 'date', default: null}
]
const thpParams = []
const pmParams = []
const geigerParams = [
{name: 'what', type: 'string', default: 'day'},
{name: 'span', type: 'int', default: 1},
{name: 'daystart', type: 'bool', default: null},
{name: 'avg', type: 'int', default: 1},
{name: 'since', type: 'date', default: '1900-01-01T00:00:00Z'},
{name: 'box', type: 'array', default: null},
{name: 'out', type: 'string', default: ''},
{name: 'csv', type: 'bool', default: false},
{name: 'long', type: 'bool', default: false},
{name: 'sort', type: 'int', default: 1},
{name: 'last_seen', type: 'date', default: '1900-01-01T00:00:00Z'},
{name: 'datetime', type: 'date', default: null},
{name: 'moving', type: 'bool', default: false},
]
// >>>>>>>>>>>>>>>>>>>>> DUMMIES
async function getPmData(opts) {
}
async function getThpData(opts) {
}
// <<<<<<<<<<<<<<<<<<<<< DUMMIES
// Table for the different sensor types
const sensorTypeTable = [
{ typ: 'thp', possibleParams: thpParams, func: getThpData},
{ typ: 'noise', possibleParams: noiseParams, func: getNoiseData},
{ typ: 'pm', possibleParams: pmParams, func: getPmData},
{ typ: 'radioactivity', possibleParams: geigerParams, func: getgeigerData}
]
/* Units:
* span -> days
* avg -> minutes
*/
// *********************************************
// calcRange
//
// Calculate the date/time range ro read the data from
//
// If 'datetime' is not given, use 'now()' as end and 'now()' - span as start
// If 'datetime' is given, use it as start and 'datetime'+span as end
// If 'daystart'==true, change start and end so, that they begin at 00:00:00h
// if 'avg' has a value, move start backward ba 'avg' minutes
//
// params:
// opts: Object with different options, specially
// datetime, avg, daystart, span
//
// return:
// Object with start and stop in ISO-Format
// *********************************************
export const calcRange = (opts) => {
let start, end
let ret = { start: start, stop: end, err: null}
if((opts.datetime === null) || (opts.datetime === undefined) || (opts.datetime === '')) {
start = end = DateTime.local().toUTC()
start = start.minus({days: opts.span})
} else {
start = end = DateTime.fromISO(opts.datetime).toUTC()
end = end.plus({days: opts.span})
}
if(opts.daystart) {
start = start.startOf("day")
end = end.startOf("day")
}
// start = start.toUTC()
// end = end.toUTC()
// if(opts.avg !== undefined) {
// start = start.minus({minutes: opts.avg})
// }
ret.start = `start: ${start.toISO()}`
ret.stop = `stop: ${end.toISO()}`
return ret
}
// *********************************************
// getSensorData
//
// Depending on the parameter 'sensorid', distribute to the special routines for
// the sensors
//
// params:
// params: all parameters from the url
//
// return:
// Returns from the special routines
// *********************************************
// export const getSensorData = async (params) => {
export async function getSensorData(params) {
let ret = {err: null}
let {opts, err} = checkParams(params, { // check for sensorid
mandatory: [{name: 'sensorid', type: 'int'}],
optional: []
})
if (err) {
return returnOnError(ret, err, getSensorData.name)
}
// with the sensorid get the type of this sensor
let erg = await getOneProperty({sensorid: opts.sensorid})
if (erg.err) {
return returnOnError(ret, erg.err, getSensorData.name)
}
// distribute to the right routine
for(let item of sensorTypeTable) {
if(item.typ === erg.props.type) {
ret = await item.func(params, item.possibleParams, erg.props) // get the values from database
return ret
}
}
return returnOnError(ret, 'CMNDUNKNOWN', getActData.name)
}
// export const getActData = async (opts) => {
export async function getActData(opts) {
if (DBASE === 'mongo') {
return await mongo.fetchActData(opts)
} else if (DBASE === 'influx') {
return await influx.fetchActData(opts)
}
return {err: 'DBASEUNKNOWN', values: []}
}
// ..../api/getavgdata?
// ToDo: check UTC !!!!
export var getAvgData = async (params) => {
let ret = {data: {count: 0, values: []}, err: null}
let {opts, err} = checkParams(params,
{
mandatory:
[
{name: 'sensorid', type: 'int'},
],
optional:
[
{name: 'span', type: 'int', default: 1},
{name: 'datetime', type: 'date', default: null},
{name: 'avg', type: 'int', default: 10},
{name: 'moving', type: 'bool', default: true},
]
}
)
if (err) {
return returnOnError(ret, err, getActdata.name)
}
let {start, stop} = calcRange(opts)
if (stop === '') {
ret.data.start = DateTime.now().toUTC().minus({days: `${opts.span}`}).toFormat("yyyy-LL-dd'T'HH:mm:ss'Z'")
} else {
ret.data.start = DateTime.fromISO(opts.datetime, {zone: 'utc'}).toISO()
}
ret.data.span = opts.span
ret.data.moving = opts.moving
ret.data.avg = opts.avg
let every = opts.moving ? '150s' : `${opts.avg}m`
let period = `${opts.avg}m`
let query = `
from(bucket: "sensor_data")
|> range(${start}, ${stop})
|> filter(fn: (r) => r.sid == "${opts.sensorid}")
|> timedMovingAverage(every: ${every}, period: ${period})
|> keep(columns: ["_time","_field","_value"])
|> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
`
return await fetchFromInflux(ret, query)
}
// ..../api/getlongavg?sensorid=123&span=2
export var getLongAvg = async (params) => {
let ret = {data: {count: 0, values: []}, err: null}
let {opts, err} = checkParams(params,
{
mandatory:
[
{name: 'sensorid', type: 'int'},
],
optional:
[
{name: 'span', type: 'int', default: 2},
]
}
)
if (err) {
return returnOnError(ret, err, getActdata.name)
}
ret.data.span = opts.span
let query = `
from(bucket: "sensor_data")
|> range(start: -${opts.span}d)
|> filter(fn: (r) => r.sid == "${opts.sensorid}")
|> mean()
|> drop(columns: ["_start","_measurement", "sid"])
|> pivot(rowKey:["_stop"], columnKey: ["_field"], valueColumn: "_value")
`
return await fetchFromInflux(ret, query)
}
// *********************************************
// 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", max: "65" }
// { location: [ 9.10, 49.80 ], id: 28194, lastSeen: "2019-10-22T16:03:00.000Z", 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('LA_max')) {
oneAktData['noise_max'] = item.values.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
}