refactor: split ChargingStationUtils class static methods into functions
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16ServiceUtils.ts
CommitLineData
edd13439 1// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
c8eeb62b 2
130783a7
JB
3import type { JSONSchemaType } from 'ajv';
4
fba11dc6 5import { type ChargingStation, getIdTagsFile } from '../../../charging-station';
268a74bb 6import { OCPPError } from '../../../exception';
e7aeea18 7import {
268a74bb
JB
8 CurrentType,
9 ErrorType,
10 type JsonType,
11 type MeasurandPerPhaseSampledValueTemplates,
12 type MeasurandValues,
e7aeea18
JB
13 MeterValueContext,
14 MeterValueLocation,
15 MeterValueUnit,
66dd3447
JB
16 OCPP16AuthorizationStatus,
17 type OCPP16AuthorizeRequest,
18 type OCPP16AuthorizeResponse,
268a74bb
JB
19 type OCPP16ChargingProfile,
20 type OCPP16IncomingRequestCommand,
27782dbc 21 type OCPP16MeterValue,
e7aeea18
JB
22 OCPP16MeterValueMeasurand,
23 OCPP16MeterValuePhase,
370ae4ee 24 OCPP16RequestCommand,
268a74bb
JB
25 type OCPP16SampledValue,
26 OCPP16StandardParametersKey,
27 type OCPP16SupportedFeatureProfiles,
28 OCPPVersion,
29 type SampledValueTemplate,
30 Voltage,
31} from '../../../types';
9bf0ef23
JB
32import {
33 ACElectricUtils,
34 Constants,
35 DCElectricUtils,
36 convertToFloat,
37 convertToInt,
38 getRandomFloatFluctuatedRounded,
39 getRandomFloatRounded,
40 getRandomInteger,
41 isNotEmptyArray,
42 isNotEmptyString,
43 isNullOrUndefined,
44 isUndefined,
45 logger,
46 roundTo,
47} from '../../../utils';
4c3c0d59 48import { OCPPServiceUtils } from '../OCPPServiceUtils';
6ed92bc1 49
7bc31f9c 50export class OCPP16ServiceUtils extends OCPPServiceUtils {
370ae4ee
JB
51 public static checkFeatureProfile(
52 chargingStation: ChargingStation,
53 featureProfile: OCPP16SupportedFeatureProfiles,
54 command: OCPP16RequestCommand | OCPP16IncomingRequestCommand
55 ): boolean {
56 if (!chargingStation.hasFeatureProfile(featureProfile)) {
57 logger.warn(
58 `${chargingStation.logPrefix()} Trying to '${command}' without '${featureProfile}' feature enabled in ${
59 OCPP16StandardParametersKey.SupportedFeatureProfiles
60 } in configuration`
61 );
62 return false;
63 }
64 return true;
65 }
66
78085c42
JB
67 public static buildMeterValue(
68 chargingStation: ChargingStation,
69 connectorId: number,
70 transactionId: number,
71 interval: number,
72 debug = false
73 ): OCPP16MeterValue {
74 const meterValue: OCPP16MeterValue = {
c38f0ced 75 timestamp: new Date(),
78085c42
JB
76 sampledValue: [],
77 };
78 const connector = chargingStation.getConnectorStatus(connectorId);
79 // SoC measurand
ed3d2808 80 const socSampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab 81 chargingStation,
78085c42
JB
82 connectorId,
83 OCPP16MeterValueMeasurand.STATE_OF_CHARGE
84 );
85 if (socSampledValueTemplate) {
860ef183
JB
86 const socMaximumValue = 100;
87 const socMinimumValue = socSampledValueTemplate.minimumValue ?? 0;
78085c42 88 const socSampledValueTemplateValue = socSampledValueTemplate.value
9bf0ef23 89 ? getRandomFloatFluctuatedRounded(
78085c42
JB
90 parseInt(socSampledValueTemplate.value),
91 socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
92 )
9bf0ef23 93 : getRandomInteger(socMaximumValue, socMinimumValue);
78085c42
JB
94 meterValue.sampledValue.push(
95 OCPP16ServiceUtils.buildSampledValue(socSampledValueTemplate, socSampledValueTemplateValue)
96 );
97 const sampledValuesIndex = meterValue.sampledValue.length - 1;
860ef183 98 if (
9bf0ef23
JB
99 convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > socMaximumValue ||
100 convertToInt(meterValue.sampledValue[sampledValuesIndex].value) < socMinimumValue ||
860ef183
JB
101 debug
102 ) {
78085c42
JB
103 logger.error(
104 `${chargingStation.logPrefix()} MeterValues measurand ${
105 meterValue.sampledValue[sampledValuesIndex].measurand ??
106 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
54ebb82c 107 }: connector id ${connectorId}, transaction id ${
860ef183
JB
108 connector?.transactionId
109 }, value: ${socMinimumValue}/${
78085c42 110 meterValue.sampledValue[sampledValuesIndex].value
860ef183 111 }/${socMaximumValue}}`
78085c42
JB
112 );
113 }
114 }
115 // Voltage measurand
ed3d2808 116 const voltageSampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab 117 chargingStation,
78085c42
JB
118 connectorId,
119 OCPP16MeterValueMeasurand.VOLTAGE
120 );
121 if (voltageSampledValueTemplate) {
122 const voltageSampledValueTemplateValue = voltageSampledValueTemplate.value
123 ? parseInt(voltageSampledValueTemplate.value)
124 : chargingStation.getVoltageOut();
125 const fluctuationPercent =
126 voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT;
9bf0ef23 127 const voltageMeasurandValue = getRandomFloatFluctuatedRounded(
78085c42
JB
128 voltageSampledValueTemplateValue,
129 fluctuationPercent
130 );
131 if (
132 chargingStation.getNumberOfPhases() !== 3 ||
133 (chargingStation.getNumberOfPhases() === 3 && chargingStation.getMainVoltageMeterValues())
134 ) {
135 meterValue.sampledValue.push(
136 OCPP16ServiceUtils.buildSampledValue(voltageSampledValueTemplate, voltageMeasurandValue)
137 );
138 }
139 for (
140 let phase = 1;
141 chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
142 phase++
143 ) {
144 const phaseLineToNeutralValue = `L${phase}-N`;
145 const voltagePhaseLineToNeutralSampledValueTemplate =
ed3d2808 146 OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab 147 chargingStation,
78085c42
JB
148 connectorId,
149 OCPP16MeterValueMeasurand.VOLTAGE,
150 phaseLineToNeutralValue as OCPP16MeterValuePhase
151 );
152 let voltagePhaseLineToNeutralMeasurandValue: number;
153 if (voltagePhaseLineToNeutralSampledValueTemplate) {
154 const voltagePhaseLineToNeutralSampledValueTemplateValue =
155 voltagePhaseLineToNeutralSampledValueTemplate.value
156 ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value)
157 : chargingStation.getVoltageOut();
158 const fluctuationPhaseToNeutralPercent =
159 voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ??
160 Constants.DEFAULT_FLUCTUATION_PERCENT;
9bf0ef23 161 voltagePhaseLineToNeutralMeasurandValue = getRandomFloatFluctuatedRounded(
78085c42
JB
162 voltagePhaseLineToNeutralSampledValueTemplateValue,
163 fluctuationPhaseToNeutralPercent
164 );
165 }
166 meterValue.sampledValue.push(
167 OCPP16ServiceUtils.buildSampledValue(
168 voltagePhaseLineToNeutralSampledValueTemplate ?? voltageSampledValueTemplate,
169 voltagePhaseLineToNeutralMeasurandValue ?? voltageMeasurandValue,
72092cfc 170 undefined,
78085c42
JB
171 phaseLineToNeutralValue as OCPP16MeterValuePhase
172 )
173 );
174 if (chargingStation.getPhaseLineToLineVoltageMeterValues()) {
175 const phaseLineToLineValue = `L${phase}-L${
176 (phase + 1) % chargingStation.getNumberOfPhases() !== 0
177 ? (phase + 1) % chargingStation.getNumberOfPhases()
178 : chargingStation.getNumberOfPhases()
179 }`;
180 const voltagePhaseLineToLineSampledValueTemplate =
ed3d2808 181 OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab 182 chargingStation,
78085c42
JB
183 connectorId,
184 OCPP16MeterValueMeasurand.VOLTAGE,
185 phaseLineToLineValue as OCPP16MeterValuePhase
186 );
187 let voltagePhaseLineToLineMeasurandValue: number;
188 if (voltagePhaseLineToLineSampledValueTemplate) {
189 const voltagePhaseLineToLineSampledValueTemplateValue =
190 voltagePhaseLineToLineSampledValueTemplate.value
191 ? parseInt(voltagePhaseLineToLineSampledValueTemplate.value)
192 : Voltage.VOLTAGE_400;
193 const fluctuationPhaseLineToLinePercent =
194 voltagePhaseLineToLineSampledValueTemplate.fluctuationPercent ??
195 Constants.DEFAULT_FLUCTUATION_PERCENT;
9bf0ef23 196 voltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded(
78085c42
JB
197 voltagePhaseLineToLineSampledValueTemplateValue,
198 fluctuationPhaseLineToLinePercent
199 );
200 }
9bf0ef23 201 const defaultVoltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded(
78085c42
JB
202 Voltage.VOLTAGE_400,
203 fluctuationPercent
204 );
205 meterValue.sampledValue.push(
206 OCPP16ServiceUtils.buildSampledValue(
207 voltagePhaseLineToLineSampledValueTemplate ?? voltageSampledValueTemplate,
208 voltagePhaseLineToLineMeasurandValue ?? defaultVoltagePhaseLineToLineMeasurandValue,
72092cfc 209 undefined,
78085c42
JB
210 phaseLineToLineValue as OCPP16MeterValuePhase
211 )
212 );
213 }
214 }
215 }
216 // Power.Active.Import measurand
ed3d2808 217 const powerSampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab 218 chargingStation,
78085c42
JB
219 connectorId,
220 OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT
221 );
abe9e9dd 222 let powerPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {};
78085c42
JB
223 if (chargingStation.getNumberOfPhases() === 3) {
224 powerPerPhaseSampledValueTemplates = {
ed3d2808 225 L1: OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab 226 chargingStation,
78085c42
JB
227 connectorId,
228 OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT,
229 OCPP16MeterValuePhase.L1_N
230 ),
ed3d2808 231 L2: OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab 232 chargingStation,
78085c42
JB
233 connectorId,
234 OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT,
235 OCPP16MeterValuePhase.L2_N
236 ),
ed3d2808 237 L3: OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab 238 chargingStation,
78085c42
JB
239 connectorId,
240 OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT,
241 OCPP16MeterValuePhase.L3_N
242 ),
243 };
244 }
245 if (powerSampledValueTemplate) {
246 OCPP16ServiceUtils.checkMeasurandPowerDivider(
247 chargingStation,
248 powerSampledValueTemplate.measurand
249 );
fc040c43 250 const errMsg = `MeterValues measurand ${
78085c42
JB
251 powerSampledValueTemplate.measurand ??
252 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
253 }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
2484ac1e 254 chargingStation.templateFile
78085c42
JB
255 }, cannot calculate ${
256 powerSampledValueTemplate.measurand ??
257 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
258 } measurand value`;
abe9e9dd 259 const powerMeasurandValues = {} as MeasurandValues;
78085c42 260 const unitDivider = powerSampledValueTemplate?.unit === MeterValueUnit.KILO_WATT ? 1000 : 1;
1b6498ba
JB
261 const connectorMaximumAvailablePower =
262 chargingStation.getConnectorMaximumAvailablePower(connectorId);
263 const connectorMaximumPower = Math.round(connectorMaximumAvailablePower);
ad8537a7 264 const connectorMaximumPowerPerPhase = Math.round(
1b6498ba 265 connectorMaximumAvailablePower / chargingStation.getNumberOfPhases()
78085c42 266 );
860ef183
JB
267 const connectorMinimumPower = Math.round(powerSampledValueTemplate.minimumValue) ?? 0;
268 const connectorMinimumPowerPerPhase = Math.round(
269 connectorMinimumPower / chargingStation.getNumberOfPhases()
270 );
78085c42
JB
271 switch (chargingStation.getCurrentOutType()) {
272 case CurrentType.AC:
273 if (chargingStation.getNumberOfPhases() === 3) {
274 const defaultFluctuatedPowerPerPhase =
275 powerSampledValueTemplate.value &&
9bf0ef23 276 getRandomFloatFluctuatedRounded(
7bc31f9c
JB
277 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
278 powerSampledValueTemplate.value,
279 connectorMaximumPower / unitDivider,
280 { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
34464008 281 ) / chargingStation.getNumberOfPhases(),
78085c42
JB
282 powerSampledValueTemplate.fluctuationPercent ??
283 Constants.DEFAULT_FLUCTUATION_PERCENT
284 );
285 const phase1FluctuatedValue =
286 powerPerPhaseSampledValueTemplates?.L1?.value &&
9bf0ef23 287 getRandomFloatFluctuatedRounded(
7bc31f9c
JB
288 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
289 powerPerPhaseSampledValueTemplates.L1.value,
290 connectorMaximumPowerPerPhase / unitDivider,
291 { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
34464008 292 ),
78085c42
JB
293 powerPerPhaseSampledValueTemplates.L1.fluctuationPercent ??
294 Constants.DEFAULT_FLUCTUATION_PERCENT
295 );
296 const phase2FluctuatedValue =
297 powerPerPhaseSampledValueTemplates?.L2?.value &&
9bf0ef23 298 getRandomFloatFluctuatedRounded(
7bc31f9c
JB
299 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
300 powerPerPhaseSampledValueTemplates.L2.value,
301 connectorMaximumPowerPerPhase / unitDivider,
302 { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
34464008 303 ),
78085c42
JB
304 powerPerPhaseSampledValueTemplates.L2.fluctuationPercent ??
305 Constants.DEFAULT_FLUCTUATION_PERCENT
306 );
307 const phase3FluctuatedValue =
308 powerPerPhaseSampledValueTemplates?.L3?.value &&
9bf0ef23 309 getRandomFloatFluctuatedRounded(
7bc31f9c
JB
310 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
311 powerPerPhaseSampledValueTemplates.L3.value,
312 connectorMaximumPowerPerPhase / unitDivider,
313 { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
34464008 314 ),
78085c42
JB
315 powerPerPhaseSampledValueTemplates.L3.fluctuationPercent ??
316 Constants.DEFAULT_FLUCTUATION_PERCENT
317 );
318 powerMeasurandValues.L1 =
319 phase1FluctuatedValue ??
320 defaultFluctuatedPowerPerPhase ??
9bf0ef23 321 getRandomFloatRounded(
860ef183
JB
322 connectorMaximumPowerPerPhase / unitDivider,
323 connectorMinimumPowerPerPhase / unitDivider
324 );
78085c42
JB
325 powerMeasurandValues.L2 =
326 phase2FluctuatedValue ??
327 defaultFluctuatedPowerPerPhase ??
9bf0ef23 328 getRandomFloatRounded(
860ef183
JB
329 connectorMaximumPowerPerPhase / unitDivider,
330 connectorMinimumPowerPerPhase / unitDivider
331 );
78085c42
JB
332 powerMeasurandValues.L3 =
333 phase3FluctuatedValue ??
334 defaultFluctuatedPowerPerPhase ??
9bf0ef23 335 getRandomFloatRounded(
860ef183
JB
336 connectorMaximumPowerPerPhase / unitDivider,
337 connectorMinimumPowerPerPhase / unitDivider
338 );
78085c42
JB
339 } else {
340 powerMeasurandValues.L1 = powerSampledValueTemplate.value
9bf0ef23 341 ? getRandomFloatFluctuatedRounded(
7bc31f9c
JB
342 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
343 powerSampledValueTemplate.value,
344 connectorMaximumPower / unitDivider,
345 { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
34464008 346 ),
78085c42
JB
347 powerSampledValueTemplate.fluctuationPercent ??
348 Constants.DEFAULT_FLUCTUATION_PERCENT
349 )
9bf0ef23 350 : getRandomFloatRounded(
860ef183
JB
351 connectorMaximumPower / unitDivider,
352 connectorMinimumPower / unitDivider
353 );
78085c42
JB
354 powerMeasurandValues.L2 = 0;
355 powerMeasurandValues.L3 = 0;
356 }
9bf0ef23 357 powerMeasurandValues.allPhases = roundTo(
78085c42
JB
358 powerMeasurandValues.L1 + powerMeasurandValues.L2 + powerMeasurandValues.L3,
359 2
360 );
361 break;
362 case CurrentType.DC:
363 powerMeasurandValues.allPhases = powerSampledValueTemplate.value
9bf0ef23 364 ? getRandomFloatFluctuatedRounded(
7bc31f9c
JB
365 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
366 powerSampledValueTemplate.value,
367 connectorMaximumPower / unitDivider,
368 { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
34464008 369 ),
78085c42
JB
370 powerSampledValueTemplate.fluctuationPercent ??
371 Constants.DEFAULT_FLUCTUATION_PERCENT
372 )
9bf0ef23 373 : getRandomFloatRounded(
860ef183
JB
374 connectorMaximumPower / unitDivider,
375 connectorMinimumPower / unitDivider
376 );
78085c42
JB
377 break;
378 default:
fc040c43 379 logger.error(`${chargingStation.logPrefix()} ${errMsg}`);
78085c42
JB
380 throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
381 }
382 meterValue.sampledValue.push(
383 OCPP16ServiceUtils.buildSampledValue(
384 powerSampledValueTemplate,
385 powerMeasurandValues.allPhases
386 )
387 );
388 const sampledValuesIndex = meterValue.sampledValue.length - 1;
9bf0ef23
JB
389 const connectorMaximumPowerRounded = roundTo(connectorMaximumPower / unitDivider, 2);
390 const connectorMinimumPowerRounded = roundTo(connectorMinimumPower / unitDivider, 2);
78085c42 391 if (
9bf0ef23 392 convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) >
ad8537a7 393 connectorMaximumPowerRounded ||
9bf0ef23 394 convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) <
860ef183 395 connectorMinimumPowerRounded ||
78085c42
JB
396 debug
397 ) {
398 logger.error(
399 `${chargingStation.logPrefix()} MeterValues measurand ${
400 meterValue.sampledValue[sampledValuesIndex].measurand ??
401 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
a7529a86 402 }: connector id ${connectorId}, transaction id ${
860ef183
JB
403 connector?.transactionId
404 }, value: ${connectorMinimumPowerRounded}/${
78085c42 405 meterValue.sampledValue[sampledValuesIndex].value
ad8537a7 406 }/${connectorMaximumPowerRounded}`
78085c42
JB
407 );
408 }
409 for (
410 let phase = 1;
411 chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
412 phase++
413 ) {
414 const phaseValue = `L${phase}-N`;
415 meterValue.sampledValue.push(
416 OCPP16ServiceUtils.buildSampledValue(
417 (powerPerPhaseSampledValueTemplates[`L${phase}`] as SampledValueTemplate) ??
418 powerSampledValueTemplate,
419 powerMeasurandValues[`L${phase}`] as number,
72092cfc 420 undefined,
78085c42
JB
421 phaseValue as OCPP16MeterValuePhase
422 )
423 );
424 const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1;
9bf0ef23 425 const connectorMaximumPowerPerPhaseRounded = roundTo(
ad8537a7
JB
426 connectorMaximumPowerPerPhase / unitDivider,
427 2
428 );
9bf0ef23 429 const connectorMinimumPowerPerPhaseRounded = roundTo(
860ef183
JB
430 connectorMinimumPowerPerPhase / unitDivider,
431 2
432 );
78085c42 433 if (
9bf0ef23 434 convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
ad8537a7 435 connectorMaximumPowerPerPhaseRounded ||
9bf0ef23 436 convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) <
860ef183 437 connectorMinimumPowerPerPhaseRounded ||
78085c42
JB
438 debug
439 ) {
440 logger.error(
441 `${chargingStation.logPrefix()} MeterValues measurand ${
442 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
443 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
444 }: phase ${
445 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
54ebb82c 446 }, connector id ${connectorId}, transaction id ${
860ef183
JB
447 connector?.transactionId
448 }, value: ${connectorMinimumPowerPerPhaseRounded}/${
78085c42 449 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
ad8537a7 450 }/${connectorMaximumPowerPerPhaseRounded}`
78085c42
JB
451 );
452 }
453 }
454 }
455 // Current.Import measurand
ed3d2808 456 const currentSampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab 457 chargingStation,
78085c42
JB
458 connectorId,
459 OCPP16MeterValueMeasurand.CURRENT_IMPORT
460 );
abe9e9dd 461 let currentPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {};
78085c42
JB
462 if (chargingStation.getNumberOfPhases() === 3) {
463 currentPerPhaseSampledValueTemplates = {
ed3d2808 464 L1: OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab 465 chargingStation,
78085c42
JB
466 connectorId,
467 OCPP16MeterValueMeasurand.CURRENT_IMPORT,
468 OCPP16MeterValuePhase.L1
469 ),
ed3d2808 470 L2: OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab 471 chargingStation,
78085c42
JB
472 connectorId,
473 OCPP16MeterValueMeasurand.CURRENT_IMPORT,
474 OCPP16MeterValuePhase.L2
475 ),
ed3d2808 476 L3: OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab 477 chargingStation,
78085c42
JB
478 connectorId,
479 OCPP16MeterValueMeasurand.CURRENT_IMPORT,
480 OCPP16MeterValuePhase.L3
481 ),
482 };
483 }
484 if (currentSampledValueTemplate) {
485 OCPP16ServiceUtils.checkMeasurandPowerDivider(
486 chargingStation,
487 currentSampledValueTemplate.measurand
488 );
fc040c43 489 const errMsg = `MeterValues measurand ${
78085c42
JB
490 currentSampledValueTemplate.measurand ??
491 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
492 }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
2484ac1e 493 chargingStation.templateFile
78085c42
JB
494 }, cannot calculate ${
495 currentSampledValueTemplate.measurand ??
496 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
497 } measurand value`;
abe9e9dd 498 const currentMeasurandValues: MeasurandValues = {} as MeasurandValues;
1b6498ba
JB
499 const connectorMaximumAvailablePower =
500 chargingStation.getConnectorMaximumAvailablePower(connectorId);
860ef183 501 const connectorMinimumAmperage = currentSampledValueTemplate.minimumValue ?? 0;
ad8537a7 502 let connectorMaximumAmperage: number;
78085c42
JB
503 switch (chargingStation.getCurrentOutType()) {
504 case CurrentType.AC:
ad8537a7 505 connectorMaximumAmperage = ACElectricUtils.amperagePerPhaseFromPower(
78085c42 506 chargingStation.getNumberOfPhases(),
1b6498ba 507 connectorMaximumAvailablePower,
78085c42
JB
508 chargingStation.getVoltageOut()
509 );
510 if (chargingStation.getNumberOfPhases() === 3) {
511 const defaultFluctuatedAmperagePerPhase =
512 currentSampledValueTemplate.value &&
9bf0ef23 513 getRandomFloatFluctuatedRounded(
7bc31f9c
JB
514 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
515 currentSampledValueTemplate.value,
516 connectorMaximumAmperage,
517 { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
518 ),
78085c42
JB
519 currentSampledValueTemplate.fluctuationPercent ??
520 Constants.DEFAULT_FLUCTUATION_PERCENT
521 );
522 const phase1FluctuatedValue =
523 currentPerPhaseSampledValueTemplates?.L1?.value &&
9bf0ef23 524 getRandomFloatFluctuatedRounded(
7bc31f9c
JB
525 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
526 currentPerPhaseSampledValueTemplates.L1.value,
527 connectorMaximumAmperage,
528 { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
34464008 529 ),
78085c42
JB
530 currentPerPhaseSampledValueTemplates.L1.fluctuationPercent ??
531 Constants.DEFAULT_FLUCTUATION_PERCENT
532 );
533 const phase2FluctuatedValue =
534 currentPerPhaseSampledValueTemplates?.L2?.value &&
9bf0ef23 535 getRandomFloatFluctuatedRounded(
7bc31f9c
JB
536 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
537 currentPerPhaseSampledValueTemplates.L2.value,
538 connectorMaximumAmperage,
539 { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
34464008 540 ),
78085c42
JB
541 currentPerPhaseSampledValueTemplates.L2.fluctuationPercent ??
542 Constants.DEFAULT_FLUCTUATION_PERCENT
543 );
544 const phase3FluctuatedValue =
545 currentPerPhaseSampledValueTemplates?.L3?.value &&
9bf0ef23 546 getRandomFloatFluctuatedRounded(
7bc31f9c
JB
547 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
548 currentPerPhaseSampledValueTemplates.L3.value,
549 connectorMaximumAmperage,
550 { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
34464008 551 ),
78085c42
JB
552 currentPerPhaseSampledValueTemplates.L3.fluctuationPercent ??
553 Constants.DEFAULT_FLUCTUATION_PERCENT
554 );
555 currentMeasurandValues.L1 =
556 phase1FluctuatedValue ??
557 defaultFluctuatedAmperagePerPhase ??
9bf0ef23 558 getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
78085c42
JB
559 currentMeasurandValues.L2 =
560 phase2FluctuatedValue ??
561 defaultFluctuatedAmperagePerPhase ??
9bf0ef23 562 getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
78085c42
JB
563 currentMeasurandValues.L3 =
564 phase3FluctuatedValue ??
565 defaultFluctuatedAmperagePerPhase ??
9bf0ef23 566 getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
78085c42
JB
567 } else {
568 currentMeasurandValues.L1 = currentSampledValueTemplate.value
9bf0ef23 569 ? getRandomFloatFluctuatedRounded(
7bc31f9c
JB
570 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
571 currentSampledValueTemplate.value,
572 connectorMaximumAmperage,
573 { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
574 ),
78085c42
JB
575 currentSampledValueTemplate.fluctuationPercent ??
576 Constants.DEFAULT_FLUCTUATION_PERCENT
577 )
9bf0ef23 578 : getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
78085c42
JB
579 currentMeasurandValues.L2 = 0;
580 currentMeasurandValues.L3 = 0;
581 }
9bf0ef23 582 currentMeasurandValues.allPhases = roundTo(
78085c42
JB
583 (currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) /
584 chargingStation.getNumberOfPhases(),
585 2
586 );
587 break;
588 case CurrentType.DC:
ad8537a7 589 connectorMaximumAmperage = DCElectricUtils.amperage(
1b6498ba 590 connectorMaximumAvailablePower,
78085c42
JB
591 chargingStation.getVoltageOut()
592 );
593 currentMeasurandValues.allPhases = currentSampledValueTemplate.value
9bf0ef23 594 ? getRandomFloatFluctuatedRounded(
7bc31f9c
JB
595 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
596 currentSampledValueTemplate.value,
597 connectorMaximumAmperage,
598 { limitationEnabled: chargingStation.getCustomValueLimitationMeterValues() }
599 ),
78085c42
JB
600 currentSampledValueTemplate.fluctuationPercent ??
601 Constants.DEFAULT_FLUCTUATION_PERCENT
602 )
9bf0ef23 603 : getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
78085c42
JB
604 break;
605 default:
fc040c43 606 logger.error(`${chargingStation.logPrefix()} ${errMsg}`);
78085c42
JB
607 throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
608 }
609 meterValue.sampledValue.push(
610 OCPP16ServiceUtils.buildSampledValue(
611 currentSampledValueTemplate,
612 currentMeasurandValues.allPhases
613 )
614 );
615 const sampledValuesIndex = meterValue.sampledValue.length - 1;
616 if (
9bf0ef23 617 convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) >
ad8537a7 618 connectorMaximumAmperage ||
9bf0ef23 619 convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) <
860ef183 620 connectorMinimumAmperage ||
78085c42
JB
621 debug
622 ) {
623 logger.error(
624 `${chargingStation.logPrefix()} MeterValues measurand ${
625 meterValue.sampledValue[sampledValuesIndex].measurand ??
626 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
54ebb82c 627 }: connector id ${connectorId}, transaction id ${
860ef183
JB
628 connector?.transactionId
629 }, value: ${connectorMinimumAmperage}/${
78085c42 630 meterValue.sampledValue[sampledValuesIndex].value
ad8537a7 631 }/${connectorMaximumAmperage}`
78085c42
JB
632 );
633 }
634 for (
635 let phase = 1;
636 chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases();
637 phase++
638 ) {
639 const phaseValue = `L${phase}`;
640 meterValue.sampledValue.push(
641 OCPP16ServiceUtils.buildSampledValue(
642 (currentPerPhaseSampledValueTemplates[phaseValue] as SampledValueTemplate) ??
643 currentSampledValueTemplate,
644 currentMeasurandValues[phaseValue] as number,
72092cfc 645 undefined,
78085c42
JB
646 phaseValue as OCPP16MeterValuePhase
647 )
648 );
649 const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1;
650 if (
9bf0ef23 651 convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) >
ad8537a7 652 connectorMaximumAmperage ||
9bf0ef23 653 convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) <
860ef183 654 connectorMinimumAmperage ||
78085c42
JB
655 debug
656 ) {
657 logger.error(
658 `${chargingStation.logPrefix()} MeterValues measurand ${
659 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
660 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
661 }: phase ${
662 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
54ebb82c 663 }, connector id ${connectorId}, transaction id ${
860ef183
JB
664 connector?.transactionId
665 }, value: ${connectorMinimumAmperage}/${
78085c42 666 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
ad8537a7 667 }/${connectorMaximumAmperage}`
78085c42
JB
668 );
669 }
670 }
671 }
672 // Energy.Active.Import.Register measurand (default)
ed3d2808 673 const energySampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab
JB
674 chargingStation,
675 connectorId
676 );
78085c42
JB
677 if (energySampledValueTemplate) {
678 OCPP16ServiceUtils.checkMeasurandPowerDivider(
679 chargingStation,
680 energySampledValueTemplate.measurand
681 );
682 const unitDivider =
683 energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
1b6498ba
JB
684 const connectorMaximumAvailablePower =
685 chargingStation.getConnectorMaximumAvailablePower(connectorId);
9bf0ef23 686 const connectorMaximumEnergyRounded = roundTo(
1b6498ba 687 (connectorMaximumAvailablePower * interval) / (3600 * 1000),
78085c42
JB
688 2
689 );
690 const energyValueRounded = energySampledValueTemplate.value
691 ? // Cumulate the fluctuated value around the static one
9bf0ef23 692 getRandomFloatFluctuatedRounded(
7bc31f9c
JB
693 OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
694 energySampledValueTemplate.value,
695 connectorMaximumEnergyRounded,
696 {
697 limitationEnabled: chargingStation.getCustomValueLimitationMeterValues(),
698 unitMultiplier: unitDivider,
699 }
34464008 700 ),
78085c42
JB
701 energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT
702 )
9bf0ef23 703 : getRandomFloatRounded(connectorMaximumEnergyRounded);
78085c42
JB
704 // Persist previous value on connector
705 if (
706 connector &&
9bf0ef23 707 isNullOrUndefined(connector.energyActiveImportRegisterValue) === false &&
78085c42 708 connector.energyActiveImportRegisterValue >= 0 &&
9bf0ef23 709 isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) === false &&
78085c42
JB
710 connector.transactionEnergyActiveImportRegisterValue >= 0
711 ) {
712 connector.energyActiveImportRegisterValue += energyValueRounded;
713 connector.transactionEnergyActiveImportRegisterValue += energyValueRounded;
714 } else {
715 connector.energyActiveImportRegisterValue = 0;
716 connector.transactionEnergyActiveImportRegisterValue = 0;
717 }
718 meterValue.sampledValue.push(
719 OCPP16ServiceUtils.buildSampledValue(
720 energySampledValueTemplate,
9bf0ef23 721 roundTo(
78085c42
JB
722 chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) /
723 unitDivider,
724 2
725 )
726 )
727 );
728 const sampledValuesIndex = meterValue.sampledValue.length - 1;
ad8537a7 729 if (energyValueRounded > connectorMaximumEnergyRounded || debug) {
78085c42
JB
730 logger.error(
731 `${chargingStation.logPrefix()} MeterValues measurand ${
732 meterValue.sampledValue[sampledValuesIndex].measurand ??
733 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
54ebb82c 734 }: connector id ${connectorId}, transaction id ${
72092cfc 735 connector?.transactionId
9bf0ef23 736 }, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${roundTo(
78085c42
JB
737 interval / (3600 * 1000),
738 4
739 )}h`
740 );
741 }
742 }
743 return meterValue;
744 }
745
e7aeea18
JB
746 public static buildTransactionBeginMeterValue(
747 chargingStation: ChargingStation,
748 connectorId: number,
78085c42 749 meterStart: number
e7aeea18 750 ): OCPP16MeterValue {
fd0c36fa 751 const meterValue: OCPP16MeterValue = {
c38f0ced 752 timestamp: new Date(),
fd0c36fa
JB
753 sampledValue: [],
754 };
9ccca265 755 // Energy.Active.Import.Register measurand (default)
ed3d2808 756 const sampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab
JB
757 chargingStation,
758 connectorId
759 );
9ccca265 760 const unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
e7aeea18
JB
761 meterValue.sampledValue.push(
762 OCPP16ServiceUtils.buildSampledValue(
763 sampledValueTemplate,
9bf0ef23 764 roundTo((meterStart ?? 0) / unitDivider, 4),
e7aeea18
JB
765 MeterValueContext.TRANSACTION_BEGIN
766 )
767 );
fd0c36fa
JB
768 return meterValue;
769 }
770
e7aeea18
JB
771 public static buildTransactionEndMeterValue(
772 chargingStation: ChargingStation,
773 connectorId: number,
78085c42 774 meterStop: number
e7aeea18 775 ): OCPP16MeterValue {
fd0c36fa 776 const meterValue: OCPP16MeterValue = {
c38f0ced 777 timestamp: new Date(),
fd0c36fa
JB
778 sampledValue: [],
779 };
9ccca265 780 // Energy.Active.Import.Register measurand (default)
ed3d2808 781 const sampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate(
492cf6ab
JB
782 chargingStation,
783 connectorId
784 );
9ccca265 785 const unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
e7aeea18
JB
786 meterValue.sampledValue.push(
787 OCPP16ServiceUtils.buildSampledValue(
788 sampledValueTemplate,
9bf0ef23 789 roundTo((meterStop ?? 0) / unitDivider, 4),
e7aeea18
JB
790 MeterValueContext.TRANSACTION_END
791 )
792 );
fd0c36fa
JB
793 return meterValue;
794 }
795
e7aeea18
JB
796 public static buildTransactionDataMeterValues(
797 transactionBeginMeterValue: OCPP16MeterValue,
798 transactionEndMeterValue: OCPP16MeterValue
799 ): OCPP16MeterValue[] {
fd0c36fa
JB
800 const meterValues: OCPP16MeterValue[] = [];
801 meterValues.push(transactionBeginMeterValue);
802 meterValues.push(transactionEndMeterValue);
803 return meterValues;
804 }
7bc31f9c 805
ed3d2808
JB
806 public static setChargingProfile(
807 chargingStation: ChargingStation,
808 connectorId: number,
884a6fdf 809 cp: OCPP16ChargingProfile
ed3d2808 810 ): void {
9bf0ef23 811 if (isNullOrUndefined(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) {
ed3d2808 812 logger.error(
54ebb82c 813 `${chargingStation.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an uninitialized charging profiles array attribute, applying deferred initialization`
ed3d2808
JB
814 );
815 chargingStation.getConnectorStatus(connectorId).chargingProfiles = [];
816 }
72092cfc
JB
817 if (
818 Array.isArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles) === false
819 ) {
ed3d2808 820 logger.error(
54ebb82c 821 `${chargingStation.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an improper attribute type for the charging profiles array, applying proper type initialization`
ed3d2808
JB
822 );
823 chargingStation.getConnectorStatus(connectorId).chargingProfiles = [];
824 }
825 let cpReplaced = false;
9bf0ef23 826 if (isNotEmptyArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) {
ed3d2808
JB
827 chargingStation
828 .getConnectorStatus(connectorId)
72092cfc 829 ?.chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
ed3d2808
JB
830 if (
831 chargingProfile.chargingProfileId === cp.chargingProfileId ||
832 (chargingProfile.stackLevel === cp.stackLevel &&
833 chargingProfile.chargingProfilePurpose === cp.chargingProfilePurpose)
834 ) {
835 chargingStation.getConnectorStatus(connectorId).chargingProfiles[index] = cp;
836 cpReplaced = true;
837 }
838 });
839 }
72092cfc 840 !cpReplaced && chargingStation.getConnectorStatus(connectorId)?.chargingProfiles?.push(cp);
ed3d2808
JB
841 }
842
1b271a54
JB
843 public static parseJsonSchemaFile<T extends JsonType>(
844 relativePath: string,
845 moduleName?: string,
846 methodName?: string
847 ): JSONSchemaType<T> {
7164966d 848 return super.parseJsonSchemaFile<T>(
51022aa0 849 relativePath,
1b271a54
JB
850 OCPPVersion.VERSION_16,
851 moduleName,
852 methodName
7164966d 853 );
130783a7
JB
854 }
855
66dd3447
JB
856 public static async isIdTagAuthorized(
857 chargingStation: ChargingStation,
858 connectorId: number,
022a231c 859 idTag: string
66dd3447
JB
860 ): Promise<boolean> {
861 let authorized = false;
862 const connectorStatus = chargingStation.getConnectorStatus(connectorId);
863 if (OCPP16ServiceUtils.isIdTagLocalAuthorized(chargingStation, idTag)) {
864 connectorStatus.localAuthorizeIdTag = idTag;
865 connectorStatus.idTagLocalAuthorized = true;
866 authorized = true;
867 } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
868 connectorStatus.authorizeIdTag = idTag;
869 authorized = await OCPP16ServiceUtils.isIdTagRemoteAuthorized(chargingStation, idTag);
870 } else {
871 logger.warn(
872 `${chargingStation.logPrefix()} The charging station configuration expects authorize at
873 remote start transaction but local authorization or authorize isn't enabled`
874 );
875 }
876 return authorized;
877 }
878
7bc31f9c
JB
879 private static buildSampledValue(
880 sampledValueTemplate: SampledValueTemplate,
881 value: number,
882 context?: MeterValueContext,
883 phase?: OCPP16MeterValuePhase
884 ): OCPP16SampledValue {
885 const sampledValueValue = value ?? sampledValueTemplate?.value ?? null;
886 const sampledValueContext = context ?? sampledValueTemplate?.context ?? null;
887 const sampledValueLocation =
888 sampledValueTemplate?.location ??
889 OCPP16ServiceUtils.getMeasurandDefaultLocation(sampledValueTemplate?.measurand ?? null);
890 const sampledValuePhase = phase ?? sampledValueTemplate?.phase ?? null;
891 return {
9bf0ef23 892 ...(!isNullOrUndefined(sampledValueTemplate.unit) && {
7bc31f9c
JB
893 unit: sampledValueTemplate.unit,
894 }),
9bf0ef23
JB
895 ...(!isNullOrUndefined(sampledValueContext) && { context: sampledValueContext }),
896 ...(!isNullOrUndefined(sampledValueTemplate.measurand) && {
7bc31f9c
JB
897 measurand: sampledValueTemplate.measurand,
898 }),
9bf0ef23
JB
899 ...(!isNullOrUndefined(sampledValueLocation) && { location: sampledValueLocation }),
900 ...(!isNullOrUndefined(sampledValueValue) && { value: sampledValueValue.toString() }),
901 ...(!isNullOrUndefined(sampledValuePhase) && { phase: sampledValuePhase }),
7bc31f9c
JB
902 };
903 }
904
905 private static checkMeasurandPowerDivider(
906 chargingStation: ChargingStation,
907 measurandType: OCPP16MeterValueMeasurand
908 ): void {
9bf0ef23 909 if (isUndefined(chargingStation.powerDivider)) {
fc040c43 910 const errMsg = `MeterValues measurand ${
7bc31f9c
JB
911 measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
912 }: powerDivider is undefined`;
fc040c43 913 logger.error(`${chargingStation.logPrefix()} ${errMsg}`);
7bc31f9c 914 throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
fa7bccf4 915 } else if (chargingStation?.powerDivider <= 0) {
fc040c43 916 const errMsg = `MeterValues measurand ${
7bc31f9c 917 measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
fa7bccf4 918 }: powerDivider have zero or below value ${chargingStation.powerDivider}`;
fc040c43 919 logger.error(`${chargingStation.logPrefix()} ${errMsg}`);
7bc31f9c
JB
920 throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
921 }
922 }
923
924 private static getMeasurandDefaultLocation(
925 measurandType: OCPP16MeterValueMeasurand
926 ): MeterValueLocation | undefined {
927 switch (measurandType) {
928 case OCPP16MeterValueMeasurand.STATE_OF_CHARGE:
929 return MeterValueLocation.EV;
930 }
931 }
932
933 private static getMeasurandDefaultUnit(
934 measurandType: OCPP16MeterValueMeasurand
935 ): MeterValueUnit | undefined {
936 switch (measurandType) {
937 case OCPP16MeterValueMeasurand.CURRENT_EXPORT:
938 case OCPP16MeterValueMeasurand.CURRENT_IMPORT:
939 case OCPP16MeterValueMeasurand.CURRENT_OFFERED:
940 return MeterValueUnit.AMP;
941 case OCPP16MeterValueMeasurand.ENERGY_ACTIVE_EXPORT_REGISTER:
942 case OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER:
943 return MeterValueUnit.WATT_HOUR;
944 case OCPP16MeterValueMeasurand.POWER_ACTIVE_EXPORT:
945 case OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT:
946 case OCPP16MeterValueMeasurand.POWER_OFFERED:
947 return MeterValueUnit.WATT;
948 case OCPP16MeterValueMeasurand.STATE_OF_CHARGE:
949 return MeterValueUnit.PERCENT;
950 case OCPP16MeterValueMeasurand.VOLTAGE:
951 return MeterValueUnit.VOLT;
952 }
953 }
66dd3447
JB
954
955 private static isIdTagLocalAuthorized(chargingStation: ChargingStation, idTag: string): boolean {
956 return (
957 chargingStation.getLocalAuthListEnabled() === true &&
958 chargingStation.hasIdTags() === true &&
9bf0ef23 959 isNotEmptyString(
66dd3447 960 chargingStation.idTagsCache
fba11dc6 961 .getIdTags(getIdTagsFile(chargingStation.stationInfo))
66dd3447
JB
962 ?.find((tag) => tag === idTag)
963 )
964 );
965 }
966
967 private static async isIdTagRemoteAuthorized(
968 chargingStation: ChargingStation,
969 idTag: string
970 ): Promise<boolean> {
971 const authorizeResponse: OCPP16AuthorizeResponse =
972 await chargingStation.ocppRequestService.requestHandler<
973 OCPP16AuthorizeRequest,
974 OCPP16AuthorizeResponse
975 >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
976 idTag: idTag,
977 });
978 return authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED;
979 }
6ed92bc1 980}