calculateTemperatureDiff.mjs 8.62 KB
Newer Older
1
2
"use strict";

3
4
5
6
7
import {
  checkForAndDeleteUniqueObservationsFromLargerArray,
  extractSamplingRateFromDatastreamName,
  extractBuildingIdFromDatastreamName,
} from "./chartHelpers.mjs";
8

Pithon Kabiro's avatar
Pithon Kabiro committed
9
10
import { getMetadataPlusObservationsFromSingleOrMultipleDatastreams } from "./fetchData.mjs";

11
import { extractPhenomenonNameFromDatastreamName } from "./fetchedDataProcessing.mjs";
12

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
 * Calculate the temperature difference, dT, between Vorlauf temperature [VL] and
 * Rücklauf temperature [RL] (i.e., dT = VL - RL) for a single building
 *
 * @param {Array} observationsPlusMetadataArr A 1*2 array, where the first element is an array made up of two arrays of observations and the second element is an array of two metadata objects
 * @returns {Array} An array made up of timestamps + values for dT
 */
const calculateVorlaufMinusRuecklaufTemperatureObservations = function (
  observationsPlusMetadataArr
) {
  // Extract Vorlauf temperature, Ruecklauf temperature; first element of array
  const [[vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr]] =
    observationsPlusMetadataArr;

  // Compare the lengths of the observations arrays for VL and RL,
  // delete the unique observation(s), if necessary
  const [vorlaufTemperatureObsFinalArr, ruecklaufTemperatureObsFinalArr] =
    vorlaufTemperatureObsArr.length === ruecklaufTemperatureObsArr.length
      ? [vorlaufTemperatureObsArr, ruecklaufTemperatureObsArr]
      : checkForAndDeleteUniqueObservationsFromLargerArray(
          vorlaufTemperatureObsArr,
          ruecklaufTemperatureObsArr
        );

  // Extract the temperature values
  const vorlaufTemperatureValues = vorlaufTemperatureObsFinalArr.map(
    (vlTempObs) => vlTempObs[1]
  );
  const ruecklaufTemperatureValues = ruecklaufTemperatureObsFinalArr.map(
    (rlTempObs) => rlTempObs[1]
  );

  // Check that the final observation arrays have equal lengths
  if (
    vorlaufTemperatureObsFinalArr.length !==
    ruecklaufTemperatureObsFinalArr.length
  ) {
    throw new Error(
      "The two observations arrays (VL and RL) have different lengths"
    );
  }
  // The final observation arrays now have equal length, we need only use one of them for looping
  // Resulting array contains the following pairs (timestamp + dT)
  else {
    return vorlaufTemperatureObsFinalArr.map((vlTempObsFinal, i) => {
      // Use timestamp from VL, since is equal to that of RL
      const timestamp = vlTempObsFinal[0];

      // Case 1: One of the observation values is `null`,
      // no need to calculate temperature difference
      // Case 2: Neither of the observation values is `null`,
      // calculate temperature difference
      return vorlaufTemperatureValues[i] === null ||
        ruecklaufTemperatureValues[i] === null
        ? [timestamp, null]
        : [
            timestamp,
            vorlaufTemperatureValues[i] - ruecklaufTemperatureValues[i],
          ];
    });
  }
};

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
 * Create synthetic metadata for the temperature difference, dT, between Vorlauf temperature [VL] and
 * Rücklauf temperature [RL] (i.e., dT = VL - RL) of a single building
 *
 * @param {Array} observationsPlusMetadataArr A 1*2 array, where the first element is an array made up of two arrays of observations and the second element is an array of two metadata objects
 * @returns {Object} A metadata object for dT, made up of three properties: description, name and unit of measurement
 */
