Loading http-bridge/controllers/generalControllers.js +70 −82 Original line number Diff line number Diff line Loading @@ -3,44 +3,38 @@ const NGSITranslator = require("../translator/NGSITranslator"); const typeMaps = require("../typeMaps/typeMaps"); /** * Generates a modality_id parameter based on the type and attributes provided. * If attributes are given, it will filter the modalities to only include the ones * that correspond to the given attributes. Otherwise, it will return all modalities * available for the given type. * * Based on Smart City Heraklion API * * @param {string} type The NGSI-LD type to generate the modality_id for * @param {string} type The NGSI-LD type to generate the measurement_type for * @param {string} attributes A comma-separated list of attributes to filter by * @throws {Error} If the type is invalid or no valid attributes are found * @returns {string} The modality_id parameter as a string * @returns {string[]} List of measurement types */ function generateModalityIdParam(type, attributes) { const modalityMap = typeMaps[type]; if (!modalityMap) { function generateMeasurementTypeParam(type, attributes) { const typeMap = typeMaps[type]; if (!typeMap) { throw new Error(`Invalid type: ${type}`); } // Filter attributes if provided, otherwise return all const modalityIds = attributes ? attributes.split(",").map(attr => Object.entries(modalityMap) const measurementTypes = attributes ? attributes.split(",").map(attr => Object.entries(typeMap) .find(([_, value]) => value.slug === attr)?.[0]) .filter(Boolean) : Object.keys(modalityMap); : Object.keys(typeMap); if (modalityIds.length === 0) { throw new Error(`No valid attributes found for type: ${type}`); if (measurementTypes.length === 0) { throw new Error(`No valid attributes found for type '${type}' and attributes '${attributes}'`); } return modalityIds; // console.log("Measurement types:", measurementTypes); return measurementTypes; } /** * Fetches data from the Smart City Heraklion API and translates it to NGSI-LD. * Fetches data from the provided API and translates it to NGSI-LD. * * @param {string} type The NGSI-LD type to retrieve * @param {string} [attrs] A comma-separated list of attributes to filter by * @param {Function} fetcher The specific fetcher function to use (fetchWeatherObservedTemporalData or fetchWeatherObservedRealTimeData) * @param {Function} fetcher The specific fetcher function to use (fetchTemporalData or fetchRealTimeData) * @param {Object} [additionalParams] Additional parameters to pass to the fetcher function * @param {Response} res The Express response object * @throws {Error} If the type is invalid, no valid attributes are found, or the API request fails Loading @@ -55,19 +49,17 @@ const fetchEntities = async (type, attrs, fetcher, additionalParams, res) => { return res.status(400).json({ error: `Unsupported entity type: ${type}` }); } const modalityIdParam = generateModalityIdParam(type, attrs); if (!modalityIdParam) { return res.status(400).json({ error: "Missing modality_id parameter." }); const measurementTypes = generateMeasurementTypeParam(type, attrs); if (!measurementTypes) { return res.status(400).json({ error: "Missing measurement_type parameter." }); } // Call the specific fetcher function const apiResponse = await fetcher(type, modalityIdParam, additionalParams); const apiResponse = await fetcher(type, measurementTypes, additionalParams); if (!apiResponse || !apiResponse.data) { if (!apiResponse || apiResponse.data.length === 0) { return res.status(500).json({ error: "Failed to retrieve data from API." }); } // Translate response to NGSI-LD const ngsiData = NGSITranslator.toNGSI(apiResponse.data, type); res.status(200).json(ngsiData); } catch (error) { Loading @@ -80,67 +72,63 @@ const fetchEntities = async (type, attrs, fetcher, additionalParams, res) => { * Fetches the temporal data for a given NGSI-LD entity type. * * @param {string} type The NGSI-LD type to retrieve * @param {string} modalityIdParam The modality_id parameter for filtering * @param {string} measurementTypes The measurement type parameter for filtering * @param {Object} [params] Additional parameters for the API request * @throws {Error} If the entity type is unsupported */ const fetchWeatherObservedTemporalData = async (type, modalityIds, params) => { if (!Array.isArray(modalityIds) || modalityIds.length === 0) { throw new Error(`Invalid modality IDs for type: ${type}`); } const results = { data: [] }; // Loop through each modality ID and make separate requests for (const modalityId of modalityIds) { const fetchTemporalData = async (type, measurementTypes, params) => { switch (type) { case "DeviceMeasurement": try { const apiResponse = await axios.post(`${process.env.API_URL}/rpc/measurements_averages`, { _devices: "{71,72,73,63,64,36,68,70,61,69,67,66,65,60,59,120}", _from: params.time, _to: params.endTime, _granularity: "1 hour", _modality: typeMaps[type][modalityId].key, _show_out_of_range: false const response = await axios.get(`${process.env.API_URL}/device_measurement`, { params: { measurement_type: `in.(${measurementTypes.join(",")})`, ts: params.time, // TODO: Implement proper time handling according to postgres timestamp format limit: params.limit, } }); if (apiResponse && apiResponse.data) { formattedData = apiResponse.data.map(entry => ({ device_id: modalityId, modality_id: parseInt(modalityId, 10), value: entry.avg, observed_at: entry.at })); results.data.push(...formattedData); } return response; } catch (error) { console.error(`Error fetching data for modalityId ${modalityId}:`, error.message); } console.error("Error fetching temporal data:", error.message); return { data: [] }; } return results; default: return { data: [] }; } }; /** * Fetches the latest real-time data for a given NGSI-LD entity type. * * @param {string} type The NGSI-LD type to retrieve * @param {string} modalityIdParam The modality_id parameter for filtering * @param {string} measurementTypes The measurement type parameter for filtering * @throws {Error} If the entity type is unsupported */ const fetchWeatherObservedRealTimeData = async (type, modalityIdParam) => { const fetchRealTimeData = async (type, measurementTypes, params) => { switch (type) { case "WeatherObserved": return axios.get(`${process.env.API_URL}/latest_measurements_averages`, { params: { modality_id: `in.(${Array.isArray(modalityIdParam) ? modalityIdParam.join(",") : modalityIdParam})` }, case "DeviceMeasurement": try { const response = await axios.get(`${process.env.API_URL}/device_measurement`, { params: { measurement_type: `in.(${measurementTypes.join(",")})`, order: "ts.desc", limit: params.limit || 100, // Default to 100 if limit is not provided. (MEETING: Remove this?) } }); return response; } catch (error) { console.error("Error fetching real-time data:", error.message); return { data: [] }; } default: throw new Error(`Unsupported real-time entity type: ${type}`); return { data: [] }; } }; /** * Handles incoming requests for temporal NGSI-LD entities. * Loading @@ -149,26 +137,24 @@ const fetchWeatherObservedRealTimeData = async (type, modalityIdParam) => { * @prop {string} time The timestamp to apply the temporal relationship to * @prop {string} endTime The end time for the time range query * @prop {string} attrs A comma-separated list of attributes to filter by * @prop {string} limit The maximum number of entities to return */ const getTemporalEntities = async (req, res) => { console.log("Incoming temporal request:", req.query); let { type, timerel, time, endTime, attrs } = req.query; const { type, timerel, time, endTime, attrs, limit } = req.query; // Decode URL-encoded time values (fixes %3A issue) time = decodeURIComponent(time); if (endTime) endTime = decodeURIComponent(endTime); // Ensure proper ISO 8601 formatting let adjustedEndTime = endTime; const now = new Date().toISOString(); // TODO: Implement "between" timerel and fix temporal queries not working at all. if (timerel === "after") { endTime = now; // Set endTime to current time adjustedEndTime = now; } else if (timerel === "before") { endTime = time; // Swap values for "before" time = new Date(new Date(endTime).setDate(new Date(endTime).getDate() - 30)).toISOString(); // 30 days before adjustedEndTime = time; time = new Date(new Date(adjustedEndTime).setDate(new Date(adjustedEndTime).getDate() - 30)).toISOString(); } await fetchEntities(type, attrs, fetchWeatherObservedTemporalData, { time, endTime }, res); await fetchEntities(type, attrs, fetchTemporalData, { time, endTime: adjustedEndTime, limit }, res); }; /** Loading @@ -176,11 +162,13 @@ const getTemporalEntities = async (req, res) => { * * @prop {string} type The NGSI-LD type to retrieve * @prop {string} attrs A comma-separated list of attributes to filter by * @prop {string} limit The maximum number of entities to return */ const getRealTimeEntities = async (req, res) => { console.log("Incoming real-time request:", req.query); const { type, attrs } = req.query; await fetchEntities(type, attrs, fetchWeatherObservedRealTimeData, {}, res); const { type, attrs, limit } = req.query; await fetchEntities(type, attrs, fetchRealTimeData, { limit }, res); }; module.exports = { getTemporalEntities, getRealTimeEntities }; http-bridge/translator/NGSITranslator.js +15 −15 Original line number Diff line number Diff line Loading @@ -3,14 +3,14 @@ const typeMaps = require("../typeMaps/typeMaps"); class NGSITranslator { static toNGSI(apiData, entityType) { switch (entityType) { case "WeatherObserved": return NGSITranslator.processWeatherObserved(apiData, entityType); case "DeviceMeasurement": return NGSITranslator.processDeviceMeasurement(apiData, entityType); default: throw new Error(`Unsupported entity type: ${entityType}`); } } static processWeatherObserved(apiData, entityType) { static processDeviceMeasurement(apiData, entityType) { const typeMap = typeMaps[entityType]; if (!typeMap) { throw new Error(`No mapping found for entity type: ${entityType}`); Loading @@ -19,7 +19,7 @@ class NGSITranslator { const groupedByDevice = {}; apiData.forEach(item => { const { device_id, modality_id, value, observed_at } = item; const { item: device_id, measurement_type, value, ts } = item; // Ensure the device entry exists if (!groupedByDevice[device_id]) { Loading @@ -28,30 +28,30 @@ class NGSITranslator { type: entityType, dateObserved: { type: "Property", values: [] }, "@context": [ "https://raw.githubusercontent.com/smart-data-models/dataModel.Weather/master/context.jsonld", "https://raw.githubusercontent.com/smart-data-models/dataModel.Device/master/context.jsonld", "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld" ] }; } const modalityInfo = typeMap[modality_id]; if (modalityInfo) { const measurementInfo = typeMap[measurement_type]; if (measurementInfo) { // Ensure property exists, else initialize if (!groupedByDevice[device_id][modalityInfo.key]) { groupedByDevice[device_id][modalityInfo.key] = { if (!groupedByDevice[device_id][measurementInfo.key]) { groupedByDevice[device_id][measurementInfo.key] = { type: "Property", values: [] }; } // Append new observation instead of overwriting groupedByDevice[device_id][modalityInfo.key].values.push( NGSITranslator.createProperty(value, modalityInfo.unit, modalityInfo.min, modalityInfo.max, modalityInfo.slug, observed_at) groupedByDevice[device_id][measurementInfo.key].values.push( NGSITranslator.createProperty(value, measurementInfo.unit, measurementInfo.min, measurementInfo.max, measurementInfo.slug, ts) ); // Append observed_at to dateObserved (avoid duplicates) if (!groupedByDevice[device_id].dateObserved.values.includes(observed_at)) { groupedByDevice[device_id].dateObserved.values.push(observed_at); // Append ts to dateObserved (avoid duplicates) if (!groupedByDevice[device_id].dateObserved.values.includes(ts)) { groupedByDevice[device_id].dateObserved.values.push(ts); } } }); Loading http-bridge/typeMaps/deviceMeasurementTypeMap.js 0 → 100644 +56 −0 Original line number Diff line number Diff line const deviceMeasurementTypeMap = { /* Each entry in the map is a key-value pair where the key is the **type of measurement** and the value is an object with the following properties: - key: A string representing the name of the property in NGSI format. - unit: A character string representing the measurement unit for the type (e.g. "C" for temperature, "%" for humidity). - min: Min is the minimum possible value for the measurement type. - max: Max is the maximum possible value for the measurement type. - slug: The slug is used as "attrs" in the controller, allowing for the selection of specific attributes to be returned in the response. */ // TODO: Replace the numbers with the actual measurement types "temperature": { key: "temperature", unit: "CEL", min: 0, max: 60, slug: "temperature" }, // Θερμοκρασία 836 στο αρχείο "humidity": { key: "humidity", unit: "P1", min: 0, max: 100, slug: "humidity" }, "energy": { key: "energy", unit: "KWH", min: 0, max: 10000, slug: "energyconsumption" }, // Κατανάλωση ενέργειας 581 line "power": { key: "power", unit: "WTT", min: 0, max: 10000, slug: "powerUsage" }, // Κατανάλωση ισχύος 1155 "luminance": { key: "luminance", unit: "A24", min: 0, max: 1000, slug: "luminance" }, // Φωτεινότητα γραμμή 1231 6: { key: "volt", unit: "V", min: 0, max: 1000, slug: "volt" }, // Voltage γραμμή 1035 7: { key: "ampere", unit: "AMP", min: 0, max: 100, slug: "current" }, // Ampere γραμμή 980 8: { key: "frequency", unit: "HTZ", min: 0, max: 1000, slug: "frequency" }, // γραμμή 1191 9: { key: "hectopascal", unit: "A97", min: 0, max: 1200, slug: "pressure" }, 10: { key: "windSpeed", unit: "KMH", min: 0, max: 50, slug: "wind-speed" }, 11: { key: "windDirection", unit: "DD", min: 0, max: 359.9, slug: "wind-direction" }, "CO2": { key: "carbonDioxide", unit: "P1", min: 0, max: 100, slug: "co2" }, 13: { key: "decibel", unit: "2N", min: 0, max: 100, slug: "noise_level" }, 14: { key: "motion", unit: "A99", min: 0, max: 1, slug: "motion_detection" }, 15: { key: "door", unit: "A99", min: 0, max: 1, slug: "magnetic_switch" }, //14: { key: "state", unit: "", min: null, max: null, slug: "state" } // Κατάσταση συσκευής 2N γραμμή 1289 }; /* NOTES * - Percentages are represented as "P1" in the unit field. * - Luminance is represented as "A24" in the unit field not candela per square meter. * - Decibels are represented as "2N" in the unit field. * * type / unit * battery level % * humidity % * CO2 % * temperature Celcius * temperature apparent Celcius * switch Off * switch On * switch On/Off * motion detection switch On/Off * magnetic switch On/Off * UV UV * power Watt * luminance cd/m2 * noise level dB * wind angle degrees * barometric pressure hPa * energy kWh * wind speed km/h */ module.exports = deviceMeasurementTypeMap; http-bridge/typeMaps/typeMaps.js +3 −4 Original line number Diff line number Diff line const weatherObservedTypeMap = require("./weatherObservedTypeMaps"); const deviceMeasurementTypeMap = require('./deviceMeasurementTypeMap'); // List of all available NGSI-LD model types const typeMaps = { "WeatherObserved": weatherObservedTypeMap, // "WaterQualityObserved": waterQualityObservedTypeMap "DeviceMeasurement": deviceMeasurementTypeMap, }; module.exports = typeMaps; Loading
http-bridge/controllers/generalControllers.js +70 −82 Original line number Diff line number Diff line Loading @@ -3,44 +3,38 @@ const NGSITranslator = require("../translator/NGSITranslator"); const typeMaps = require("../typeMaps/typeMaps"); /** * Generates a modality_id parameter based on the type and attributes provided. * If attributes are given, it will filter the modalities to only include the ones * that correspond to the given attributes. Otherwise, it will return all modalities * available for the given type. * * Based on Smart City Heraklion API * * @param {string} type The NGSI-LD type to generate the modality_id for * @param {string} type The NGSI-LD type to generate the measurement_type for * @param {string} attributes A comma-separated list of attributes to filter by * @throws {Error} If the type is invalid or no valid attributes are found * @returns {string} The modality_id parameter as a string * @returns {string[]} List of measurement types */ function generateModalityIdParam(type, attributes) { const modalityMap = typeMaps[type]; if (!modalityMap) { function generateMeasurementTypeParam(type, attributes) { const typeMap = typeMaps[type]; if (!typeMap) { throw new Error(`Invalid type: ${type}`); } // Filter attributes if provided, otherwise return all const modalityIds = attributes ? attributes.split(",").map(attr => Object.entries(modalityMap) const measurementTypes = attributes ? attributes.split(",").map(attr => Object.entries(typeMap) .find(([_, value]) => value.slug === attr)?.[0]) .filter(Boolean) : Object.keys(modalityMap); : Object.keys(typeMap); if (modalityIds.length === 0) { throw new Error(`No valid attributes found for type: ${type}`); if (measurementTypes.length === 0) { throw new Error(`No valid attributes found for type '${type}' and attributes '${attributes}'`); } return modalityIds; // console.log("Measurement types:", measurementTypes); return measurementTypes; } /** * Fetches data from the Smart City Heraklion API and translates it to NGSI-LD. * Fetches data from the provided API and translates it to NGSI-LD. * * @param {string} type The NGSI-LD type to retrieve * @param {string} [attrs] A comma-separated list of attributes to filter by * @param {Function} fetcher The specific fetcher function to use (fetchWeatherObservedTemporalData or fetchWeatherObservedRealTimeData) * @param {Function} fetcher The specific fetcher function to use (fetchTemporalData or fetchRealTimeData) * @param {Object} [additionalParams] Additional parameters to pass to the fetcher function * @param {Response} res The Express response object * @throws {Error} If the type is invalid, no valid attributes are found, or the API request fails Loading @@ -55,19 +49,17 @@ const fetchEntities = async (type, attrs, fetcher, additionalParams, res) => { return res.status(400).json({ error: `Unsupported entity type: ${type}` }); } const modalityIdParam = generateModalityIdParam(type, attrs); if (!modalityIdParam) { return res.status(400).json({ error: "Missing modality_id parameter." }); const measurementTypes = generateMeasurementTypeParam(type, attrs); if (!measurementTypes) { return res.status(400).json({ error: "Missing measurement_type parameter." }); } // Call the specific fetcher function const apiResponse = await fetcher(type, modalityIdParam, additionalParams); const apiResponse = await fetcher(type, measurementTypes, additionalParams); if (!apiResponse || !apiResponse.data) { if (!apiResponse || apiResponse.data.length === 0) { return res.status(500).json({ error: "Failed to retrieve data from API." }); } // Translate response to NGSI-LD const ngsiData = NGSITranslator.toNGSI(apiResponse.data, type); res.status(200).json(ngsiData); } catch (error) { Loading @@ -80,67 +72,63 @@ const fetchEntities = async (type, attrs, fetcher, additionalParams, res) => { * Fetches the temporal data for a given NGSI-LD entity type. * * @param {string} type The NGSI-LD type to retrieve * @param {string} modalityIdParam The modality_id parameter for filtering * @param {string} measurementTypes The measurement type parameter for filtering * @param {Object} [params] Additional parameters for the API request * @throws {Error} If the entity type is unsupported */ const fetchWeatherObservedTemporalData = async (type, modalityIds, params) => { if (!Array.isArray(modalityIds) || modalityIds.length === 0) { throw new Error(`Invalid modality IDs for type: ${type}`); } const results = { data: [] }; // Loop through each modality ID and make separate requests for (const modalityId of modalityIds) { const fetchTemporalData = async (type, measurementTypes, params) => { switch (type) { case "DeviceMeasurement": try { const apiResponse = await axios.post(`${process.env.API_URL}/rpc/measurements_averages`, { _devices: "{71,72,73,63,64,36,68,70,61,69,67,66,65,60,59,120}", _from: params.time, _to: params.endTime, _granularity: "1 hour", _modality: typeMaps[type][modalityId].key, _show_out_of_range: false const response = await axios.get(`${process.env.API_URL}/device_measurement`, { params: { measurement_type: `in.(${measurementTypes.join(",")})`, ts: params.time, // TODO: Implement proper time handling according to postgres timestamp format limit: params.limit, } }); if (apiResponse && apiResponse.data) { formattedData = apiResponse.data.map(entry => ({ device_id: modalityId, modality_id: parseInt(modalityId, 10), value: entry.avg, observed_at: entry.at })); results.data.push(...formattedData); } return response; } catch (error) { console.error(`Error fetching data for modalityId ${modalityId}:`, error.message); } console.error("Error fetching temporal data:", error.message); return { data: [] }; } return results; default: return { data: [] }; } }; /** * Fetches the latest real-time data for a given NGSI-LD entity type. * * @param {string} type The NGSI-LD type to retrieve * @param {string} modalityIdParam The modality_id parameter for filtering * @param {string} measurementTypes The measurement type parameter for filtering * @throws {Error} If the entity type is unsupported */ const fetchWeatherObservedRealTimeData = async (type, modalityIdParam) => { const fetchRealTimeData = async (type, measurementTypes, params) => { switch (type) { case "WeatherObserved": return axios.get(`${process.env.API_URL}/latest_measurements_averages`, { params: { modality_id: `in.(${Array.isArray(modalityIdParam) ? modalityIdParam.join(",") : modalityIdParam})` }, case "DeviceMeasurement": try { const response = await axios.get(`${process.env.API_URL}/device_measurement`, { params: { measurement_type: `in.(${measurementTypes.join(",")})`, order: "ts.desc", limit: params.limit || 100, // Default to 100 if limit is not provided. (MEETING: Remove this?) } }); return response; } catch (error) { console.error("Error fetching real-time data:", error.message); return { data: [] }; } default: throw new Error(`Unsupported real-time entity type: ${type}`); return { data: [] }; } }; /** * Handles incoming requests for temporal NGSI-LD entities. * Loading @@ -149,26 +137,24 @@ const fetchWeatherObservedRealTimeData = async (type, modalityIdParam) => { * @prop {string} time The timestamp to apply the temporal relationship to * @prop {string} endTime The end time for the time range query * @prop {string} attrs A comma-separated list of attributes to filter by * @prop {string} limit The maximum number of entities to return */ const getTemporalEntities = async (req, res) => { console.log("Incoming temporal request:", req.query); let { type, timerel, time, endTime, attrs } = req.query; const { type, timerel, time, endTime, attrs, limit } = req.query; // Decode URL-encoded time values (fixes %3A issue) time = decodeURIComponent(time); if (endTime) endTime = decodeURIComponent(endTime); // Ensure proper ISO 8601 formatting let adjustedEndTime = endTime; const now = new Date().toISOString(); // TODO: Implement "between" timerel and fix temporal queries not working at all. if (timerel === "after") { endTime = now; // Set endTime to current time adjustedEndTime = now; } else if (timerel === "before") { endTime = time; // Swap values for "before" time = new Date(new Date(endTime).setDate(new Date(endTime).getDate() - 30)).toISOString(); // 30 days before adjustedEndTime = time; time = new Date(new Date(adjustedEndTime).setDate(new Date(adjustedEndTime).getDate() - 30)).toISOString(); } await fetchEntities(type, attrs, fetchWeatherObservedTemporalData, { time, endTime }, res); await fetchEntities(type, attrs, fetchTemporalData, { time, endTime: adjustedEndTime, limit }, res); }; /** Loading @@ -176,11 +162,13 @@ const getTemporalEntities = async (req, res) => { * * @prop {string} type The NGSI-LD type to retrieve * @prop {string} attrs A comma-separated list of attributes to filter by * @prop {string} limit The maximum number of entities to return */ const getRealTimeEntities = async (req, res) => { console.log("Incoming real-time request:", req.query); const { type, attrs } = req.query; await fetchEntities(type, attrs, fetchWeatherObservedRealTimeData, {}, res); const { type, attrs, limit } = req.query; await fetchEntities(type, attrs, fetchRealTimeData, { limit }, res); }; module.exports = { getTemporalEntities, getRealTimeEntities };
http-bridge/translator/NGSITranslator.js +15 −15 Original line number Diff line number Diff line Loading @@ -3,14 +3,14 @@ const typeMaps = require("../typeMaps/typeMaps"); class NGSITranslator { static toNGSI(apiData, entityType) { switch (entityType) { case "WeatherObserved": return NGSITranslator.processWeatherObserved(apiData, entityType); case "DeviceMeasurement": return NGSITranslator.processDeviceMeasurement(apiData, entityType); default: throw new Error(`Unsupported entity type: ${entityType}`); } } static processWeatherObserved(apiData, entityType) { static processDeviceMeasurement(apiData, entityType) { const typeMap = typeMaps[entityType]; if (!typeMap) { throw new Error(`No mapping found for entity type: ${entityType}`); Loading @@ -19,7 +19,7 @@ class NGSITranslator { const groupedByDevice = {}; apiData.forEach(item => { const { device_id, modality_id, value, observed_at } = item; const { item: device_id, measurement_type, value, ts } = item; // Ensure the device entry exists if (!groupedByDevice[device_id]) { Loading @@ -28,30 +28,30 @@ class NGSITranslator { type: entityType, dateObserved: { type: "Property", values: [] }, "@context": [ "https://raw.githubusercontent.com/smart-data-models/dataModel.Weather/master/context.jsonld", "https://raw.githubusercontent.com/smart-data-models/dataModel.Device/master/context.jsonld", "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld" ] }; } const modalityInfo = typeMap[modality_id]; if (modalityInfo) { const measurementInfo = typeMap[measurement_type]; if (measurementInfo) { // Ensure property exists, else initialize if (!groupedByDevice[device_id][modalityInfo.key]) { groupedByDevice[device_id][modalityInfo.key] = { if (!groupedByDevice[device_id][measurementInfo.key]) { groupedByDevice[device_id][measurementInfo.key] = { type: "Property", values: [] }; } // Append new observation instead of overwriting groupedByDevice[device_id][modalityInfo.key].values.push( NGSITranslator.createProperty(value, modalityInfo.unit, modalityInfo.min, modalityInfo.max, modalityInfo.slug, observed_at) groupedByDevice[device_id][measurementInfo.key].values.push( NGSITranslator.createProperty(value, measurementInfo.unit, measurementInfo.min, measurementInfo.max, measurementInfo.slug, ts) ); // Append observed_at to dateObserved (avoid duplicates) if (!groupedByDevice[device_id].dateObserved.values.includes(observed_at)) { groupedByDevice[device_id].dateObserved.values.push(observed_at); // Append ts to dateObserved (avoid duplicates) if (!groupedByDevice[device_id].dateObserved.values.includes(ts)) { groupedByDevice[device_id].dateObserved.values.push(ts); } } }); Loading
http-bridge/typeMaps/deviceMeasurementTypeMap.js 0 → 100644 +56 −0 Original line number Diff line number Diff line const deviceMeasurementTypeMap = { /* Each entry in the map is a key-value pair where the key is the **type of measurement** and the value is an object with the following properties: - key: A string representing the name of the property in NGSI format. - unit: A character string representing the measurement unit for the type (e.g. "C" for temperature, "%" for humidity). - min: Min is the minimum possible value for the measurement type. - max: Max is the maximum possible value for the measurement type. - slug: The slug is used as "attrs" in the controller, allowing for the selection of specific attributes to be returned in the response. */ // TODO: Replace the numbers with the actual measurement types "temperature": { key: "temperature", unit: "CEL", min: 0, max: 60, slug: "temperature" }, // Θερμοκρασία 836 στο αρχείο "humidity": { key: "humidity", unit: "P1", min: 0, max: 100, slug: "humidity" }, "energy": { key: "energy", unit: "KWH", min: 0, max: 10000, slug: "energyconsumption" }, // Κατανάλωση ενέργειας 581 line "power": { key: "power", unit: "WTT", min: 0, max: 10000, slug: "powerUsage" }, // Κατανάλωση ισχύος 1155 "luminance": { key: "luminance", unit: "A24", min: 0, max: 1000, slug: "luminance" }, // Φωτεινότητα γραμμή 1231 6: { key: "volt", unit: "V", min: 0, max: 1000, slug: "volt" }, // Voltage γραμμή 1035 7: { key: "ampere", unit: "AMP", min: 0, max: 100, slug: "current" }, // Ampere γραμμή 980 8: { key: "frequency", unit: "HTZ", min: 0, max: 1000, slug: "frequency" }, // γραμμή 1191 9: { key: "hectopascal", unit: "A97", min: 0, max: 1200, slug: "pressure" }, 10: { key: "windSpeed", unit: "KMH", min: 0, max: 50, slug: "wind-speed" }, 11: { key: "windDirection", unit: "DD", min: 0, max: 359.9, slug: "wind-direction" }, "CO2": { key: "carbonDioxide", unit: "P1", min: 0, max: 100, slug: "co2" }, 13: { key: "decibel", unit: "2N", min: 0, max: 100, slug: "noise_level" }, 14: { key: "motion", unit: "A99", min: 0, max: 1, slug: "motion_detection" }, 15: { key: "door", unit: "A99", min: 0, max: 1, slug: "magnetic_switch" }, //14: { key: "state", unit: "", min: null, max: null, slug: "state" } // Κατάσταση συσκευής 2N γραμμή 1289 }; /* NOTES * - Percentages are represented as "P1" in the unit field. * - Luminance is represented as "A24" in the unit field not candela per square meter. * - Decibels are represented as "2N" in the unit field. * * type / unit * battery level % * humidity % * CO2 % * temperature Celcius * temperature apparent Celcius * switch Off * switch On * switch On/Off * motion detection switch On/Off * magnetic switch On/Off * UV UV * power Watt * luminance cd/m2 * noise level dB * wind angle degrees * barometric pressure hPa * energy kWh * wind speed km/h */ module.exports = deviceMeasurementTypeMap;
http-bridge/typeMaps/typeMaps.js +3 −4 Original line number Diff line number Diff line const weatherObservedTypeMap = require("./weatherObservedTypeMaps"); const deviceMeasurementTypeMap = require('./deviceMeasurementTypeMap'); // List of all available NGSI-LD model types const typeMaps = { "WeatherObserved": weatherObservedTypeMap, // "WaterQualityObserved": waterQualityObservedTypeMap "DeviceMeasurement": deviceMeasurementTypeMap, }; module.exports = typeMaps;