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