"use strict";
// DEBUG:
// Observations WITHOUT data gap - Bau 225 / Datastream ID = 80
// Observations WITH data gap - Bau 112 / Datastream ID = 78
export const BASE_URL = "http://193.196.39.91:8080/frost-icity-tp31/v1.1";
/**
* Retrieve the datastream ID that corresponds to a particular building
* @param {*} buildingNumber Integer representing the building ID
* @param {*} phenomenon String representing the phenomenon of interest
* @param {*} samplingRate String representing the sampling rate of the observations
* @returns {Number} Datastream corresponding to the input building
*/
export const getDatastreamIdFromBuildingNumber = function (
buildingNumber,
phenomenon,
samplingRate
) {
const buildingToDatastreamMapping = {
101: {
vl: { "15min": "69", "60min": "75" },
rl: { "15min": "81", "60min": "87" },
flow: { "15min": "93", "60min": "99" },
power: { "15min": "105", "60min": "111" },
energy: { "15min": "117", "60min": "123" },
energy_verb: { "15min": "129", "60min": "135" },
},
102: {
vl: { "15min": "70", "60min": "76" },
rl: { "15min": "82", "60min": "88" },
flow: { "15min": "94", "60min": "100" },
power: { "15min": "106", "60min": "112" },
energy: { "15min": "118", "60min": "124" },
energy_verb: { "15min": "130", "60min": "136" },
},
107: {
vl: { "15min": "71", "60min": "77" },
rl: { "15min": "83", "60min": "89" },
flow: { "15min": "95", "60min": "101" },
power: { "15min": "107", "60min": "113" },
energy: { "15min": "119", "60min": "125" },
energy_verb: { "15min": "131", "60min": "137" },
},
"112, 118": {
vl: { "15min": "72", "60min": "78" },
rl: { "15min": "84", "60min": "90" },
flow: { "15min": "96", "60min": "102" },
power: { "15min": "108", "60min": "114" },
energy: { "15min": "120", "60min": "126" },
energy_verb: { "15min": "132", "60min": "138" },
},
125: {
vl: { "15min": "73", "60min": "79" },
rl: { "15min": "85", "60min": "91" },
flow: { "15min": "97", "60min": "103" },
power: { "15min": "109", "60min": "115" },
energy: { "15min": "121", "60min": "127" },
energy_verb: { "15min": "133", "60min": "139" },
},
225: {
vl: { "15min": "74", "60min": "80" },
rl: { "15min": "86", "60min": "92" },
flow: { "15min": "98", "60min": "104" },
power: { "15min": "110", "60min": "116" },
energy: { "15min": "122", "60min": "128" },
energy_verb: { "15min": "134", "60min": "140" },
},
};
if (!buildingNumber) return;
// check if building is contained in mapping object
if (!(buildingNumber in buildingToDatastreamMapping)) return;
const datastreamIdMatched = Number(
buildingToDatastreamMapping[buildingNumber][phenomenon][samplingRate]
);
return datastreamIdMatched;
};
/**
* Create URL to fetch the Observations corresponding to a provided Datastream
* @param {String} baseUrl Base URl of the STA server
* @param {Number} datastreamID - Integer representing the Datastream ID
* @returns {String} URL string for fetching the Observations corresponding to a Datastream
*/
export const getObservationsUrl = function (baseUrl, datastreamID) {
if (!datastreamID) return;
const fullDatastreamURL = `${baseUrl}/Datastreams(${datastreamID})/Observations`;
return fullDatastreamURL;
};
/**
* Create a temporal filter string for the fetched Observations
* @param {String} dateStart Start date in YYYY-MM-DD format
* @param {String} dateStop Stop date in YYYY-MM-DD format
* @returns {String} Temporal filter string
*/
const createTemporalFilterString = function (dateStart, dateStop) {
if (!dateStart || !dateStop) return;
const filterString = `resultTime ge ${dateStart}T00:00:00.000Z and resultTime le ${dateStop}T00:00:00.000Z`;
return filterString;
};
// const BASE_URL_OBSERVATIONS = getObservationsUrl(80);
const PARAM_RESULT_FORMAT = "dataArray";
const PARAM_ORDER_BY = "phenomenonTime asc";
const PARAM_FILTER = createTemporalFilterString("2020-01-01", "2021-01-01");
const PARAM_SELECT = "result,phenomenonTime";
export const PARAM_OBJ = {
"$resultFormat": PARAM_RESULT_FORMAT,
"$orderBy": PARAM_ORDER_BY,
"$filter": PARAM_FILTER,
"$select": PARAM_SELECT,
};
/**
* Perform a GET request using the Axios library
* @param {String} urlObservations A URL that fetches Observations from STA instance
* @param {Object} urlParamObj The URL parameters to be sent together with the GET request
* @returns {Object} Axios request object
*/
export const axiosGetRequest = function (urlObservations, urlParamObj) {
return axios.get(urlObservations, {
params: urlParamObj,
});
};
/**
* Format the response from SensorThings API to make it suitable for heatmap
* @param {Array} obsArray Response from SensorThings API as array
* @returns {Array} Array of formatted observations suitable for use in a heatmap
*/
export const formatSTAResponseForHeatMap = function (obsArray) {
if (!obsArray) return;
const dataSTAFormatted = [];
obsArray.forEach((obs) => {
// Get the date/time string; first element in input array; remove trailing "Z"
const obsDateTimeInput = obs[0].slice(0, -1);
// Get the "date" part of an observation
const obsDateInput = obs[0].slice(0, 10);
// Create Date objects
const obsDateTime = new Date(obsDateTimeInput);
const obsDate = new Date(obsDateInput);
// x-axis -> timestamp; will be the same for observations from the same date
const timestamp = Date.parse(obsDate);
// y-axis -> hourOfDay
const hourOfDay = obsDateTime.getHours();
// value -> the observation's value; second element in input array
const value = obs[1];
dataSTAFormatted.push([timestamp, hourOfDay, value]);
});
return dataSTAFormatted;
};
/**
* Draw a heatmap using Highcharts library
* @param {Array} formattedObsArrayForHeatmap Response from SensorThings API formatted for use in a heatmap
* @returns {Object} Highcharts library heatmap object
*/
export const drawHeatMapHC = function (formattedObsArrayForHeatmap) {
Highcharts.chart("chart-heatmap", {
chart: {
type: "heatmap",
zoomType: "x",
},
boost: {
useGPUTranslations: true,
},
title: {
text: "Inlet flow (Vorlauf)",
align: "left",
x: 40,
},
subtitle: {
text: "Temperature variation by day and hour in 2020",
align: "left",
x: 40,
},
xAxis: {
type: "datetime",
// min: Date.UTC(2017, 0, 1),
// max: Date.UTC(2017, 11, 31, 23, 59, 59),
labels: {
align: "left",
x: 5,
y: 14,
format: "{value:%B}", // long month
},
showLastLabel: false,
tickLength: 16,
},
yAxis: {
title: {
text: null,
},
labels: {
format: "{value}:00",
},
minPadding: 0,
maxPadding: 0,
startOnTick: false,
endOnTick: false,
// tickPositions: [0, 6, 12, 18, 24],
tickPositions: [0, 3, 6, 9, 12, 15, 18, 21, 24],
tickWidth: 1,
min: 0,
max: 23,
reversed: true,
},
colorAxis: {
stops: [
[0, "#3060cf"],
[0.5, "#fffbbc"],
[0.9, "#c4463a"],
[1, "#c4463a"],
],
min: 60,
max: 85,
startOnTick: false,
endOnTick: false,
labels: {
format: "{value}℃",
},
},
series: [
{
data: formattedObsArrayForHeatmap,
boostThreshold: 100,
borderWidth: 0,
nullColor: "#525252",
colsize: 24 * 36e5, // one day
tooltip: {
headerFormat: "Temperature
",
pointFormat:
"{point.x:%e %b, %Y} {point.y}:00: {point.value} ℃",
},
turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release
},
],
});
};
/**
* Convert the observations' phenomenonTime from an ISO 8601 string to Unix epoch
* @param {Array} obsArray Response from SensorThings API as array
* @returns {Array} Array of formatted observations suitable for use in a line chart
*/
export const formatSTAResponseForLineChart = function (obsArray) {
if (!obsArray) return;
const dataSTAFormatted = [];
obsArray.forEach((result) => {
const timestampObs = new Date(result[0].slice(0, -1)).getTime(); // slice() removes trailing "Z" character in timestamp
const valueObs = result[1];
dataSTAFormatted.push([timestampObs, valueObs]);
});
return dataSTAFormatted;
};
/**
* Draw a line chart using Highcharts library
* @param {Array} formattedObsArrayForLineChart - Response from SensorThings API formatted for use in a line chart
* @returns {Object} Highcharts library line chart object
*/
export const drawLineChartHC = function (formattedObsArrayForLineChart) {
// Create the chart
Highcharts.stockChart("chart-line", {
chart: {
zoomType: "x",
},
rangeSelector: {
selected: 1,
},
title: {
text: "Inlet flow (Vorlauf)",
"align": "left",
},
subtitle: {
text: "Temperature variation by hour in 2020",
align: "left",
},
series: [
{
name: "AAPL",
data: formattedObsArrayForLineChart,
tooltip: {
valueDecimals: 2,
},
turboThreshold: Number.MAX_VALUE, // #3404, remove after 4.0.5 release
},
],
});
};
/**
* Follows "@iot.nextLink" links in SensorThingsAPI's response
* Appends new results to existing results
* @async
* @param {Object} responsePromise Promise object
* @returns {Object} - Object containing results from all the "@iot.nextLink" links
*/
export const followNextLink = function (responsePromise) {
if (!responsePromise) return;
return responsePromise
.then(function (lastSuccess) {
if (lastSuccess.data["@iot.nextLink"]) {
return followNextLink(
axios.get(lastSuccess.data["@iot.nextLink"])
).then(function (nextLinkSuccess) {
nextLinkSuccess.data.value = lastSuccess.data.value.concat(
nextLinkSuccess.data.value
);
return nextLinkSuccess;
});
} else {
return lastSuccess;
}
})
.catch(function (err) {
console.log(err);
});
};
// Get "ALL" the Observations that satisfy our query
// followNextLink(axiosGetRequest)
// .then((success) => {
// const successValue = success.data.value;
// // Array that will hold the combined observations
// const combinedObservations = [];
// successValue.forEach((dataObj) => {
// // Each page of results will have a dataArray that holds the observations
// const dataArrays = dataObj.dataArray;
// combinedObservations.push(...dataArrays);
// });
// // DEBUG: Check total number of observations
// console.log(combinedObservations.length);
// // DEBUG: Print the array of observations
// console.log(combinedObservations);
// return combinedObservations;
// })
// .catch((err) => {
// console.log(err);
// })
// .then((observationArr) => {
// drawHeatMapHC(observationArr);
// drawLineChartHC(observationArr);
// });