1 // Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
3 import { ACElectricUtils
, DCElectricUtils
} from
'../../../utils/ElectricUtils';
4 import { CurrentType
, Voltage
} from
'../../../types/ChargingStationTemplate';
5 import MeasurandPerPhaseSampledValueTemplates
, {
7 } from
'../../../types/MeasurandPerPhaseSampledValueTemplates';
13 OCPP16MeterValueMeasurand
,
14 OCPP16MeterValuePhase
,
16 } from
'../../../types/ocpp/1.6/MeterValues';
18 import type ChargingStation from
'../../ChargingStation';
19 import Constants from
'../../../utils/Constants';
20 import { ErrorType
} from
'../../../types/ocpp/ErrorType';
21 import MeasurandValues from
'../../../types/MeasurandValues';
22 import { OCPP16RequestCommand
} from
'../../../types/ocpp/1.6/Requests';
23 import OCPPError from
'../../../exception/OCPPError';
24 import { RequestCommand
} from
'../../../types/ocpp/Requests';
25 import Utils from
'../../../utils/Utils';
26 import logger from
'../../../utils/Logger';
28 export class OCPP16ServiceUtils
{
29 public static checkMeasurandPowerDivider(
30 chargingStation
: ChargingStation
,
31 measurandType
: OCPP16MeterValueMeasurand
33 if (Utils
.isUndefined(chargingStation
.stationInfo
.powerDivider
)) {
34 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
35 measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
36 }: powerDivider is undefined`;
38 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, RequestCommand
.METER_VALUES
);
39 } else if (chargingStation
.stationInfo
?.powerDivider
<= 0) {
40 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
41 measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
42 }: powerDivider have zero or below value ${chargingStation.stationInfo.powerDivider}`;
44 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, RequestCommand
.METER_VALUES
);
48 public static buildSampledValue(
49 sampledValueTemplate
: SampledValueTemplate
,
51 context
?: MeterValueContext
,
52 phase
?: OCPP16MeterValuePhase
53 ): OCPP16SampledValue
{
54 const sampledValueValue
= value
?? sampledValueTemplate
?.value
?? null;
55 const sampledValueContext
= context
?? sampledValueTemplate
?.context
?? null;
56 const sampledValueLocation
=
57 sampledValueTemplate
?.location
??
58 OCPP16ServiceUtils
.getMeasurandDefaultLocation(sampledValueTemplate
?.measurand
?? null);
59 const sampledValuePhase
= phase
?? sampledValueTemplate
?.phase
?? null;
61 ...(!Utils
.isNullOrUndefined(sampledValueTemplate
.unit
) && {
62 unit
: sampledValueTemplate
.unit
,
64 ...(!Utils
.isNullOrUndefined(sampledValueContext
) && { context
: sampledValueContext
}),
65 ...(!Utils
.isNullOrUndefined(sampledValueTemplate
.measurand
) && {
66 measurand
: sampledValueTemplate
.measurand
,
68 ...(!Utils
.isNullOrUndefined(sampledValueLocation
) && { location
: sampledValueLocation
}),
69 ...(!Utils
.isNullOrUndefined(sampledValueValue
) && { value
: sampledValueValue
.toString() }),
70 ...(!Utils
.isNullOrUndefined(sampledValuePhase
) && { phase
: sampledValuePhase
}),
74 public static getMeasurandDefaultUnit(
75 measurandType
: OCPP16MeterValueMeasurand
76 ): MeterValueUnit
| undefined {
77 switch (measurandType
) {
78 case OCPP16MeterValueMeasurand
.CURRENT_EXPORT
:
79 case OCPP16MeterValueMeasurand
.CURRENT_IMPORT
:
80 case OCPP16MeterValueMeasurand
.CURRENT_OFFERED
:
81 return MeterValueUnit
.AMP
;
82 case OCPP16MeterValueMeasurand
.ENERGY_ACTIVE_EXPORT_REGISTER
:
83 case OCPP16MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
:
84 return MeterValueUnit
.WATT_HOUR
;
85 case OCPP16MeterValueMeasurand
.POWER_ACTIVE_EXPORT
:
86 case OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
:
87 case OCPP16MeterValueMeasurand
.POWER_OFFERED
:
88 return MeterValueUnit
.WATT
;
89 case OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
:
90 return MeterValueUnit
.PERCENT
;
91 case OCPP16MeterValueMeasurand
.VOLTAGE
:
92 return MeterValueUnit
.VOLT
;
96 public static getMeasurandDefaultLocation(
97 measurandType
: OCPP16MeterValueMeasurand
98 ): MeterValueLocation
| undefined {
99 switch (measurandType
) {
100 case OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
:
101 return MeterValueLocation
.EV
;
105 public static buildMeterValue(
106 chargingStation
: ChargingStation
,
108 transactionId
: number,
111 ): OCPP16MeterValue
{
112 const meterValue
: OCPP16MeterValue
= {
113 timestamp
: new Date().toISOString(),
116 const connector
= chargingStation
.getConnectorStatus(connectorId
);
118 const socSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
120 OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
122 if (socSampledValueTemplate
) {
123 const socSampledValueTemplateValue
= socSampledValueTemplate
.value
124 ? Utils
.getRandomFloatFluctuatedRounded(
125 parseInt(socSampledValueTemplate
.value
),
126 socSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
128 : Utils
.getRandomInteger(100);
129 meterValue
.sampledValue
.push(
130 OCPP16ServiceUtils
.buildSampledValue(socSampledValueTemplate
, socSampledValueTemplateValue
)
132 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
133 if (Utils
.convertToInt(meterValue
.sampledValue
[sampledValuesIndex
].value
) > 100 || debug
) {
135 `${chargingStation.logPrefix()} MeterValues measurand ${
136 meterValue.sampledValue[sampledValuesIndex].measurand ??
137 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
138 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
139 meterValue.sampledValue[sampledValuesIndex].value
145 const voltageSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
147 OCPP16MeterValueMeasurand
.VOLTAGE
149 if (voltageSampledValueTemplate
) {
150 const voltageSampledValueTemplateValue
= voltageSampledValueTemplate
.value
151 ? parseInt(voltageSampledValueTemplate
.value
)
152 : chargingStation
.getVoltageOut();
153 const fluctuationPercent
=
154 voltageSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
;
155 const voltageMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
156 voltageSampledValueTemplateValue
,
160 chargingStation
.getNumberOfPhases() !== 3 ||
161 (chargingStation
.getNumberOfPhases() === 3 && chargingStation
.getMainVoltageMeterValues())
163 meterValue
.sampledValue
.push(
164 OCPP16ServiceUtils
.buildSampledValue(voltageSampledValueTemplate
, voltageMeasurandValue
)
169 chargingStation
.getNumberOfPhases() === 3 && phase
<= chargingStation
.getNumberOfPhases();
172 const phaseLineToNeutralValue
= `L${phase}-N`;
173 const voltagePhaseLineToNeutralSampledValueTemplate
=
174 chargingStation
.getSampledValueTemplate(
176 OCPP16MeterValueMeasurand
.VOLTAGE
,
177 phaseLineToNeutralValue
as OCPP16MeterValuePhase
179 let voltagePhaseLineToNeutralMeasurandValue
: number;
180 if (voltagePhaseLineToNeutralSampledValueTemplate
) {
181 const voltagePhaseLineToNeutralSampledValueTemplateValue
=
182 voltagePhaseLineToNeutralSampledValueTemplate
.value
183 ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate
.value
)
184 : chargingStation
.getVoltageOut();
185 const fluctuationPhaseToNeutralPercent
=
186 voltagePhaseLineToNeutralSampledValueTemplate
.fluctuationPercent
??
187 Constants
.DEFAULT_FLUCTUATION_PERCENT
;
188 voltagePhaseLineToNeutralMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
189 voltagePhaseLineToNeutralSampledValueTemplateValue
,
190 fluctuationPhaseToNeutralPercent
193 meterValue
.sampledValue
.push(
194 OCPP16ServiceUtils
.buildSampledValue(
195 voltagePhaseLineToNeutralSampledValueTemplate
?? voltageSampledValueTemplate
,
196 voltagePhaseLineToNeutralMeasurandValue
?? voltageMeasurandValue
,
198 phaseLineToNeutralValue
as OCPP16MeterValuePhase
201 if (chargingStation
.getPhaseLineToLineVoltageMeterValues()) {
202 const phaseLineToLineValue
= `L${phase}-L${
203 (phase + 1) % chargingStation.getNumberOfPhases() !== 0
204 ? (phase + 1) % chargingStation.getNumberOfPhases()
205 : chargingStation.getNumberOfPhases()
207 const voltagePhaseLineToLineSampledValueTemplate
=
208 chargingStation
.getSampledValueTemplate(
210 OCPP16MeterValueMeasurand
.VOLTAGE
,
211 phaseLineToLineValue
as OCPP16MeterValuePhase
213 let voltagePhaseLineToLineMeasurandValue
: number;
214 if (voltagePhaseLineToLineSampledValueTemplate
) {
215 const voltagePhaseLineToLineSampledValueTemplateValue
=
216 voltagePhaseLineToLineSampledValueTemplate
.value
217 ? parseInt(voltagePhaseLineToLineSampledValueTemplate
.value
)
218 : Voltage
.VOLTAGE_400
;
219 const fluctuationPhaseLineToLinePercent
=
220 voltagePhaseLineToLineSampledValueTemplate
.fluctuationPercent
??
221 Constants
.DEFAULT_FLUCTUATION_PERCENT
;
222 voltagePhaseLineToLineMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
223 voltagePhaseLineToLineSampledValueTemplateValue
,
224 fluctuationPhaseLineToLinePercent
227 const defaultVoltagePhaseLineToLineMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
231 meterValue
.sampledValue
.push(
232 OCPP16ServiceUtils
.buildSampledValue(
233 voltagePhaseLineToLineSampledValueTemplate
?? voltageSampledValueTemplate
,
234 voltagePhaseLineToLineMeasurandValue
?? defaultVoltagePhaseLineToLineMeasurandValue
,
236 phaseLineToLineValue
as OCPP16MeterValuePhase
242 // Power.Active.Import measurand
243 const powerSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
245 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
247 let powerPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
248 if (chargingStation
.getNumberOfPhases() === 3) {
249 powerPerPhaseSampledValueTemplates
= {
250 L1
: chargingStation
.getSampledValueTemplate(
252 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
253 OCPP16MeterValuePhase
.L1_N
255 L2
: chargingStation
.getSampledValueTemplate(
257 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
258 OCPP16MeterValuePhase
.L2_N
260 L3
: chargingStation
.getSampledValueTemplate(
262 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
263 OCPP16MeterValuePhase
.L3_N
267 if (powerSampledValueTemplate
) {
268 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
270 powerSampledValueTemplate
.measurand
272 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
273 powerSampledValueTemplate.measurand ??
274 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
275 }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
276 chargingStation.stationTemplateFile
277 }, cannot calculate ${
278 powerSampledValueTemplate.measurand ??
279 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
281 const powerMeasurandValues
= {} as MeasurandValues
;
282 const unitDivider
= powerSampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT
? 1000 : 1;
283 const maxPower
= Math.round(
284 chargingStation
.stationInfo
.maxPower
/ chargingStation
.stationInfo
.powerDivider
286 const maxPowerPerPhase
= Math.round(
287 chargingStation
.stationInfo
.maxPower
/
288 chargingStation
.stationInfo
.powerDivider
/
289 chargingStation
.getNumberOfPhases()
291 switch (chargingStation
.getCurrentOutType()) {
293 if (chargingStation
.getNumberOfPhases() === 3) {
294 const defaultFluctuatedPowerPerPhase
=
295 powerSampledValueTemplate
.value
&&
296 Utils
.getRandomFloatFluctuatedRounded(
297 parseInt(powerSampledValueTemplate
.value
) / chargingStation
.getNumberOfPhases(),
298 powerSampledValueTemplate
.fluctuationPercent
??
299 Constants
.DEFAULT_FLUCTUATION_PERCENT
301 const phase1FluctuatedValue
=
302 powerPerPhaseSampledValueTemplates
?.L1
?.value
&&
303 Utils
.getRandomFloatFluctuatedRounded(
304 parseInt(powerPerPhaseSampledValueTemplates
.L1
.value
),
305 powerPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
??
306 Constants
.DEFAULT_FLUCTUATION_PERCENT
308 const phase2FluctuatedValue
=
309 powerPerPhaseSampledValueTemplates
?.L2
?.value
&&
310 Utils
.getRandomFloatFluctuatedRounded(
311 parseInt(powerPerPhaseSampledValueTemplates
.L2
.value
),
312 powerPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
??
313 Constants
.DEFAULT_FLUCTUATION_PERCENT
315 const phase3FluctuatedValue
=
316 powerPerPhaseSampledValueTemplates
?.L3
?.value
&&
317 Utils
.getRandomFloatFluctuatedRounded(
318 parseInt(powerPerPhaseSampledValueTemplates
.L3
.value
),
319 powerPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
??
320 Constants
.DEFAULT_FLUCTUATION_PERCENT
322 powerMeasurandValues
.L1
=
323 phase1FluctuatedValue
??
324 defaultFluctuatedPowerPerPhase
??
325 Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
326 powerMeasurandValues
.L2
=
327 phase2FluctuatedValue
??
328 defaultFluctuatedPowerPerPhase
??
329 Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
330 powerMeasurandValues
.L3
=
331 phase3FluctuatedValue
??
332 defaultFluctuatedPowerPerPhase
??
333 Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
335 powerMeasurandValues
.L1
= powerSampledValueTemplate
.value
336 ? Utils
.getRandomFloatFluctuatedRounded(
337 parseInt(powerSampledValueTemplate
.value
),
338 powerSampledValueTemplate
.fluctuationPercent
??
339 Constants
.DEFAULT_FLUCTUATION_PERCENT
341 : Utils
.getRandomFloatRounded(maxPower
/ unitDivider
);
342 powerMeasurandValues
.L2
= 0;
343 powerMeasurandValues
.L3
= 0;
345 powerMeasurandValues
.allPhases
= Utils
.roundTo(
346 powerMeasurandValues
.L1
+ powerMeasurandValues
.L2
+ powerMeasurandValues
.L3
,
351 powerMeasurandValues
.allPhases
= powerSampledValueTemplate
.value
352 ? Utils
.getRandomFloatFluctuatedRounded(
353 parseInt(powerSampledValueTemplate
.value
),
354 powerSampledValueTemplate
.fluctuationPercent
??
355 Constants
.DEFAULT_FLUCTUATION_PERCENT
357 : Utils
.getRandomFloatRounded(maxPower
/ unitDivider
);
360 logger
.error(errMsg
);
361 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
363 meterValue
.sampledValue
.push(
364 OCPP16ServiceUtils
.buildSampledValue(
365 powerSampledValueTemplate
,
366 powerMeasurandValues
.allPhases
369 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
370 const maxPowerRounded
= Utils
.roundTo(maxPower
/ unitDivider
, 2);
372 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxPowerRounded
||
376 `${chargingStation.logPrefix()} MeterValues measurand ${
377 meterValue.sampledValue[sampledValuesIndex].measurand ??
378 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
379 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
380 meterValue.sampledValue[sampledValuesIndex].value
381 }/${maxPowerRounded}`
386 chargingStation
.getNumberOfPhases() === 3 && phase
<= chargingStation
.getNumberOfPhases();
389 const phaseValue
= `L${phase}-N`;
390 meterValue
.sampledValue
.push(
391 OCPP16ServiceUtils
.buildSampledValue(
392 (powerPerPhaseSampledValueTemplates
[`L${phase}`] as SampledValueTemplate
) ??
393 powerSampledValueTemplate
,
394 powerMeasurandValues
[`L${phase}`] as number,
396 phaseValue
as OCPP16MeterValuePhase
399 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
400 const maxPowerPerPhaseRounded
= Utils
.roundTo(maxPowerPerPhase
/ unitDivider
, 2);
402 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) >
403 maxPowerPerPhaseRounded
||
407 `${chargingStation.logPrefix()} MeterValues measurand ${
408 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
409 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
411 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
412 }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
413 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
414 }/${maxPowerPerPhaseRounded}`
419 // Current.Import measurand
420 const currentSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
422 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
424 let currentPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
425 if (chargingStation
.getNumberOfPhases() === 3) {
426 currentPerPhaseSampledValueTemplates
= {
427 L1
: chargingStation
.getSampledValueTemplate(
429 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
430 OCPP16MeterValuePhase
.L1
432 L2
: chargingStation
.getSampledValueTemplate(
434 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
435 OCPP16MeterValuePhase
.L2
437 L3
: chargingStation
.getSampledValueTemplate(
439 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
440 OCPP16MeterValuePhase
.L3
444 if (currentSampledValueTemplate
) {
445 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
447 currentSampledValueTemplate
.measurand
449 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
450 currentSampledValueTemplate.measurand ??
451 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
452 }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
453 chargingStation.stationTemplateFile
454 }, cannot calculate ${
455 currentSampledValueTemplate.measurand ??
456 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
458 const currentMeasurandValues
: MeasurandValues
= {} as MeasurandValues
;
459 let maxAmperage
: number;
460 switch (chargingStation
.getCurrentOutType()) {
462 maxAmperage
= ACElectricUtils
.amperagePerPhaseFromPower(
463 chargingStation
.getNumberOfPhases(),
464 chargingStation
.stationInfo
.maxPower
/ chargingStation
.stationInfo
.powerDivider
,
465 chargingStation
.getVoltageOut()
467 if (chargingStation
.getNumberOfPhases() === 3) {
468 const defaultFluctuatedAmperagePerPhase
=
469 currentSampledValueTemplate
.value
&&
470 Utils
.getRandomFloatFluctuatedRounded(
471 parseInt(currentSampledValueTemplate
.value
),
472 currentSampledValueTemplate
.fluctuationPercent
??
473 Constants
.DEFAULT_FLUCTUATION_PERCENT
475 const phase1FluctuatedValue
=
476 currentPerPhaseSampledValueTemplates
?.L1
?.value
&&
477 Utils
.getRandomFloatFluctuatedRounded(
478 parseInt(currentPerPhaseSampledValueTemplates
.L1
.value
),
479 currentPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
??
480 Constants
.DEFAULT_FLUCTUATION_PERCENT
482 const phase2FluctuatedValue
=
483 currentPerPhaseSampledValueTemplates
?.L2
?.value
&&
484 Utils
.getRandomFloatFluctuatedRounded(
485 parseInt(currentPerPhaseSampledValueTemplates
.L2
.value
),
486 currentPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
??
487 Constants
.DEFAULT_FLUCTUATION_PERCENT
489 const phase3FluctuatedValue
=
490 currentPerPhaseSampledValueTemplates
?.L3
?.value
&&
491 Utils
.getRandomFloatFluctuatedRounded(
492 parseInt(currentPerPhaseSampledValueTemplates
.L3
.value
),
493 currentPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
??
494 Constants
.DEFAULT_FLUCTUATION_PERCENT
496 currentMeasurandValues
.L1
=
497 phase1FluctuatedValue
??
498 defaultFluctuatedAmperagePerPhase
??
499 Utils
.getRandomFloatRounded(maxAmperage
);
500 currentMeasurandValues
.L2
=
501 phase2FluctuatedValue
??
502 defaultFluctuatedAmperagePerPhase
??
503 Utils
.getRandomFloatRounded(maxAmperage
);
504 currentMeasurandValues
.L3
=
505 phase3FluctuatedValue
??
506 defaultFluctuatedAmperagePerPhase
??
507 Utils
.getRandomFloatRounded(maxAmperage
);
509 currentMeasurandValues
.L1
= currentSampledValueTemplate
.value
510 ? Utils
.getRandomFloatFluctuatedRounded(
511 parseInt(currentSampledValueTemplate
.value
),
512 currentSampledValueTemplate
.fluctuationPercent
??
513 Constants
.DEFAULT_FLUCTUATION_PERCENT
515 : Utils
.getRandomFloatRounded(maxAmperage
);
516 currentMeasurandValues
.L2
= 0;
517 currentMeasurandValues
.L3
= 0;
519 currentMeasurandValues
.allPhases
= Utils
.roundTo(
520 (currentMeasurandValues
.L1
+ currentMeasurandValues
.L2
+ currentMeasurandValues
.L3
) /
521 chargingStation
.getNumberOfPhases(),
526 maxAmperage
= DCElectricUtils
.amperage(
527 chargingStation
.stationInfo
.maxPower
/ chargingStation
.stationInfo
.powerDivider
,
528 chargingStation
.getVoltageOut()
530 currentMeasurandValues
.allPhases
= currentSampledValueTemplate
.value
531 ? Utils
.getRandomFloatFluctuatedRounded(
532 parseInt(currentSampledValueTemplate
.value
),
533 currentSampledValueTemplate
.fluctuationPercent
??
534 Constants
.DEFAULT_FLUCTUATION_PERCENT
536 : Utils
.getRandomFloatRounded(maxAmperage
);
539 logger
.error(errMsg
);
540 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
542 meterValue
.sampledValue
.push(
543 OCPP16ServiceUtils
.buildSampledValue(
544 currentSampledValueTemplate
,
545 currentMeasurandValues
.allPhases
548 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
550 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxAmperage
||
554 `${chargingStation.logPrefix()} MeterValues measurand ${
555 meterValue.sampledValue[sampledValuesIndex].measurand ??
556 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
557 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
558 meterValue.sampledValue[sampledValuesIndex].value
564 chargingStation
.getNumberOfPhases() === 3 && phase
<= chargingStation
.getNumberOfPhases();
567 const phaseValue
= `L${phase}`;
568 meterValue
.sampledValue
.push(
569 OCPP16ServiceUtils
.buildSampledValue(
570 (currentPerPhaseSampledValueTemplates
[phaseValue
] as SampledValueTemplate
) ??
571 currentSampledValueTemplate
,
572 currentMeasurandValues
[phaseValue
] as number,
574 phaseValue
as OCPP16MeterValuePhase
577 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
579 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) >
584 `${chargingStation.logPrefix()} MeterValues measurand ${
585 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
586 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
588 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
589 }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
590 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
596 // Energy.Active.Import.Register measurand (default)
597 const energySampledValueTemplate
= chargingStation
.getSampledValueTemplate(connectorId
);
598 if (energySampledValueTemplate
) {
599 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
601 energySampledValueTemplate
.measurand
604 energySampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
605 const maxEnergyRounded
= Utils
.roundTo(
606 ((chargingStation
.stationInfo
.maxPower
/ chargingStation
.stationInfo
.powerDivider
) *
611 const energyValueRounded
= energySampledValueTemplate
.value
612 ? // Cumulate the fluctuated value around the static one
613 Utils
.getRandomFloatFluctuatedRounded(
614 parseInt(energySampledValueTemplate
.value
),
615 energySampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
617 : Utils
.getRandomFloatRounded(maxEnergyRounded
);
618 // Persist previous value on connector
621 !Utils
.isNullOrUndefined(connector
.energyActiveImportRegisterValue
) &&
622 connector
.energyActiveImportRegisterValue
>= 0 &&
623 !Utils
.isNullOrUndefined(connector
.transactionEnergyActiveImportRegisterValue
) &&
624 connector
.transactionEnergyActiveImportRegisterValue
>= 0
626 connector
.energyActiveImportRegisterValue
+= energyValueRounded
;
627 connector
.transactionEnergyActiveImportRegisterValue
+= energyValueRounded
;
629 connector
.energyActiveImportRegisterValue
= 0;
630 connector
.transactionEnergyActiveImportRegisterValue
= 0;
632 meterValue
.sampledValue
.push(
633 OCPP16ServiceUtils
.buildSampledValue(
634 energySampledValueTemplate
,
636 chargingStation
.getEnergyActiveImportRegisterByTransactionId(transactionId
) /
642 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
643 if (energyValueRounded
> maxEnergyRounded
|| debug
) {
645 `${chargingStation.logPrefix()} MeterValues measurand ${
646 meterValue.sampledValue[sampledValuesIndex].measurand ??
647 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
648 }: connectorId ${connectorId}, transaction ${
649 connector.transactionId
650 }, value: ${energyValueRounded}/${maxEnergyRounded}, duration: ${Utils.roundTo(
651 interval / (3600 * 1000),
660 public static buildTransactionBeginMeterValue(
661 chargingStation
: ChargingStation
,
664 ): OCPP16MeterValue
{
665 const meterValue
: OCPP16MeterValue
= {
666 timestamp
: new Date().toISOString(),
669 // Energy.Active.Import.Register measurand (default)
670 const sampledValueTemplate
= chargingStation
.getSampledValueTemplate(connectorId
);
671 const unitDivider
= sampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
672 meterValue
.sampledValue
.push(
673 OCPP16ServiceUtils
.buildSampledValue(
674 sampledValueTemplate
,
675 Utils
.roundTo(meterStart
/ unitDivider
, 4),
676 MeterValueContext
.TRANSACTION_BEGIN
682 public static buildTransactionEndMeterValue(
683 chargingStation
: ChargingStation
,
686 ): OCPP16MeterValue
{
687 const meterValue
: OCPP16MeterValue
= {
688 timestamp
: new Date().toISOString(),
691 // Energy.Active.Import.Register measurand (default)
692 const sampledValueTemplate
= chargingStation
.getSampledValueTemplate(connectorId
);
693 const unitDivider
= sampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
694 meterValue
.sampledValue
.push(
695 OCPP16ServiceUtils
.buildSampledValue(
696 sampledValueTemplate
,
697 Utils
.roundTo(meterStop
/ unitDivider
, 4),
698 MeterValueContext
.TRANSACTION_END
704 public static buildTransactionDataMeterValues(
705 transactionBeginMeterValue
: OCPP16MeterValue
,
706 transactionEndMeterValue
: OCPP16MeterValue
707 ): OCPP16MeterValue
[] {
708 const meterValues
: OCPP16MeterValue
[] = [];
709 meterValues
.push(transactionBeginMeterValue
);
710 meterValues
.push(transactionEndMeterValue
);