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 Utils from
'../../../utils/Utils';
25 import logger from
'../../../utils/Logger';
27 export class OCPP16ServiceUtils
{
28 public static checkMeasurandPowerDivider(
29 chargingStation
: ChargingStation
,
30 measurandType
: OCPP16MeterValueMeasurand
32 if (Utils
.isUndefined(chargingStation
.stationInfo
.powerDivider
)) {
33 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
34 measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
35 }: powerDivider is undefined`;
37 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
38 } else if (chargingStation
.stationInfo
?.powerDivider
<= 0) {
39 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
40 measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
41 }: powerDivider have zero or below value ${chargingStation.stationInfo.powerDivider}`;
43 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
47 public static buildSampledValue(
48 sampledValueTemplate
: SampledValueTemplate
,
50 context
?: MeterValueContext
,
51 phase
?: OCPP16MeterValuePhase
52 ): OCPP16SampledValue
{
53 const sampledValueValue
= value
?? sampledValueTemplate
?.value
?? null;
54 const sampledValueContext
= context
?? sampledValueTemplate
?.context
?? null;
55 const sampledValueLocation
=
56 sampledValueTemplate
?.location
??
57 OCPP16ServiceUtils
.getMeasurandDefaultLocation(sampledValueTemplate
?.measurand
?? null);
58 const sampledValuePhase
= phase
?? sampledValueTemplate
?.phase
?? null;
60 ...(!Utils
.isNullOrUndefined(sampledValueTemplate
.unit
) && {
61 unit
: sampledValueTemplate
.unit
,
63 ...(!Utils
.isNullOrUndefined(sampledValueContext
) && { context
: sampledValueContext
}),
64 ...(!Utils
.isNullOrUndefined(sampledValueTemplate
.measurand
) && {
65 measurand
: sampledValueTemplate
.measurand
,
67 ...(!Utils
.isNullOrUndefined(sampledValueLocation
) && { location
: sampledValueLocation
}),
68 ...(!Utils
.isNullOrUndefined(sampledValueValue
) && { value
: sampledValueValue
.toString() }),
69 ...(!Utils
.isNullOrUndefined(sampledValuePhase
) && { phase
: sampledValuePhase
}),
73 public static getMeasurandDefaultUnit(
74 measurandType
: OCPP16MeterValueMeasurand
75 ): MeterValueUnit
| undefined {
76 switch (measurandType
) {
77 case OCPP16MeterValueMeasurand
.CURRENT_EXPORT
:
78 case OCPP16MeterValueMeasurand
.CURRENT_IMPORT
:
79 case OCPP16MeterValueMeasurand
.CURRENT_OFFERED
:
80 return MeterValueUnit
.AMP
;
81 case OCPP16MeterValueMeasurand
.ENERGY_ACTIVE_EXPORT_REGISTER
:
82 case OCPP16MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
:
83 return MeterValueUnit
.WATT_HOUR
;
84 case OCPP16MeterValueMeasurand
.POWER_ACTIVE_EXPORT
:
85 case OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
:
86 case OCPP16MeterValueMeasurand
.POWER_OFFERED
:
87 return MeterValueUnit
.WATT
;
88 case OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
:
89 return MeterValueUnit
.PERCENT
;
90 case OCPP16MeterValueMeasurand
.VOLTAGE
:
91 return MeterValueUnit
.VOLT
;
95 public static getMeasurandDefaultLocation(
96 measurandType
: OCPP16MeterValueMeasurand
97 ): MeterValueLocation
| undefined {
98 switch (measurandType
) {
99 case OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
:
100 return MeterValueLocation
.EV
;
104 public static buildMeterValue(
105 chargingStation
: ChargingStation
,
107 transactionId
: number,
110 ): OCPP16MeterValue
{
111 const meterValue
: OCPP16MeterValue
= {
112 timestamp
: new Date().toISOString(),
115 const connector
= chargingStation
.getConnectorStatus(connectorId
);
117 const socSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
119 OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
121 if (socSampledValueTemplate
) {
122 const socSampledValueTemplateValue
= socSampledValueTemplate
.value
123 ? Utils
.getRandomFloatFluctuatedRounded(
124 parseInt(socSampledValueTemplate
.value
),
125 socSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
127 : Utils
.getRandomInteger(100);
128 meterValue
.sampledValue
.push(
129 OCPP16ServiceUtils
.buildSampledValue(socSampledValueTemplate
, socSampledValueTemplateValue
)
131 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
132 if (Utils
.convertToInt(meterValue
.sampledValue
[sampledValuesIndex
].value
) > 100 || debug
) {
134 `${chargingStation.logPrefix()} MeterValues measurand ${
135 meterValue.sampledValue[sampledValuesIndex].measurand ??
136 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
137 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
138 meterValue.sampledValue[sampledValuesIndex].value
144 const voltageSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
146 OCPP16MeterValueMeasurand
.VOLTAGE
148 if (voltageSampledValueTemplate
) {
149 const voltageSampledValueTemplateValue
= voltageSampledValueTemplate
.value
150 ? parseInt(voltageSampledValueTemplate
.value
)
151 : chargingStation
.getVoltageOut();
152 const fluctuationPercent
=
153 voltageSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
;
154 const voltageMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
155 voltageSampledValueTemplateValue
,
159 chargingStation
.getNumberOfPhases() !== 3 ||
160 (chargingStation
.getNumberOfPhases() === 3 && chargingStation
.getMainVoltageMeterValues())
162 meterValue
.sampledValue
.push(
163 OCPP16ServiceUtils
.buildSampledValue(voltageSampledValueTemplate
, voltageMeasurandValue
)
168 chargingStation
.getNumberOfPhases() === 3 && phase
<= chargingStation
.getNumberOfPhases();
171 const phaseLineToNeutralValue
= `L${phase}-N`;
172 const voltagePhaseLineToNeutralSampledValueTemplate
=
173 chargingStation
.getSampledValueTemplate(
175 OCPP16MeterValueMeasurand
.VOLTAGE
,
176 phaseLineToNeutralValue
as OCPP16MeterValuePhase
178 let voltagePhaseLineToNeutralMeasurandValue
: number;
179 if (voltagePhaseLineToNeutralSampledValueTemplate
) {
180 const voltagePhaseLineToNeutralSampledValueTemplateValue
=
181 voltagePhaseLineToNeutralSampledValueTemplate
.value
182 ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate
.value
)
183 : chargingStation
.getVoltageOut();
184 const fluctuationPhaseToNeutralPercent
=
185 voltagePhaseLineToNeutralSampledValueTemplate
.fluctuationPercent
??
186 Constants
.DEFAULT_FLUCTUATION_PERCENT
;
187 voltagePhaseLineToNeutralMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
188 voltagePhaseLineToNeutralSampledValueTemplateValue
,
189 fluctuationPhaseToNeutralPercent
192 meterValue
.sampledValue
.push(
193 OCPP16ServiceUtils
.buildSampledValue(
194 voltagePhaseLineToNeutralSampledValueTemplate
?? voltageSampledValueTemplate
,
195 voltagePhaseLineToNeutralMeasurandValue
?? voltageMeasurandValue
,
197 phaseLineToNeutralValue
as OCPP16MeterValuePhase
200 if (chargingStation
.getPhaseLineToLineVoltageMeterValues()) {
201 const phaseLineToLineValue
= `L${phase}-L${
202 (phase + 1) % chargingStation.getNumberOfPhases() !== 0
203 ? (phase + 1) % chargingStation.getNumberOfPhases()
204 : chargingStation.getNumberOfPhases()
206 const voltagePhaseLineToLineSampledValueTemplate
=
207 chargingStation
.getSampledValueTemplate(
209 OCPP16MeterValueMeasurand
.VOLTAGE
,
210 phaseLineToLineValue
as OCPP16MeterValuePhase
212 let voltagePhaseLineToLineMeasurandValue
: number;
213 if (voltagePhaseLineToLineSampledValueTemplate
) {
214 const voltagePhaseLineToLineSampledValueTemplateValue
=
215 voltagePhaseLineToLineSampledValueTemplate
.value
216 ? parseInt(voltagePhaseLineToLineSampledValueTemplate
.value
)
217 : Voltage
.VOLTAGE_400
;
218 const fluctuationPhaseLineToLinePercent
=
219 voltagePhaseLineToLineSampledValueTemplate
.fluctuationPercent
??
220 Constants
.DEFAULT_FLUCTUATION_PERCENT
;
221 voltagePhaseLineToLineMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
222 voltagePhaseLineToLineSampledValueTemplateValue
,
223 fluctuationPhaseLineToLinePercent
226 const defaultVoltagePhaseLineToLineMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
230 meterValue
.sampledValue
.push(
231 OCPP16ServiceUtils
.buildSampledValue(
232 voltagePhaseLineToLineSampledValueTemplate
?? voltageSampledValueTemplate
,
233 voltagePhaseLineToLineMeasurandValue
?? defaultVoltagePhaseLineToLineMeasurandValue
,
235 phaseLineToLineValue
as OCPP16MeterValuePhase
241 // Power.Active.Import measurand
242 const powerSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
244 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
246 let powerPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
247 if (chargingStation
.getNumberOfPhases() === 3) {
248 powerPerPhaseSampledValueTemplates
= {
249 L1
: chargingStation
.getSampledValueTemplate(
251 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
252 OCPP16MeterValuePhase
.L1_N
254 L2
: chargingStation
.getSampledValueTemplate(
256 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
257 OCPP16MeterValuePhase
.L2_N
259 L3
: chargingStation
.getSampledValueTemplate(
261 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
262 OCPP16MeterValuePhase
.L3_N
266 if (powerSampledValueTemplate
) {
267 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
269 powerSampledValueTemplate
.measurand
271 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
272 powerSampledValueTemplate.measurand ??
273 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
274 }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
275 chargingStation.templateFile
276 }, cannot calculate ${
277 powerSampledValueTemplate.measurand ??
278 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
280 const powerMeasurandValues
= {} as MeasurandValues
;
281 const unitDivider
= powerSampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT
? 1000 : 1;
282 const maximumPower
= Math.round(
283 chargingStation
.getMaximumConfiguredPower() / chargingStation
.stationInfo
.powerDivider
285 const maximumPowerPerPhase
= Math.round(
286 chargingStation
.getMaximumConfiguredPower() /
287 chargingStation
.stationInfo
.powerDivider
/
288 chargingStation
.getNumberOfPhases()
290 switch (chargingStation
.getCurrentOutType()) {
292 if (chargingStation
.getNumberOfPhases() === 3) {
293 const defaultFluctuatedPowerPerPhase
=
294 powerSampledValueTemplate
.value
&&
295 Utils
.getRandomFloatFluctuatedRounded(
296 parseInt(powerSampledValueTemplate
.value
) / chargingStation
.getNumberOfPhases(),
297 powerSampledValueTemplate
.fluctuationPercent
??
298 Constants
.DEFAULT_FLUCTUATION_PERCENT
300 const phase1FluctuatedValue
=
301 powerPerPhaseSampledValueTemplates
?.L1
?.value
&&
302 Utils
.getRandomFloatFluctuatedRounded(
303 parseInt(powerPerPhaseSampledValueTemplates
.L1
.value
),
304 powerPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
??
305 Constants
.DEFAULT_FLUCTUATION_PERCENT
307 const phase2FluctuatedValue
=
308 powerPerPhaseSampledValueTemplates
?.L2
?.value
&&
309 Utils
.getRandomFloatFluctuatedRounded(
310 parseInt(powerPerPhaseSampledValueTemplates
.L2
.value
),
311 powerPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
??
312 Constants
.DEFAULT_FLUCTUATION_PERCENT
314 const phase3FluctuatedValue
=
315 powerPerPhaseSampledValueTemplates
?.L3
?.value
&&
316 Utils
.getRandomFloatFluctuatedRounded(
317 parseInt(powerPerPhaseSampledValueTemplates
.L3
.value
),
318 powerPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
??
319 Constants
.DEFAULT_FLUCTUATION_PERCENT
321 powerMeasurandValues
.L1
=
322 phase1FluctuatedValue
??
323 defaultFluctuatedPowerPerPhase
??
324 Utils
.getRandomFloatRounded(maximumPowerPerPhase
/ unitDivider
);
325 powerMeasurandValues
.L2
=
326 phase2FluctuatedValue
??
327 defaultFluctuatedPowerPerPhase
??
328 Utils
.getRandomFloatRounded(maximumPowerPerPhase
/ unitDivider
);
329 powerMeasurandValues
.L3
=
330 phase3FluctuatedValue
??
331 defaultFluctuatedPowerPerPhase
??
332 Utils
.getRandomFloatRounded(maximumPowerPerPhase
/ unitDivider
);
334 powerMeasurandValues
.L1
= powerSampledValueTemplate
.value
335 ? Utils
.getRandomFloatFluctuatedRounded(
336 parseInt(powerSampledValueTemplate
.value
),
337 powerSampledValueTemplate
.fluctuationPercent
??
338 Constants
.DEFAULT_FLUCTUATION_PERCENT
340 : Utils
.getRandomFloatRounded(maximumPower
/ unitDivider
);
341 powerMeasurandValues
.L2
= 0;
342 powerMeasurandValues
.L3
= 0;
344 powerMeasurandValues
.allPhases
= Utils
.roundTo(
345 powerMeasurandValues
.L1
+ powerMeasurandValues
.L2
+ powerMeasurandValues
.L3
,
350 powerMeasurandValues
.allPhases
= powerSampledValueTemplate
.value
351 ? Utils
.getRandomFloatFluctuatedRounded(
352 parseInt(powerSampledValueTemplate
.value
),
353 powerSampledValueTemplate
.fluctuationPercent
??
354 Constants
.DEFAULT_FLUCTUATION_PERCENT
356 : Utils
.getRandomFloatRounded(maximumPower
/ unitDivider
);
359 logger
.error(errMsg
);
360 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
362 meterValue
.sampledValue
.push(
363 OCPP16ServiceUtils
.buildSampledValue(
364 powerSampledValueTemplate
,
365 powerMeasurandValues
.allPhases
368 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
369 const maxPowerRounded
= Utils
.roundTo(maximumPower
/ unitDivider
, 2);
371 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxPowerRounded
||
375 `${chargingStation.logPrefix()} MeterValues measurand ${
376 meterValue.sampledValue[sampledValuesIndex].measurand ??
377 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
378 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
379 meterValue.sampledValue[sampledValuesIndex].value
380 }/${maxPowerRounded}`
385 chargingStation
.getNumberOfPhases() === 3 && phase
<= chargingStation
.getNumberOfPhases();
388 const phaseValue
= `L${phase}-N`;
389 meterValue
.sampledValue
.push(
390 OCPP16ServiceUtils
.buildSampledValue(
391 (powerPerPhaseSampledValueTemplates
[`L${phase}`] as SampledValueTemplate
) ??
392 powerSampledValueTemplate
,
393 powerMeasurandValues
[`L${phase}`] as number,
395 phaseValue
as OCPP16MeterValuePhase
398 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
399 const maxPowerPerPhaseRounded
= Utils
.roundTo(maximumPowerPerPhase
/ unitDivider
, 2);
401 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) >
402 maxPowerPerPhaseRounded
||
406 `${chargingStation.logPrefix()} MeterValues measurand ${
407 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
408 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
410 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
411 }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
412 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
413 }/${maxPowerPerPhaseRounded}`
418 // Current.Import measurand
419 const currentSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
421 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
423 let currentPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
424 if (chargingStation
.getNumberOfPhases() === 3) {
425 currentPerPhaseSampledValueTemplates
= {
426 L1
: chargingStation
.getSampledValueTemplate(
428 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
429 OCPP16MeterValuePhase
.L1
431 L2
: chargingStation
.getSampledValueTemplate(
433 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
434 OCPP16MeterValuePhase
.L2
436 L3
: chargingStation
.getSampledValueTemplate(
438 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
439 OCPP16MeterValuePhase
.L3
443 if (currentSampledValueTemplate
) {
444 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
446 currentSampledValueTemplate
.measurand
448 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
449 currentSampledValueTemplate.measurand ??
450 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
451 }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
452 chargingStation.templateFile
453 }, cannot calculate ${
454 currentSampledValueTemplate.measurand ??
455 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
457 const currentMeasurandValues
: MeasurandValues
= {} as MeasurandValues
;
458 let maximumAmperage
: number;
459 switch (chargingStation
.getCurrentOutType()) {
461 maximumAmperage
= ACElectricUtils
.amperagePerPhaseFromPower(
462 chargingStation
.getNumberOfPhases(),
463 chargingStation
.getMaximumConfiguredPower() / chargingStation
.stationInfo
.powerDivider
,
464 chargingStation
.getVoltageOut()
466 if (chargingStation
.getNumberOfPhases() === 3) {
467 const defaultFluctuatedAmperagePerPhase
=
468 currentSampledValueTemplate
.value
&&
469 Utils
.getRandomFloatFluctuatedRounded(
470 parseInt(currentSampledValueTemplate
.value
),
471 currentSampledValueTemplate
.fluctuationPercent
??
472 Constants
.DEFAULT_FLUCTUATION_PERCENT
474 const phase1FluctuatedValue
=
475 currentPerPhaseSampledValueTemplates
?.L1
?.value
&&
476 Utils
.getRandomFloatFluctuatedRounded(
477 parseInt(currentPerPhaseSampledValueTemplates
.L1
.value
),
478 currentPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
??
479 Constants
.DEFAULT_FLUCTUATION_PERCENT
481 const phase2FluctuatedValue
=
482 currentPerPhaseSampledValueTemplates
?.L2
?.value
&&
483 Utils
.getRandomFloatFluctuatedRounded(
484 parseInt(currentPerPhaseSampledValueTemplates
.L2
.value
),
485 currentPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
??
486 Constants
.DEFAULT_FLUCTUATION_PERCENT
488 const phase3FluctuatedValue
=
489 currentPerPhaseSampledValueTemplates
?.L3
?.value
&&
490 Utils
.getRandomFloatFluctuatedRounded(
491 parseInt(currentPerPhaseSampledValueTemplates
.L3
.value
),
492 currentPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
??
493 Constants
.DEFAULT_FLUCTUATION_PERCENT
495 currentMeasurandValues
.L1
=
496 phase1FluctuatedValue
??
497 defaultFluctuatedAmperagePerPhase
??
498 Utils
.getRandomFloatRounded(maximumAmperage
);
499 currentMeasurandValues
.L2
=
500 phase2FluctuatedValue
??
501 defaultFluctuatedAmperagePerPhase
??
502 Utils
.getRandomFloatRounded(maximumAmperage
);
503 currentMeasurandValues
.L3
=
504 phase3FluctuatedValue
??
505 defaultFluctuatedAmperagePerPhase
??
506 Utils
.getRandomFloatRounded(maximumAmperage
);
508 currentMeasurandValues
.L1
= currentSampledValueTemplate
.value
509 ? Utils
.getRandomFloatFluctuatedRounded(
510 parseInt(currentSampledValueTemplate
.value
),
511 currentSampledValueTemplate
.fluctuationPercent
??
512 Constants
.DEFAULT_FLUCTUATION_PERCENT
514 : Utils
.getRandomFloatRounded(maximumAmperage
);
515 currentMeasurandValues
.L2
= 0;
516 currentMeasurandValues
.L3
= 0;
518 currentMeasurandValues
.allPhases
= Utils
.roundTo(
519 (currentMeasurandValues
.L1
+ currentMeasurandValues
.L2
+ currentMeasurandValues
.L3
) /
520 chargingStation
.getNumberOfPhases(),
525 maximumAmperage
= DCElectricUtils
.amperage(
526 chargingStation
.getMaximumConfiguredPower() / chargingStation
.stationInfo
.powerDivider
,
527 chargingStation
.getVoltageOut()
529 currentMeasurandValues
.allPhases
= currentSampledValueTemplate
.value
530 ? Utils
.getRandomFloatFluctuatedRounded(
531 parseInt(currentSampledValueTemplate
.value
),
532 currentSampledValueTemplate
.fluctuationPercent
??
533 Constants
.DEFAULT_FLUCTUATION_PERCENT
535 : Utils
.getRandomFloatRounded(maximumAmperage
);
538 logger
.error(errMsg
);
539 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
541 meterValue
.sampledValue
.push(
542 OCPP16ServiceUtils
.buildSampledValue(
543 currentSampledValueTemplate
,
544 currentMeasurandValues
.allPhases
547 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
549 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maximumAmperage
||
553 `${chargingStation.logPrefix()} MeterValues measurand ${
554 meterValue.sampledValue[sampledValuesIndex].measurand ??
555 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
556 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
557 meterValue.sampledValue[sampledValuesIndex].value
558 }/${maximumAmperage}`
563 chargingStation
.getNumberOfPhases() === 3 && phase
<= chargingStation
.getNumberOfPhases();
566 const phaseValue
= `L${phase}`;
567 meterValue
.sampledValue
.push(
568 OCPP16ServiceUtils
.buildSampledValue(
569 (currentPerPhaseSampledValueTemplates
[phaseValue
] as SampledValueTemplate
) ??
570 currentSampledValueTemplate
,
571 currentMeasurandValues
[phaseValue
] as number,
573 phaseValue
as OCPP16MeterValuePhase
576 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
578 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) >
583 `${chargingStation.logPrefix()} MeterValues measurand ${
584 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
585 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
587 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
588 }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
589 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
590 }/${maximumAmperage}`
595 // Energy.Active.Import.Register measurand (default)
596 const energySampledValueTemplate
= chargingStation
.getSampledValueTemplate(connectorId
);
597 if (energySampledValueTemplate
) {
598 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
600 energySampledValueTemplate
.measurand
603 energySampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
604 const maximumEnergyRounded
= Utils
.roundTo(
605 ((chargingStation
.getMaximumConfiguredPower() / chargingStation
.stationInfo
.powerDivider
) *
610 const energyValueRounded
= energySampledValueTemplate
.value
611 ? // Cumulate the fluctuated value around the static one
612 Utils
.getRandomFloatFluctuatedRounded(
613 parseInt(energySampledValueTemplate
.value
),
614 energySampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
616 : Utils
.getRandomFloatRounded(maximumEnergyRounded
);
617 // Persist previous value on connector
620 !Utils
.isNullOrUndefined(connector
.energyActiveImportRegisterValue
) &&
621 connector
.energyActiveImportRegisterValue
>= 0 &&
622 !Utils
.isNullOrUndefined(connector
.transactionEnergyActiveImportRegisterValue
) &&
623 connector
.transactionEnergyActiveImportRegisterValue
>= 0
625 connector
.energyActiveImportRegisterValue
+= energyValueRounded
;
626 connector
.transactionEnergyActiveImportRegisterValue
+= energyValueRounded
;
628 connector
.energyActiveImportRegisterValue
= 0;
629 connector
.transactionEnergyActiveImportRegisterValue
= 0;
631 meterValue
.sampledValue
.push(
632 OCPP16ServiceUtils
.buildSampledValue(
633 energySampledValueTemplate
,
635 chargingStation
.getEnergyActiveImportRegisterByTransactionId(transactionId
) /
641 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
642 if (energyValueRounded
> maximumEnergyRounded
|| debug
) {
644 `${chargingStation.logPrefix()} MeterValues measurand ${
645 meterValue.sampledValue[sampledValuesIndex].measurand ??
646 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
647 }: connectorId ${connectorId}, transaction ${
648 connector.transactionId
649 }, value: ${energyValueRounded}/${maximumEnergyRounded}, duration: ${Utils.roundTo(
650 interval / (3600 * 1000),
659 public static buildTransactionBeginMeterValue(
660 chargingStation
: ChargingStation
,
663 ): OCPP16MeterValue
{
664 const meterValue
: OCPP16MeterValue
= {
665 timestamp
: new Date().toISOString(),
668 // Energy.Active.Import.Register measurand (default)
669 const sampledValueTemplate
= chargingStation
.getSampledValueTemplate(connectorId
);
670 const unitDivider
= sampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
671 meterValue
.sampledValue
.push(
672 OCPP16ServiceUtils
.buildSampledValue(
673 sampledValueTemplate
,
674 Utils
.roundTo(meterStart
/ unitDivider
, 4),
675 MeterValueContext
.TRANSACTION_BEGIN
681 public static buildTransactionEndMeterValue(
682 chargingStation
: ChargingStation
,
685 ): OCPP16MeterValue
{
686 const meterValue
: OCPP16MeterValue
= {
687 timestamp
: new Date().toISOString(),
690 // Energy.Active.Import.Register measurand (default)
691 const sampledValueTemplate
= chargingStation
.getSampledValueTemplate(connectorId
);
692 const unitDivider
= sampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
693 meterValue
.sampledValue
.push(
694 OCPP16ServiceUtils
.buildSampledValue(
695 sampledValueTemplate
,
696 Utils
.roundTo(meterStop
/ unitDivider
, 4),
697 MeterValueContext
.TRANSACTION_END
703 public static buildTransactionDataMeterValues(
704 transactionBeginMeterValue
: OCPP16MeterValue
,
705 transactionEndMeterValue
: OCPP16MeterValue
706 ): OCPP16MeterValue
[] {
707 const meterValues
: OCPP16MeterValue
[] = [];
708 meterValues
.push(transactionBeginMeterValue
);
709 meterValues
.push(transactionEndMeterValue
);