const express = require('express') const mariadb = require('mariadb') const fs = require('fs'); let db_settings = JSON.parse(fs.readFileSync('resources/db_settings.json', 'utf8')); const app = express() const pool = mariadb.createPool({ host: db_settings.host, port: db_settings.port, user: db_settings.user, password: db_settings.password, database: db_settings.database, supportBigNumbers: true, }) const PORT = 9000 function convert_rights(rights) { switch (rights) { case "Super-User": return 0; case "PTS-User": return 1; case "Premium-User": return 2; case "Standard-User": return 3; case "Test-User": return 4; default: throw new Error("Could not parse Rights (" + rights + ")"); } } // encapsulate db access async function queryDB(query) { let conn; let rows; try { conn = await pool.getConnection(); rows = await conn.query(query); } catch (err) { throw err; } finally { if (conn) { conn.end(); } } return rows; } async function queryDevice(device, incoming_rights) { let config_ids = await queryDB( "SELECT config_id " + "FROM api_info " + "WHERE name=\"Gerät\" AND value LIKE \"" + device + "\"" ); let meta_map = {}; for (let i = 0; i < config_ids.length; i++) { const config_id = config_ids[i].config_id; let metas = await queryDB( "SELECT name, value " + "FROM api_info " + "WHERE config_id=\"" + config_id + "\"" ); let m = {}; m["data"] = {}; for (let j = 0; j < metas.length; j++) { let value = metas[j].value; let name = metas[j].name; if (name == "Gerät") { m["Gerät"] = value; } else { let values = JSON.parse(value); for (let [key, value] of Object.entries(values)) { if (Array.isArray(value)) { for (let i = 0; i < value.length; i++) { let v = value[i]; let rights = convert_rights(v.right); if (rights < incoming_rights) { delete values[key]; break; } } } else { if (value.right === undefined) { for (let [inner_key, inner_value] of Object.entries(value)) { if (Array.isArray(inner_value)) { for (let i = 0; i < inner_value.length; i++) { let v = inner_value[i]; for (let [inner_inner_key, inner_inner_value] of Object.entries(v)) { let rights = convert_rights(inner_inner_value.right); if (rights < incoming_rights) { delete v[inner_inner_key]; } } } } else if (inner_value.right !== undefined) { let rights = convert_rights(inner_value.right); if (rights < incoming_rights) { delete value[inner_key]; } } else { for (let [inner_inner_key, inner_inner_value] of Object.entries(inner_value)) { let rights = convert_rights(inner_inner_value.right); if (rights < incoming_rights) { delete inner_value[inner_inner_key]; } } } } } else { let rights = convert_rights(value.right); if (rights < incoming_rights) { delete values[key]; } } } } let d = m["data"]; let l = Object.keys(d).length; if (l != 0) { m["data"] = { ...d, ...values }; } else { m["data"] = values; } } } meta_map[config_id] = m; } for (let config_id of Object.keys(meta_map)) { let results = await queryDB( "SELECT id " + "FROM api_results " + "WHERE config_id=\"" + config_id + "\"" ); for (let j = 0; j < results.length; j++) { let measurements = await queryDB( "SELECT name, value, rights " + "FROM api_measurements " + "WHERE result_id=\"" + results[j].id + "\"" ); m = {} for (let k = 0; k < measurements.length; k++) { let measurement = measurements[k]; let rights = measurement.rights.replace(/['"]+/g, ''); let rights_value = convert_rights(rights); if (rights_value >= incoming_rights) { m[measurement.name] = { name: measurement.name, value: measurement.value, rights: rights }; } } meta_map[config_id][results[j].id] = m; } } return meta_map; } async function calculate(elements, parameter, operation) { switch (operation) { case "average": let tmp = 0; let count = 0; for (let param of parameter) { const value = elements.find(element => element.name == param); if (value === undefined) { continue; } count += 1; tmp += value.value; } if (count == 0) { return null; } return tmp / count; case "sum": let sum = 0; for (let param of parameter) { const value = elements.find(element => element.name == param); if (value === undefined) { return null; } sum += value.value; } return sum; case "divide": const v1 = elements.find(element => element.name == parameter[0]); const v2 = elements.find(element => element.name == parameter[1]); if (v1 === undefined || v2 === undefined || v2 == 0) { return null; } return v1.value / v2.value; case "multiply": let p = 1.0; for (let param of parameter) { const value = elements.find(element => element.name == param); if (value === undefined) { return null; } p *= value.value; } return p; case "subtract": const s1 = elements.find(element => element.name == parameter[0]); const s2 = elements.find(element => element.name == parameter[1]); if (s1 === undefined || s2 === undefined) { return null; } return s1.value - s2.value; case "discard": const value = elements.find(element => element.name == parameter[0]); const res = elements.find(element => element.name == parameter[1]); if (value === undefined || res === undefined) { return null; } if (value.value !== null && !value.value) { return res.value; } else { return 0; } default: throw "unknown operation " + operation; } } async function createResultForEntry(parameters, descriptions, calculations) { let result = {}; for (let [key, data] of Object.entries(parameters)) { if (Object.keys(data).length == 0) { return {}; } if (key == "Gerät") { continue; } let elements = findAll(data); for (let [calc_key, calc_value] of Object.entries(calculations)) { let val = null; if (calc_value.const !== undefined) { val = parseFloat(calc_value.const[0]); } else { val = await calculate(elements, calc_value.source, calc_value.operation); } if (val !== null) { elements.push({ name: calc_key, value: val }); } } console.info(elements); for (let element of elements) { let key = String(element.name); let value = element.value; let description = descriptions[key]; if (description !== undefined) { if (value !== null) { result[key] = { name: description.name, value: value, description: description.description, unit: description.unit, } } } else { for (let [description_key, description] of Object.entries(descriptions)) { if (key.startsWith(description_key)) { result[key] = { name: description.name, value: value, description: description.description, unit: description.unit, } break; } } } } } return result; } async function createResultList(parameters, descriptions, calculations) { let result = []; for (let entry of Object.values(parameters)) { let entry_result = await createResultForEntry(entry, descriptions, calculations); if (Object.keys(entry_result).length != 0) { result.push(entry_result); } } return result; } function findAll(object) { let list = []; if (object === null) { return list; } if (object.name !== undefined) { if (object.value !== undefined) { list.push({ name: object.name, value: object.value }); } else { for (let [key, value] of Object.entries(object)) { list = list.concat(findAll(value)); } } } else { for (let [key, value] of Object.entries(object)) { list = list.concat(findAll(value)); } } return list; } async function resultList(cb_filter, rights) { // read parameters from DB and only return accessible let deviceParameters = await queryDevice(cb_filter, convert_rights(rights)); // read paremeter description file let descriptions = JSON.parse(fs.readFileSync('resources/variable_description.json', 'utf8')); // read parameter calculation file, let calculations = JSON.parse(fs.readFileSync('resources/variable_calculation.json', 'utf8')); // combine description information with parameters queried from DB let result = await createResultList(deviceParameters, descriptions, calculations); return result; } function createFilters() { // column | variable name | values // ---------------------------|----------------------- // B | finished_product | without-finishing, partial-finishing, intermediate, final, finished // C | base_material | paper, carton, corrugated, solid, fibre, mix, other // D | coating | no, one-side, two-side // E | fillers | no, yes // F | polymers | no, low, high // G | artificial_fibre | no, yes // H | ??? | // I | printed | no, yes // J | varnish | no, yes // K | glue | no, yes // L | sealing | no, yes let filters = [ { key: "finished_product", values: ["without-finishing", "partial-finishing", "intermediate", "final", "finished"] }, { key: "base_material", values: ["paper", "carton", "corrugated", "solid", "fibre", "mix", "other"] }, { key: "coating", values: ["no", "one-side", "two-side"] }, { key: "fillers", values: ["no", "yes"] }, { key: "polymers", values: ["no", "low", "high"] }, { key: "artificial_fibre", values: ["no", "yes"] }, { key: "printed", values: ["no", "yes"] }, { key: "varnish", values: ["no", "yes"] }, { key: "glue", values: ["no", "yes"] }, { key: "sealing", values: ["no", "yes"] } ]; return filters } // searches through the provided filter and returns a list of them function getFilter(query) { // get static list of implemented filters let filters = createFilters(); let result = []; // iterate over every filter and check for correct syntax filters.forEach(function (filter) { let res = query[filter.key]; if (res) { if (filter.values.find(element => element == res)) { result.push({ key: filter.key, value: res }); } else { throw "unknown value (" + res + ") for key (" + filter.key + "). Possible values: " + filter.values; } } }); return result; } function applyFilter(result_list, filters) { let results = []; // check every result if every filter can be applied result_list.forEach(function (result) { let filter_success = true; // check all filter filters.forEach(function (filter) { let value = result[filter.key]; // check if result even holds the key (and value for it) if (value) { if (value !== filter.value) { filter_success = false; } } else { filter_success = false; } }); // only add result if filters could successfully be applied if (filter_success == true) { results.push(result); } }); return results; } app.get('/cepi/list/:rights', async (request, response) => { let result_list = await resultList("%Cepi%", request.params.rights); response.json(result_list) }) app.get('/cepi/filters', async (_request, response) => { response.json(createFilters()) }) app.get('/cepi/list/:rights/filter', async (request, response) => { let results; try { let result_list = await resultList("%Cepi%", request.params.rights); let filters = getFilter(request.query); results = applyFilter(result_list, filters); } catch (err) { return response.status(400).send({ message: err }); } response.json(results) }) app.listen(PORT, () => { console.log(`Server running on port ${PORT}`) })