const createVorlaufMinusRuecklaufTemperatureMetadata = function (
  observationsPlusMetadataArr
) {
  // Extract metadata; second element of array, note that we skip the first element
  const [, [metadataVorlauf, metadataRuecklauf]] = observationsPlusMetadataArr;

  // From Vorlauf metadata, extract `name` and `unitOfMeasurement`
  const {
    name: datastreamNameVorlauf,
    unitOfMeasurement: unitOfMeasurementVorlauf,
  } = metadataVorlauf;

  // From Ruecklauf metadata, extract `name`
  const { name: datastreamNameRuecklauf } = metadataRuecklauf;

  // Extract the phenomenon names from the Datastream names
  const phenomenonNameVorlauf = extractPhenomenonNameFromDatastreamName(
    datastreamNameVorlauf
  );
  const phenomenonNameRuecklauf = extractPhenomenonNameFromDatastreamName(
    datastreamNameRuecklauf
  );

  // Create our custom datastream description text
  // The resulting datastream description string has two `temperature` substrings;
  // replace the first occurence with an empty string
  const descriptionTempDifference =
    `Computed dT: ${phenomenonNameVorlauf} minus ${phenomenonNameRuecklauf}`.replace(
      "temperature",
      ""
    );

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  // Create an array of the VL datastream name, this array has only one element.
  // We need this structure since the functions `extractBuildingIdFromDatastreamName`
  // and `extractSamplingRateFromDatastreamName` expect an array of datastream names
  const datastreamNameVorlaufArr = [datastreamNameVorlauf];

  // Extract the building ID from the datastream name
  const buildingId = extractBuildingIdFromDatastreamName(
    datastreamNameVorlaufArr
  );

  // Extract the sampling rate from the datastream name
  const samplingRate = extractSamplingRateFromDatastreamName(
    datastreamNameVorlaufArr
  );

130
  // Create our custom datastream name text
131
  const nameTempDifference = `${buildingId} / dT Temperature difference (VL-RL) DS:${samplingRate}`;
132
133
134
135
136
137
138
139

  return {
    descriptionTempDifference,
    nameTempDifference,
    unitOfMeasurementVorlauf,
  };
};

140
/**
141
142
143
 * Calculate the temperature difference, dT, between Vorlauf temperature [VL] and
 * Rücklauf temperature [RL] (i.e., dT = VL - RL). In addition, create synthetic metadata
 * for the temperature difference
144
 * @async
145
146
 * @param {String} baseUrl Base URL of the STA server
 * @param {Object} urlParams The URL parameters to be sent together with the GET request
147
148
 * @param {Array} buildingSamplingRateNestedArr A N*1 array (where N >= 1) containing a nested array of buildings & sampling rates as strings, i.e. [["101", "15min"]] or [["101", "15min"], ["102", "60min"]] or [["101", "15min"], ["102", "60min"], ["225", "60min"]], etc
 * @returns {Promise} A promise that contains a 1*2 array (the first element is an array that contans N Observations arrays; and the second element is an array of N Datastream metadata objects) when fulfilled
149
150
151
152
 */
export const calculateVorlaufMinusRuecklaufTemperature = async function (
  baseUrl,
  urlParams,
153
  buildingSamplingRateNestedArr
154
155
) {
  try {
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
    // Arrays to store our results
    const combinedObservationsArr = [];
    const combinedMetadataArr = [];

    const buildingDataPointSamplingRateNestedTwiceArr =
      buildingSamplingRateNestedArr.map((bldgSmplngRate) => {
        // The building ID is the first element, sampling rate is second element
        return [
          [bldgSmplngRate[0], "vl", bldgSmplngRate[1]],
          [bldgSmplngRate[0], "rl", bldgSmplngRate[1]],
        ];
      });

    // Note: We have to use a for/of loop here due to the asynchronous nature of our code
    for (const bldgDataPtSamplingRateNestedArr of buildingDataPointSamplingRateNestedTwiceArr) {
      const observationsPlusMetadata =
        await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
          baseUrl,
          urlParams,
          bldgDataPtSamplingRateNestedArr
        );

178
179
180
181
182
183
184
185
186
187
      // If there is an error in fetching metadata + observations,
      // the returned array will have this structure: [[undefined, undefined], undefined]
      // Note that the second element is not an array as we would expect but is a
      // a single `undefined` value
      if (typeof observationsPlusMetadata[0][0] === "undefined") {
        throw new Error(
          `There was a problem in calculating the temperature difference (dT).\nThis is most likely due to a problem in fetching metadata and observations`
        );
      }

188
      // dT observations (timestamp + value)
189
      const vorlaufMinusRuecklaufTemperatureObs =
190
191
192
193
        calculateVorlaufMinusRuecklaufTemperatureObservations(
          observationsPlusMetadata
        );

194
195
196
197
198
199
200
      // dT metadata
      const {
        descriptionTempDifference,
        nameTempDifference,
        unitOfMeasurementVorlauf,
      } = createVorlaufMinusRuecklaufTemperatureMetadata(
        observationsPlusMetadata
201
202
      );

203
204
205
206
207
208
209
210
211
212
213
      // The datastream object that we return needs to have these property names
      const description = descriptionTempDifference;
      const name = nameTempDifference;
      const unitOfMeasurement = unitOfMeasurementVorlauf;

      // Add the observations and metadata to our arrays
      combinedObservationsArr.push(vorlaufMinusRuecklaufTemperatureObs);
      combinedMetadataArr.push({ description, name, unitOfMeasurement });
    }

    return [combinedObservationsArr, combinedMetadataArr];
214
215
216
217
  } catch (err) {
    console.error(err);
  }
};