"use strict"; import { BASE_URL, QUERY_PARAMS_COMBINED, getDatastreamIdFromBuildingNumber, getDatastreamUrl, getObservationsUrl, axiosGetRequest, getDatastreamMetadata, formatDatastreamMetadataForChart, formatSTAResponseForHeatMap, drawHeatMapHC, formatSTAResponseForLineChart, drawLineChartHC, getCombinedObservationsFromAllNextLinks, getMetadataPlusObservationsForChart, } from "./appChart.js"; const buildingsAvailableSensorsArr = [ ["--Select--", "", ""], ["Bau 101", "Vorlauftemperatur", "15 min"], ["Bau 101", "Vorlauftemperatur", "60 min"], ["Bau 101", "Rücklauftemperatur", "15 min"], ["Bau 101", "Rücklauftemperatur", "60 min"], ["Bau 102", "Vorlauftemperatur", "15 min"], ["Bau 102", "Vorlauftemperatur", "60 min"], ["Bau 102", "Rücklauftemperatur", "15 min"], ["Bau 102", "Rücklauftemperatur", "60 min"], ["Bau 107", "Vorlauftemperatur", "15 min"], ["Bau 107", "Vorlauftemperatur", "60 min"], ["Bau 107", "Rücklauftemperatur", "15 min"], ["Bau 107", "Rücklauftemperatur", "60 min"], ["Bau 112", "Vorlauftemperatur", "15 min"], ["Bau 112", "Vorlauftemperatur", "60 min"], ["Bau 112", "Rücklauftemperatur", "15 min"], ["Bau 112", "Rücklauftemperatur", "60 min"], ["Bau 125", "Vorlauftemperatur", "15 min"], ["Bau 125", "Vorlauftemperatur", "60 min"], ["Bau 125", "Rücklauftemperatur", "15 min"], ["Bau 125", "Rücklauftemperatur", "60 min"], ["Bau 225", "Vorlauftemperatur", "15 min"], ["Bau 225", "Vorlauftemperatur", "60 min"], ["Bau 225", "Rücklauftemperatur", "15 min"], ["Bau 225", "Rücklauftemperatur", "60 min"], ["Bau 225", "Durchfluss", "15 min"], ["Bau 225", "Durchfluss", "60 min"], ["Bau 225", "Leistung", "15 min"], ["Bau 225", "Leistung", "60 min"], ["Bau 225", "Energie", "15 min"], ["Bau 225", "Energie", "60 min"], ["Bau 225", "Energie_VERBR", "15 min"], ["Bau 225", "Energie_VERBR", "60 min"], ]; /** * Get the unique values from an array * @param {Array} dataArr Input array * @param {Number} index Integer representing a zero-based index of the columns in the array * @returns {Array} An array of unique options */ const getUniqueValues = function (dataArr, index) { const uniqueOptions = new Set(); dataArr.forEach((row) => uniqueOptions.add(row[index])); return [...uniqueOptions]; }; /** * Populate the HTML elements that make up a drop down list * @param {String} element String corresponding to the ID of a drop down HTML element * @param {Array} uniqueValuesArr An array of unique values * @returns {undefined} */ const populateDropDown = function (element, uniqueValuesArr) { element.innerHTML = ""; uniqueValuesArr.forEach((item) => { const option = document.createElement("option"); option.textContent = item; element.appendChild(option); }); }; /** * Filter an array using filter strings of variable length * @param {*} dataArr Input array * @param {*} filtersAsArray An array of filter strings * @returns {Array} An array that contains the filter result */ const filterArray = function (dataArr, filtersAsArray) { return dataArr.filter((row) => filtersAsArray.every((filterItem, i) => filterItem === row[i]) ); }; /** * Create a drop down list in the HTML document * @param {*} dataArr Input array * @param {*} filtersAsArray An array of strings tp be used as filters * @param {*} targetElement String corresponding to the ID of a drop down HTML element * @returns {undefined} */ const makeDropDown = function (dataArr, filtersAsArray, targetElement) { const filteredArr = filterArray(dataArr, filtersAsArray); const uniqueList = getUniqueValues(filteredArr, filtersAsArray.length); getUniqueValues(dataArr); populateDropDown(targetElement, uniqueList); }; /** * Use the `makeDropDown` function to create the first two levels of the linked drop down lists * @returns {undefined} */ const applyDropDown = function () { const selectLevel1Value = document.querySelector("#drop-down--bldg").value; const selectLevel2 = document.querySelector("#drop-down--sensor"); makeDropDown(buildingsAvailableSensorsArr, [selectLevel1Value], selectLevel2); applyDropDown2(); }; /** * Use the `makeDropDown` function to create the third level of the linked drop down lists * @returns {undefined} */ const applyDropDown2 = function () { const selectLevel1Value = document.querySelector("#drop-down--bldg").value; const selectLevel2Value = document.querySelector("#drop-down--sensor").value; const selectLevel3 = document.querySelector("#drop-down--sampling-rate"); makeDropDown( buildingsAvailableSensorsArr, [selectLevel1Value, selectLevel2Value], selectLevel3 ); }; /** * Use the `populateDropDown` function to populate the first level drop down * @returns {undefined} */ const populateFirstLevelDropDown = function () { const el = document.querySelector("#drop-down--bldg"); const uniqueList = getUniqueValues(buildingsAvailableSensorsArr, 0); populateDropDown(el, uniqueList); }; document .querySelector("#drop-down--bldg") .addEventListener("change", applyDropDown); document .querySelector("#drop-down--sensor") .addEventListener("change", applyDropDown2); // These functions run after "DOMContentLoaded" event populateFirstLevelDropDown(); applyDropDown(); /** * Get the values from the currently selected options in the linked drop dpwn lists * @returns {Array} An array containing the values of the selected options */ const getSelectedOptionsFromDropDownLists = function () { const selectedBuilding = document.querySelector("#drop-down--bldg").value; const selectedSensor = document.querySelector("#drop-down--sensor").value; const selectedSamplingRate = document.querySelector( "#drop-down--sampling-rate" ).value; if ( selectedBuilding === "--Select--" || selectedSensor === "" || selectedSamplingRate === "" ) return; return [selectedBuilding, selectedSensor, selectedSamplingRate]; }; /** * Get the abbreviated form of building IDs, phenomenon names and sensor sampling rates * @param {String} buildingFullForm A string representation of the full form of a building ID * @param {String} phenomenonFullForm A string representation of the full form of a phenomenon name * @param {String} samplingRateFullForm A string representation of the full form of a sensor's sampling rate * @returns {Array} An array of abbreviated strings */ const getBuildingSensorSamplingRateAbbreviation = function ( buildingFullForm, phenomenonFullForm, samplingRateFullForm ) { const fullFormToAbbreviationMapping = { buildings: { "Bau 101": "101", "Bau 102": "102", "Bau 107": "107", "Bau 112": "112, 118", "Bau 125": "125", "Bau 225": "225", }, phenomenon: { Vorlauftemperatur: "vl", Rücklauftemperatur: "rl", Durchfluss: "flow", Leistung: "power", Energie: "energy", Energie_VERBR: "energy_verb", }, samplingRate: { "15 min": "15min", "60 min": "60min", }, }; const buildingAbbrev = fullFormToAbbreviationMapping["buildings"]?.[buildingFullForm]; const phenomenonAbbrev = fullFormToAbbreviationMapping["phenomenon"]?.[phenomenonFullForm]; const samplingRateAbbrev = fullFormToAbbreviationMapping["samplingRate"]?.[samplingRateFullForm]; return [buildingAbbrev, phenomenonAbbrev, samplingRateAbbrev]; }; /** * Show a loading indicator at the start of an async task. The indicator consists of a spinner and a transluscent mask placed on top of page elements * @returns {undefined} */ const showLoadingSpinner = function () { const loadingIndicatorMask = document.querySelector("#loadingIndicator"); const loadingIconSpinner = document.querySelector("#loadingIcon"); loadingIndicatorMask.style.display = "block"; loadingIconSpinner.style.display = "block"; }; /** * Hide the loading indicator after completion of the async tasks * @returns {undefined} */ const hideLoadingSpinner = function () { const loadingIndicatorMask = document.querySelector("#loadingIndicator"); const loadingIconSpinner = document.querySelector("#loadingIcon"); loadingIndicatorMask.style.display = "none"; loadingIconSpinner.style.display = "none"; }; /** * Callback function for chart selection using drop down list * @returns {undefined} */ const selectChartTypeFromDropDown = async function () { try { const selectedOptions = getSelectedOptionsFromDropDownLists(); if (selectedOptions === undefined) return; const abbreviationsArr = getBuildingSensorSamplingRateAbbreviation( ...selectedOptions ); const selectedDatastream = getDatastreamIdFromBuildingNumber( ...abbreviationsArr ); const selectedChartType = document.querySelector( "#drop-down--chart-type" ).value; if (selectedChartType === "--Select--") return; // Display the loading indicator showLoadingSpinner(); const URL_DATASTREAM = getDatastreamUrl(BASE_URL, selectedDatastream); const URL_OBSERVATIONS = getObservationsUrl(BASE_URL, selectedDatastream); // Create promises const promiseDatastreamMetadata = getDatastreamMetadata(URL_DATASTREAM); const promiseCombinedObservations = getCombinedObservationsFromAllNextLinks( axiosGetRequest(URL_OBSERVATIONS, QUERY_PARAMS_COMBINED) ); // Pass promises to our async function const metadataPlusObservations = await getMetadataPlusObservationsForChart([ promiseCombinedObservations, promiseDatastreamMetadata, ]); // Extract the metadata and the observations from resulting array const combinedObs = metadataPlusObservations[0]; const datastreamMetadata = metadataPlusObservations[1]; if (selectedChartType === "Line") { drawLineChartHC( formatSTAResponseForLineChart(combinedObs), formatDatastreamMetadataForChart(datastreamMetadata) ); } else if (selectedChartType === "Heatmap") { drawHeatMapHC( formatSTAResponseForHeatMap(combinedObs), formatDatastreamMetadataForChart(datastreamMetadata) ); } } catch (err) { console.error(err); } finally { // Hide the loading indicator hideLoadingSpinner(); } }; document .querySelector("#drop-down--chart-type") .addEventListener("change", selectChartTypeFromDropDown);