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 connectorMaximumPower
= Math.round(
283 chargingStation
.getConnectorMaximumAvailablePower(connectorId
)
285 const connectorMaximumPowerPerPhase
= Math.round(
286 chargingStation
.getConnectorMaximumAvailablePower(connectorId
) /
287 chargingStation
.getNumberOfPhases()
289 switch (chargingStation
.getCurrentOutType()) {
291 if (chargingStation
.getNumberOfPhases() === 3) {
292 const defaultFluctuatedPowerPerPhase
=
293 powerSampledValueTemplate
.value
&&
294 Utils
.getRandomFloatFluctuatedRounded(
295 parseInt(powerSampledValueTemplate
.value
) / chargingStation
.getNumberOfPhases(),
296 powerSampledValueTemplate
.fluctuationPercent
??
297 Constants
.DEFAULT_FLUCTUATION_PERCENT
299 const phase1FluctuatedValue
=
300 powerPerPhaseSampledValueTemplates
?.L1
?.value
&&
301 Utils
.getRandomFloatFluctuatedRounded(
302 parseInt(powerPerPhaseSampledValueTemplates
.L1
.value
),
303 powerPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
??
304 Constants
.DEFAULT_FLUCTUATION_PERCENT
306 const phase2FluctuatedValue
=
307 powerPerPhaseSampledValueTemplates
?.L2
?.value
&&
308 Utils
.getRandomFloatFluctuatedRounded(
309 parseInt(powerPerPhaseSampledValueTemplates
.L2
.value
),
310 powerPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
??
311 Constants
.DEFAULT_FLUCTUATION_PERCENT
313 const phase3FluctuatedValue
=
314 powerPerPhaseSampledValueTemplates
?.L3
?.value
&&
315 Utils
.getRandomFloatFluctuatedRounded(
316 parseInt(powerPerPhaseSampledValueTemplates
.L3
.value
),
317 powerPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
??
318 Constants
.DEFAULT_FLUCTUATION_PERCENT
320 powerMeasurandValues
.L1
=
321 phase1FluctuatedValue
??
322 defaultFluctuatedPowerPerPhase
??
323 Utils
.getRandomFloatRounded(connectorMaximumPowerPerPhase
/ unitDivider
);
324 powerMeasurandValues
.L2
=
325 phase2FluctuatedValue
??
326 defaultFluctuatedPowerPerPhase
??
327 Utils
.getRandomFloatRounded(connectorMaximumPowerPerPhase
/ unitDivider
);
328 powerMeasurandValues
.L3
=
329 phase3FluctuatedValue
??
330 defaultFluctuatedPowerPerPhase
??
331 Utils
.getRandomFloatRounded(connectorMaximumPowerPerPhase
/ unitDivider
);
333 powerMeasurandValues
.L1
= powerSampledValueTemplate
.value
334 ? Utils
.getRandomFloatFluctuatedRounded(
335 parseInt(powerSampledValueTemplate
.value
),
336 powerSampledValueTemplate
.fluctuationPercent
??
337 Constants
.DEFAULT_FLUCTUATION_PERCENT
339 : Utils
.getRandomFloatRounded(connectorMaximumPower
/ unitDivider
);
340 powerMeasurandValues
.L2
= 0;
341 powerMeasurandValues
.L3
= 0;
343 powerMeasurandValues
.allPhases
= Utils
.roundTo(
344 powerMeasurandValues
.L1
+ powerMeasurandValues
.L2
+ powerMeasurandValues
.L3
,
349 powerMeasurandValues
.allPhases
= powerSampledValueTemplate
.value
350 ? Utils
.getRandomFloatFluctuatedRounded(
351 parseInt(powerSampledValueTemplate
.value
),
352 powerSampledValueTemplate
.fluctuationPercent
??
353 Constants
.DEFAULT_FLUCTUATION_PERCENT
355 : Utils
.getRandomFloatRounded(connectorMaximumPower
/ unitDivider
);
358 logger
.error(errMsg
);
359 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
361 meterValue
.sampledValue
.push(
362 OCPP16ServiceUtils
.buildSampledValue(
363 powerSampledValueTemplate
,
364 powerMeasurandValues
.allPhases
367 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
368 const connectorMaximumPowerRounded
= Utils
.roundTo(connectorMaximumPower
/ unitDivider
, 2);
370 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) >
371 connectorMaximumPowerRounded
||
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 }/${connectorMaximumPowerRounded}`
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 connectorMaximumPowerPerPhaseRounded
= Utils
.roundTo(
400 connectorMaximumPowerPerPhase
/ unitDivider
,
404 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) >
405 connectorMaximumPowerPerPhaseRounded
||
409 `${chargingStation.logPrefix()} MeterValues measurand ${
410 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
411 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
413 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
414 }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
415 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
416 }/${connectorMaximumPowerPerPhaseRounded}`
421 // Current.Import measurand
422 const currentSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
424 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
426 let currentPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
427 if (chargingStation
.getNumberOfPhases() === 3) {
428 currentPerPhaseSampledValueTemplates
= {
429 L1
: chargingStation
.getSampledValueTemplate(
431 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
432 OCPP16MeterValuePhase
.L1
434 L2
: chargingStation
.getSampledValueTemplate(
436 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
437 OCPP16MeterValuePhase
.L2
439 L3
: chargingStation
.getSampledValueTemplate(
441 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
442 OCPP16MeterValuePhase
.L3
446 if (currentSampledValueTemplate
) {
447 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
449 currentSampledValueTemplate
.measurand
451 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
452 currentSampledValueTemplate.measurand ??
453 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
454 }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
455 chargingStation.templateFile
456 }, cannot calculate ${
457 currentSampledValueTemplate.measurand ??
458 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
460 const currentMeasurandValues
: MeasurandValues
= {} as MeasurandValues
;
461 let connectorMaximumAmperage
: number;
462 switch (chargingStation
.getCurrentOutType()) {
464 connectorMaximumAmperage
= ACElectricUtils
.amperagePerPhaseFromPower(
465 chargingStation
.getNumberOfPhases(),
466 chargingStation
.getConnectorMaximumAvailablePower(connectorId
),
467 chargingStation
.getVoltageOut()
469 if (chargingStation
.getNumberOfPhases() === 3) {
470 const defaultFluctuatedAmperagePerPhase
=
471 currentSampledValueTemplate
.value
&&
472 Utils
.getRandomFloatFluctuatedRounded(
473 parseInt(currentSampledValueTemplate
.value
),
474 currentSampledValueTemplate
.fluctuationPercent
??
475 Constants
.DEFAULT_FLUCTUATION_PERCENT
477 const phase1FluctuatedValue
=
478 currentPerPhaseSampledValueTemplates
?.L1
?.value
&&
479 Utils
.getRandomFloatFluctuatedRounded(
480 parseInt(currentPerPhaseSampledValueTemplates
.L1
.value
),
481 currentPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
??
482 Constants
.DEFAULT_FLUCTUATION_PERCENT
484 const phase2FluctuatedValue
=
485 currentPerPhaseSampledValueTemplates
?.L2
?.value
&&
486 Utils
.getRandomFloatFluctuatedRounded(
487 parseInt(currentPerPhaseSampledValueTemplates
.L2
.value
),
488 currentPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
??
489 Constants
.DEFAULT_FLUCTUATION_PERCENT
491 const phase3FluctuatedValue
=
492 currentPerPhaseSampledValueTemplates
?.L3
?.value
&&
493 Utils
.getRandomFloatFluctuatedRounded(
494 parseInt(currentPerPhaseSampledValueTemplates
.L3
.value
),
495 currentPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
??
496 Constants
.DEFAULT_FLUCTUATION_PERCENT
498 currentMeasurandValues
.L1
=
499 phase1FluctuatedValue
??
500 defaultFluctuatedAmperagePerPhase
??
501 Utils
.getRandomFloatRounded(connectorMaximumAmperage
);
502 currentMeasurandValues
.L2
=
503 phase2FluctuatedValue
??
504 defaultFluctuatedAmperagePerPhase
??
505 Utils
.getRandomFloatRounded(connectorMaximumAmperage
);
506 currentMeasurandValues
.L3
=
507 phase3FluctuatedValue
??
508 defaultFluctuatedAmperagePerPhase
??
509 Utils
.getRandomFloatRounded(connectorMaximumAmperage
);
511 currentMeasurandValues
.L1
= currentSampledValueTemplate
.value
512 ? Utils
.getRandomFloatFluctuatedRounded(
513 parseInt(currentSampledValueTemplate
.value
),
514 currentSampledValueTemplate
.fluctuationPercent
??
515 Constants
.DEFAULT_FLUCTUATION_PERCENT
517 : Utils
.getRandomFloatRounded(connectorMaximumAmperage
);
518 currentMeasurandValues
.L2
= 0;
519 currentMeasurandValues
.L3
= 0;
521 currentMeasurandValues
.allPhases
= Utils
.roundTo(
522 (currentMeasurandValues
.L1
+ currentMeasurandValues
.L2
+ currentMeasurandValues
.L3
) /
523 chargingStation
.getNumberOfPhases(),
528 connectorMaximumAmperage
= DCElectricUtils
.amperage(
529 chargingStation
.getConnectorMaximumAvailablePower(connectorId
),
530 chargingStation
.getVoltageOut()
532 currentMeasurandValues
.allPhases
= currentSampledValueTemplate
.value
533 ? Utils
.getRandomFloatFluctuatedRounded(
534 parseInt(currentSampledValueTemplate
.value
),
535 currentSampledValueTemplate
.fluctuationPercent
??
536 Constants
.DEFAULT_FLUCTUATION_PERCENT
538 : Utils
.getRandomFloatRounded(connectorMaximumAmperage
);
541 logger
.error(errMsg
);
542 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
544 meterValue
.sampledValue
.push(
545 OCPP16ServiceUtils
.buildSampledValue(
546 currentSampledValueTemplate
,
547 currentMeasurandValues
.allPhases
550 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
552 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) >
553 connectorMaximumAmperage
||
557 `${chargingStation.logPrefix()} MeterValues measurand ${
558 meterValue.sampledValue[sampledValuesIndex].measurand ??
559 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
560 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
561 meterValue.sampledValue[sampledValuesIndex].value
562 }/${connectorMaximumAmperage}`
567 chargingStation
.getNumberOfPhases() === 3 && phase
<= chargingStation
.getNumberOfPhases();
570 const phaseValue
= `L${phase}`;
571 meterValue
.sampledValue
.push(
572 OCPP16ServiceUtils
.buildSampledValue(
573 (currentPerPhaseSampledValueTemplates
[phaseValue
] as SampledValueTemplate
) ??
574 currentSampledValueTemplate
,
575 currentMeasurandValues
[phaseValue
] as number,
577 phaseValue
as OCPP16MeterValuePhase
580 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
582 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) >
583 connectorMaximumAmperage
||
587 `${chargingStation.logPrefix()} MeterValues measurand ${
588 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
589 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
591 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
592 }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
593 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
594 }/${connectorMaximumAmperage}`
599 // Energy.Active.Import.Register measurand (default)
600 const energySampledValueTemplate
= chargingStation
.getSampledValueTemplate(connectorId
);
601 if (energySampledValueTemplate
) {
602 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
604 energySampledValueTemplate
.measurand
607 energySampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
608 const connectorMaximumEnergyRounded
= Utils
.roundTo(
609 (chargingStation
.getConnectorMaximumAvailablePower(connectorId
) * interval
) / (3600 * 1000),
612 const energyValueRounded
= energySampledValueTemplate
.value
613 ? // Cumulate the fluctuated value around the static one
614 Utils
.getRandomFloatFluctuatedRounded(
615 parseInt(energySampledValueTemplate
.value
),
616 energySampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
618 : Utils
.getRandomFloatRounded(connectorMaximumEnergyRounded
);
619 // Persist previous value on connector
622 !Utils
.isNullOrUndefined(connector
.energyActiveImportRegisterValue
) &&
623 connector
.energyActiveImportRegisterValue
>= 0 &&
624 !Utils
.isNullOrUndefined(connector
.transactionEnergyActiveImportRegisterValue
) &&
625 connector
.transactionEnergyActiveImportRegisterValue
>= 0
627 connector
.energyActiveImportRegisterValue
+= energyValueRounded
;
628 connector
.transactionEnergyActiveImportRegisterValue
+= energyValueRounded
;
630 connector
.energyActiveImportRegisterValue
= 0;
631 connector
.transactionEnergyActiveImportRegisterValue
= 0;
633 meterValue
.sampledValue
.push(
634 OCPP16ServiceUtils
.buildSampledValue(
635 energySampledValueTemplate
,
637 chargingStation
.getEnergyActiveImportRegisterByTransactionId(transactionId
) /
643 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
644 if (energyValueRounded
> connectorMaximumEnergyRounded
|| debug
) {
646 `${chargingStation.logPrefix()} MeterValues measurand ${
647 meterValue.sampledValue[sampledValuesIndex].measurand ??
648 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
649 }: connectorId ${connectorId}, transaction ${
650 connector.transactionId
651 }, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${Utils.roundTo(
652 interval / (3600 * 1000),
661 public static buildTransactionBeginMeterValue(
662 chargingStation
: ChargingStation
,
665 ): OCPP16MeterValue
{
666 const meterValue
: OCPP16MeterValue
= {
667 timestamp
: new Date().toISOString(),
670 // Energy.Active.Import.Register measurand (default)
671 const sampledValueTemplate
= chargingStation
.getSampledValueTemplate(connectorId
);
672 const unitDivider
= sampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
673 meterValue
.sampledValue
.push(
674 OCPP16ServiceUtils
.buildSampledValue(
675 sampledValueTemplate
,
676 Utils
.roundTo(meterStart
/ unitDivider
, 4),
677 MeterValueContext
.TRANSACTION_BEGIN
683 public static buildTransactionEndMeterValue(
684 chargingStation
: ChargingStation
,
687 ): OCPP16MeterValue
{
688 const meterValue
: OCPP16MeterValue
= {
689 timestamp
: new Date().toISOString(),
692 // Energy.Active.Import.Register measurand (default)
693 const sampledValueTemplate
= chargingStation
.getSampledValueTemplate(connectorId
);
694 const unitDivider
= sampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
695 meterValue
.sampledValue
.push(
696 OCPP16ServiceUtils
.buildSampledValue(
697 sampledValueTemplate
,
698 Utils
.roundTo(meterStop
/ unitDivider
, 4),
699 MeterValueContext
.TRANSACTION_END
705 public static buildTransactionDataMeterValues(
706 transactionBeginMeterValue
: OCPP16MeterValue
,
707 transactionEndMeterValue
: OCPP16MeterValue
708 ): OCPP16MeterValue
[] {
709 const meterValues
: OCPP16MeterValue
[] = [];
710 meterValues
.push(transactionBeginMeterValue
);
711 meterValues
.push(transactionEndMeterValue
);