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 OCPP16IncomingRequestCommand
,
20 } from
'../../../types/ocpp/1.6/Requests';
22 OCPP16StandardParametersKey
,
23 OCPP16SupportedFeatureProfiles
,
24 } from
'../../../types/ocpp/1.6/Configuration';
26 import type ChargingStation from
'../../ChargingStation';
27 import Constants from
'../../../utils/Constants';
28 import { ErrorType
} from
'../../../types/ocpp/ErrorType';
29 import MeasurandValues from
'../../../types/MeasurandValues';
30 import OCPPError from
'../../../exception/OCPPError';
31 import Utils from
'../../../utils/Utils';
32 import logger from
'../../../utils/Logger';
34 export class OCPP16ServiceUtils
{
35 public static checkMeasurandPowerDivider(
36 chargingStation
: ChargingStation
,
37 measurandType
: OCPP16MeterValueMeasurand
39 if (Utils
.isUndefined(chargingStation
.stationInfo
.powerDivider
)) {
40 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
41 measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
42 }: powerDivider is undefined`;
44 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
45 } else if (chargingStation
.stationInfo
?.powerDivider
<= 0) {
46 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
47 measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
48 }: powerDivider have zero or below value ${chargingStation.stationInfo.powerDivider}`;
50 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
54 public static checkFeatureProfile(
55 chargingStation
: ChargingStation
,
56 featureProfile
: OCPP16SupportedFeatureProfiles
,
57 command
: OCPP16RequestCommand
| OCPP16IncomingRequestCommand
59 if (!chargingStation
.hasFeatureProfile(featureProfile
)) {
61 `${chargingStation.logPrefix()} Trying to '${command}' without '${featureProfile}' feature enabled in ${
62 OCPP16StandardParametersKey.SupportedFeatureProfiles
70 public static buildSampledValue(
71 sampledValueTemplate
: SampledValueTemplate
,
73 context
?: MeterValueContext
,
74 phase
?: OCPP16MeterValuePhase
75 ): OCPP16SampledValue
{
76 const sampledValueValue
= value
?? sampledValueTemplate
?.value
?? null;
77 const sampledValueContext
= context
?? sampledValueTemplate
?.context
?? null;
78 const sampledValueLocation
=
79 sampledValueTemplate
?.location
??
80 OCPP16ServiceUtils
.getMeasurandDefaultLocation(sampledValueTemplate
?.measurand
?? null);
81 const sampledValuePhase
= phase
?? sampledValueTemplate
?.phase
?? null;
83 ...(!Utils
.isNullOrUndefined(sampledValueTemplate
.unit
) && {
84 unit
: sampledValueTemplate
.unit
,
86 ...(!Utils
.isNullOrUndefined(sampledValueContext
) && { context
: sampledValueContext
}),
87 ...(!Utils
.isNullOrUndefined(sampledValueTemplate
.measurand
) && {
88 measurand
: sampledValueTemplate
.measurand
,
90 ...(!Utils
.isNullOrUndefined(sampledValueLocation
) && { location
: sampledValueLocation
}),
91 ...(!Utils
.isNullOrUndefined(sampledValueValue
) && { value
: sampledValueValue
.toString() }),
92 ...(!Utils
.isNullOrUndefined(sampledValuePhase
) && { phase
: sampledValuePhase
}),
96 public static getMeasurandDefaultUnit(
97 measurandType
: OCPP16MeterValueMeasurand
98 ): MeterValueUnit
| undefined {
99 switch (measurandType
) {
100 case OCPP16MeterValueMeasurand
.CURRENT_EXPORT
:
101 case OCPP16MeterValueMeasurand
.CURRENT_IMPORT
:
102 case OCPP16MeterValueMeasurand
.CURRENT_OFFERED
:
103 return MeterValueUnit
.AMP
;
104 case OCPP16MeterValueMeasurand
.ENERGY_ACTIVE_EXPORT_REGISTER
:
105 case OCPP16MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
:
106 return MeterValueUnit
.WATT_HOUR
;
107 case OCPP16MeterValueMeasurand
.POWER_ACTIVE_EXPORT
:
108 case OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
:
109 case OCPP16MeterValueMeasurand
.POWER_OFFERED
:
110 return MeterValueUnit
.WATT
;
111 case OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
:
112 return MeterValueUnit
.PERCENT
;
113 case OCPP16MeterValueMeasurand
.VOLTAGE
:
114 return MeterValueUnit
.VOLT
;
118 public static getMeasurandDefaultLocation(
119 measurandType
: OCPP16MeterValueMeasurand
120 ): MeterValueLocation
| undefined {
121 switch (measurandType
) {
122 case OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
:
123 return MeterValueLocation
.EV
;
127 public static buildMeterValue(
128 chargingStation
: ChargingStation
,
130 transactionId
: number,
133 ): OCPP16MeterValue
{
134 const meterValue
: OCPP16MeterValue
= {
135 timestamp
: new Date().toISOString(),
138 const connector
= chargingStation
.getConnectorStatus(connectorId
);
140 const socSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
142 OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
144 if (socSampledValueTemplate
) {
145 const socSampledValueTemplateValue
= socSampledValueTemplate
.value
146 ? Utils
.getRandomFloatFluctuatedRounded(
147 parseInt(socSampledValueTemplate
.value
),
148 socSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
150 : Utils
.getRandomInteger(100);
151 meterValue
.sampledValue
.push(
152 OCPP16ServiceUtils
.buildSampledValue(socSampledValueTemplate
, socSampledValueTemplateValue
)
154 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
155 if (Utils
.convertToInt(meterValue
.sampledValue
[sampledValuesIndex
].value
) > 100 || debug
) {
157 `${chargingStation.logPrefix()} MeterValues measurand ${
158 meterValue.sampledValue[sampledValuesIndex].measurand ??
159 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
160 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
161 meterValue.sampledValue[sampledValuesIndex].value
167 const voltageSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
169 OCPP16MeterValueMeasurand
.VOLTAGE
171 if (voltageSampledValueTemplate
) {
172 const voltageSampledValueTemplateValue
= voltageSampledValueTemplate
.value
173 ? parseInt(voltageSampledValueTemplate
.value
)
174 : chargingStation
.getVoltageOut();
175 const fluctuationPercent
=
176 voltageSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
;
177 const voltageMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
178 voltageSampledValueTemplateValue
,
182 chargingStation
.getNumberOfPhases() !== 3 ||
183 (chargingStation
.getNumberOfPhases() === 3 && chargingStation
.getMainVoltageMeterValues())
185 meterValue
.sampledValue
.push(
186 OCPP16ServiceUtils
.buildSampledValue(voltageSampledValueTemplate
, voltageMeasurandValue
)
191 chargingStation
.getNumberOfPhases() === 3 && phase
<= chargingStation
.getNumberOfPhases();
194 const phaseLineToNeutralValue
= `L${phase}-N`;
195 const voltagePhaseLineToNeutralSampledValueTemplate
=
196 chargingStation
.getSampledValueTemplate(
198 OCPP16MeterValueMeasurand
.VOLTAGE
,
199 phaseLineToNeutralValue
as OCPP16MeterValuePhase
201 let voltagePhaseLineToNeutralMeasurandValue
: number;
202 if (voltagePhaseLineToNeutralSampledValueTemplate
) {
203 const voltagePhaseLineToNeutralSampledValueTemplateValue
=
204 voltagePhaseLineToNeutralSampledValueTemplate
.value
205 ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate
.value
)
206 : chargingStation
.getVoltageOut();
207 const fluctuationPhaseToNeutralPercent
=
208 voltagePhaseLineToNeutralSampledValueTemplate
.fluctuationPercent
??
209 Constants
.DEFAULT_FLUCTUATION_PERCENT
;
210 voltagePhaseLineToNeutralMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
211 voltagePhaseLineToNeutralSampledValueTemplateValue
,
212 fluctuationPhaseToNeutralPercent
215 meterValue
.sampledValue
.push(
216 OCPP16ServiceUtils
.buildSampledValue(
217 voltagePhaseLineToNeutralSampledValueTemplate
?? voltageSampledValueTemplate
,
218 voltagePhaseLineToNeutralMeasurandValue
?? voltageMeasurandValue
,
220 phaseLineToNeutralValue
as OCPP16MeterValuePhase
223 if (chargingStation
.getPhaseLineToLineVoltageMeterValues()) {
224 const phaseLineToLineValue
= `L${phase}-L${
225 (phase + 1) % chargingStation.getNumberOfPhases() !== 0
226 ? (phase + 1) % chargingStation.getNumberOfPhases()
227 : chargingStation.getNumberOfPhases()
229 const voltagePhaseLineToLineSampledValueTemplate
=
230 chargingStation
.getSampledValueTemplate(
232 OCPP16MeterValueMeasurand
.VOLTAGE
,
233 phaseLineToLineValue
as OCPP16MeterValuePhase
235 let voltagePhaseLineToLineMeasurandValue
: number;
236 if (voltagePhaseLineToLineSampledValueTemplate
) {
237 const voltagePhaseLineToLineSampledValueTemplateValue
=
238 voltagePhaseLineToLineSampledValueTemplate
.value
239 ? parseInt(voltagePhaseLineToLineSampledValueTemplate
.value
)
240 : Voltage
.VOLTAGE_400
;
241 const fluctuationPhaseLineToLinePercent
=
242 voltagePhaseLineToLineSampledValueTemplate
.fluctuationPercent
??
243 Constants
.DEFAULT_FLUCTUATION_PERCENT
;
244 voltagePhaseLineToLineMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
245 voltagePhaseLineToLineSampledValueTemplateValue
,
246 fluctuationPhaseLineToLinePercent
249 const defaultVoltagePhaseLineToLineMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
253 meterValue
.sampledValue
.push(
254 OCPP16ServiceUtils
.buildSampledValue(
255 voltagePhaseLineToLineSampledValueTemplate
?? voltageSampledValueTemplate
,
256 voltagePhaseLineToLineMeasurandValue
?? defaultVoltagePhaseLineToLineMeasurandValue
,
258 phaseLineToLineValue
as OCPP16MeterValuePhase
264 // Power.Active.Import measurand
265 const powerSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
267 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
269 let powerPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
270 if (chargingStation
.getNumberOfPhases() === 3) {
271 powerPerPhaseSampledValueTemplates
= {
272 L1
: chargingStation
.getSampledValueTemplate(
274 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
275 OCPP16MeterValuePhase
.L1_N
277 L2
: chargingStation
.getSampledValueTemplate(
279 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
280 OCPP16MeterValuePhase
.L2_N
282 L3
: chargingStation
.getSampledValueTemplate(
284 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
285 OCPP16MeterValuePhase
.L3_N
289 if (powerSampledValueTemplate
) {
290 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
292 powerSampledValueTemplate
.measurand
294 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
295 powerSampledValueTemplate.measurand ??
296 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
297 }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
298 chargingStation.templateFile
299 }, cannot calculate ${
300 powerSampledValueTemplate.measurand ??
301 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
303 const powerMeasurandValues
= {} as MeasurandValues
;
304 const unitDivider
= powerSampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT
? 1000 : 1;
305 const connectorMaximumPower
= Math.round(
306 chargingStation
.getConnectorMaximumAvailablePower(connectorId
)
308 const connectorMaximumPowerPerPhase
= Math.round(
309 chargingStation
.getConnectorMaximumAvailablePower(connectorId
) /
310 chargingStation
.getNumberOfPhases()
312 switch (chargingStation
.getCurrentOutType()) {
314 if (chargingStation
.getNumberOfPhases() === 3) {
315 const defaultFluctuatedPowerPerPhase
=
316 powerSampledValueTemplate
.value
&&
317 Utils
.getRandomFloatFluctuatedRounded(
318 parseInt(powerSampledValueTemplate
.value
) / chargingStation
.getNumberOfPhases(),
319 powerSampledValueTemplate
.fluctuationPercent
??
320 Constants
.DEFAULT_FLUCTUATION_PERCENT
322 const phase1FluctuatedValue
=
323 powerPerPhaseSampledValueTemplates
?.L1
?.value
&&
324 Utils
.getRandomFloatFluctuatedRounded(
325 parseInt(powerPerPhaseSampledValueTemplates
.L1
.value
),
326 powerPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
??
327 Constants
.DEFAULT_FLUCTUATION_PERCENT
329 const phase2FluctuatedValue
=
330 powerPerPhaseSampledValueTemplates
?.L2
?.value
&&
331 Utils
.getRandomFloatFluctuatedRounded(
332 parseInt(powerPerPhaseSampledValueTemplates
.L2
.value
),
333 powerPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
??
334 Constants
.DEFAULT_FLUCTUATION_PERCENT
336 const phase3FluctuatedValue
=
337 powerPerPhaseSampledValueTemplates
?.L3
?.value
&&
338 Utils
.getRandomFloatFluctuatedRounded(
339 parseInt(powerPerPhaseSampledValueTemplates
.L3
.value
),
340 powerPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
??
341 Constants
.DEFAULT_FLUCTUATION_PERCENT
343 powerMeasurandValues
.L1
=
344 phase1FluctuatedValue
??
345 defaultFluctuatedPowerPerPhase
??
346 Utils
.getRandomFloatRounded(connectorMaximumPowerPerPhase
/ unitDivider
);
347 powerMeasurandValues
.L2
=
348 phase2FluctuatedValue
??
349 defaultFluctuatedPowerPerPhase
??
350 Utils
.getRandomFloatRounded(connectorMaximumPowerPerPhase
/ unitDivider
);
351 powerMeasurandValues
.L3
=
352 phase3FluctuatedValue
??
353 defaultFluctuatedPowerPerPhase
??
354 Utils
.getRandomFloatRounded(connectorMaximumPowerPerPhase
/ unitDivider
);
356 powerMeasurandValues
.L1
= powerSampledValueTemplate
.value
357 ? Utils
.getRandomFloatFluctuatedRounded(
358 parseInt(powerSampledValueTemplate
.value
),
359 powerSampledValueTemplate
.fluctuationPercent
??
360 Constants
.DEFAULT_FLUCTUATION_PERCENT
362 : Utils
.getRandomFloatRounded(connectorMaximumPower
/ unitDivider
);
363 powerMeasurandValues
.L2
= 0;
364 powerMeasurandValues
.L3
= 0;
366 powerMeasurandValues
.allPhases
= Utils
.roundTo(
367 powerMeasurandValues
.L1
+ powerMeasurandValues
.L2
+ powerMeasurandValues
.L3
,
372 powerMeasurandValues
.allPhases
= powerSampledValueTemplate
.value
373 ? Utils
.getRandomFloatFluctuatedRounded(
374 parseInt(powerSampledValueTemplate
.value
),
375 powerSampledValueTemplate
.fluctuationPercent
??
376 Constants
.DEFAULT_FLUCTUATION_PERCENT
378 : Utils
.getRandomFloatRounded(connectorMaximumPower
/ unitDivider
);
381 logger
.error(errMsg
);
382 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
384 meterValue
.sampledValue
.push(
385 OCPP16ServiceUtils
.buildSampledValue(
386 powerSampledValueTemplate
,
387 powerMeasurandValues
.allPhases
390 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
391 const connectorMaximumPowerRounded
= Utils
.roundTo(connectorMaximumPower
/ unitDivider
, 2);
393 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) >
394 connectorMaximumPowerRounded
||
398 `${chargingStation.logPrefix()} MeterValues measurand ${
399 meterValue.sampledValue[sampledValuesIndex].measurand ??
400 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
401 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
402 meterValue.sampledValue[sampledValuesIndex].value
403 }/${connectorMaximumPowerRounded}`
408 chargingStation
.getNumberOfPhases() === 3 && phase
<= chargingStation
.getNumberOfPhases();
411 const phaseValue
= `L${phase}-N`;
412 meterValue
.sampledValue
.push(
413 OCPP16ServiceUtils
.buildSampledValue(
414 (powerPerPhaseSampledValueTemplates
[`L${phase}`] as SampledValueTemplate
) ??
415 powerSampledValueTemplate
,
416 powerMeasurandValues
[`L${phase}`] as number,
418 phaseValue
as OCPP16MeterValuePhase
421 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
422 const connectorMaximumPowerPerPhaseRounded
= Utils
.roundTo(
423 connectorMaximumPowerPerPhase
/ unitDivider
,
427 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) >
428 connectorMaximumPowerPerPhaseRounded
||
432 `${chargingStation.logPrefix()} MeterValues measurand ${
433 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
434 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
436 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
437 }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
438 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
439 }/${connectorMaximumPowerPerPhaseRounded}`
444 // Current.Import measurand
445 const currentSampledValueTemplate
= chargingStation
.getSampledValueTemplate(
447 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
449 let currentPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
450 if (chargingStation
.getNumberOfPhases() === 3) {
451 currentPerPhaseSampledValueTemplates
= {
452 L1
: chargingStation
.getSampledValueTemplate(
454 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
455 OCPP16MeterValuePhase
.L1
457 L2
: chargingStation
.getSampledValueTemplate(
459 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
460 OCPP16MeterValuePhase
.L2
462 L3
: chargingStation
.getSampledValueTemplate(
464 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
465 OCPP16MeterValuePhase
.L3
469 if (currentSampledValueTemplate
) {
470 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
472 currentSampledValueTemplate
.measurand
474 const errMsg
= `${chargingStation.logPrefix()} MeterValues measurand ${
475 currentSampledValueTemplate.measurand ??
476 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
477 }: Unknown ${chargingStation.getCurrentOutType()} currentOutType in template file ${
478 chargingStation.templateFile
479 }, cannot calculate ${
480 currentSampledValueTemplate.measurand ??
481 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
483 const currentMeasurandValues
: MeasurandValues
= {} as MeasurandValues
;
484 let connectorMaximumAmperage
: number;
485 switch (chargingStation
.getCurrentOutType()) {
487 connectorMaximumAmperage
= ACElectricUtils
.amperagePerPhaseFromPower(
488 chargingStation
.getNumberOfPhases(),
489 chargingStation
.getConnectorMaximumAvailablePower(connectorId
),
490 chargingStation
.getVoltageOut()
492 if (chargingStation
.getNumberOfPhases() === 3) {
493 const defaultFluctuatedAmperagePerPhase
=
494 currentSampledValueTemplate
.value
&&
495 Utils
.getRandomFloatFluctuatedRounded(
496 parseInt(currentSampledValueTemplate
.value
),
497 currentSampledValueTemplate
.fluctuationPercent
??
498 Constants
.DEFAULT_FLUCTUATION_PERCENT
500 const phase1FluctuatedValue
=
501 currentPerPhaseSampledValueTemplates
?.L1
?.value
&&
502 Utils
.getRandomFloatFluctuatedRounded(
503 parseInt(currentPerPhaseSampledValueTemplates
.L1
.value
),
504 currentPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
??
505 Constants
.DEFAULT_FLUCTUATION_PERCENT
507 const phase2FluctuatedValue
=
508 currentPerPhaseSampledValueTemplates
?.L2
?.value
&&
509 Utils
.getRandomFloatFluctuatedRounded(
510 parseInt(currentPerPhaseSampledValueTemplates
.L2
.value
),
511 currentPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
??
512 Constants
.DEFAULT_FLUCTUATION_PERCENT
514 const phase3FluctuatedValue
=
515 currentPerPhaseSampledValueTemplates
?.L3
?.value
&&
516 Utils
.getRandomFloatFluctuatedRounded(
517 parseInt(currentPerPhaseSampledValueTemplates
.L3
.value
),
518 currentPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
??
519 Constants
.DEFAULT_FLUCTUATION_PERCENT
521 currentMeasurandValues
.L1
=
522 phase1FluctuatedValue
??
523 defaultFluctuatedAmperagePerPhase
??
524 Utils
.getRandomFloatRounded(connectorMaximumAmperage
);
525 currentMeasurandValues
.L2
=
526 phase2FluctuatedValue
??
527 defaultFluctuatedAmperagePerPhase
??
528 Utils
.getRandomFloatRounded(connectorMaximumAmperage
);
529 currentMeasurandValues
.L3
=
530 phase3FluctuatedValue
??
531 defaultFluctuatedAmperagePerPhase
??
532 Utils
.getRandomFloatRounded(connectorMaximumAmperage
);
534 currentMeasurandValues
.L1
= currentSampledValueTemplate
.value
535 ? Utils
.getRandomFloatFluctuatedRounded(
536 parseInt(currentSampledValueTemplate
.value
),
537 currentSampledValueTemplate
.fluctuationPercent
??
538 Constants
.DEFAULT_FLUCTUATION_PERCENT
540 : Utils
.getRandomFloatRounded(connectorMaximumAmperage
);
541 currentMeasurandValues
.L2
= 0;
542 currentMeasurandValues
.L3
= 0;
544 currentMeasurandValues
.allPhases
= Utils
.roundTo(
545 (currentMeasurandValues
.L1
+ currentMeasurandValues
.L2
+ currentMeasurandValues
.L3
) /
546 chargingStation
.getNumberOfPhases(),
551 connectorMaximumAmperage
= DCElectricUtils
.amperage(
552 chargingStation
.getConnectorMaximumAvailablePower(connectorId
),
553 chargingStation
.getVoltageOut()
555 currentMeasurandValues
.allPhases
= currentSampledValueTemplate
.value
556 ? Utils
.getRandomFloatFluctuatedRounded(
557 parseInt(currentSampledValueTemplate
.value
),
558 currentSampledValueTemplate
.fluctuationPercent
??
559 Constants
.DEFAULT_FLUCTUATION_PERCENT
561 : Utils
.getRandomFloatRounded(connectorMaximumAmperage
);
564 logger
.error(errMsg
);
565 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
567 meterValue
.sampledValue
.push(
568 OCPP16ServiceUtils
.buildSampledValue(
569 currentSampledValueTemplate
,
570 currentMeasurandValues
.allPhases
573 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
575 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) >
576 connectorMaximumAmperage
||
580 `${chargingStation.logPrefix()} MeterValues measurand ${
581 meterValue.sampledValue[sampledValuesIndex].measurand ??
582 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
583 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
584 meterValue.sampledValue[sampledValuesIndex].value
585 }/${connectorMaximumAmperage}`
590 chargingStation
.getNumberOfPhases() === 3 && phase
<= chargingStation
.getNumberOfPhases();
593 const phaseValue
= `L${phase}`;
594 meterValue
.sampledValue
.push(
595 OCPP16ServiceUtils
.buildSampledValue(
596 (currentPerPhaseSampledValueTemplates
[phaseValue
] as SampledValueTemplate
) ??
597 currentSampledValueTemplate
,
598 currentMeasurandValues
[phaseValue
] as number,
600 phaseValue
as OCPP16MeterValuePhase
603 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
605 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) >
606 connectorMaximumAmperage
||
610 `${chargingStation.logPrefix()} MeterValues measurand ${
611 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
612 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
614 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
615 }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
616 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
617 }/${connectorMaximumAmperage}`
622 // Energy.Active.Import.Register measurand (default)
623 const energySampledValueTemplate
= chargingStation
.getSampledValueTemplate(connectorId
);
624 if (energySampledValueTemplate
) {
625 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
627 energySampledValueTemplate
.measurand
630 energySampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
631 const connectorMaximumEnergyRounded
= Utils
.roundTo(
632 (chargingStation
.getConnectorMaximumAvailablePower(connectorId
) * interval
) / (3600 * 1000),
635 const energyValueRounded
= energySampledValueTemplate
.value
636 ? // Cumulate the fluctuated value around the static one
637 Utils
.getRandomFloatFluctuatedRounded(
638 parseInt(energySampledValueTemplate
.value
),
639 energySampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
641 : Utils
.getRandomFloatRounded(connectorMaximumEnergyRounded
);
642 // Persist previous value on connector
645 !Utils
.isNullOrUndefined(connector
.energyActiveImportRegisterValue
) &&
646 connector
.energyActiveImportRegisterValue
>= 0 &&
647 !Utils
.isNullOrUndefined(connector
.transactionEnergyActiveImportRegisterValue
) &&
648 connector
.transactionEnergyActiveImportRegisterValue
>= 0
650 connector
.energyActiveImportRegisterValue
+= energyValueRounded
;
651 connector
.transactionEnergyActiveImportRegisterValue
+= energyValueRounded
;
653 connector
.energyActiveImportRegisterValue
= 0;
654 connector
.transactionEnergyActiveImportRegisterValue
= 0;
656 meterValue
.sampledValue
.push(
657 OCPP16ServiceUtils
.buildSampledValue(
658 energySampledValueTemplate
,
660 chargingStation
.getEnergyActiveImportRegisterByTransactionId(transactionId
) /
666 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
667 if (energyValueRounded
> connectorMaximumEnergyRounded
|| debug
) {
669 `${chargingStation.logPrefix()} MeterValues measurand ${
670 meterValue.sampledValue[sampledValuesIndex].measurand ??
671 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
672 }: connectorId ${connectorId}, transaction ${
673 connector.transactionId
674 }, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${Utils.roundTo(
675 interval / (3600 * 1000),
684 public static buildTransactionBeginMeterValue(
685 chargingStation
: ChargingStation
,
688 ): OCPP16MeterValue
{
689 const meterValue
: OCPP16MeterValue
= {
690 timestamp
: new Date().toISOString(),
693 // Energy.Active.Import.Register measurand (default)
694 const sampledValueTemplate
= chargingStation
.getSampledValueTemplate(connectorId
);
695 const unitDivider
= sampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
696 meterValue
.sampledValue
.push(
697 OCPP16ServiceUtils
.buildSampledValue(
698 sampledValueTemplate
,
699 Utils
.roundTo(meterStart
/ unitDivider
, 4),
700 MeterValueContext
.TRANSACTION_BEGIN
706 public static buildTransactionEndMeterValue(
707 chargingStation
: ChargingStation
,
710 ): OCPP16MeterValue
{
711 const meterValue
: OCPP16MeterValue
= {
712 timestamp
: new Date().toISOString(),
715 // Energy.Active.Import.Register measurand (default)
716 const sampledValueTemplate
= chargingStation
.getSampledValueTemplate(connectorId
);
717 const unitDivider
= sampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
718 meterValue
.sampledValue
.push(
719 OCPP16ServiceUtils
.buildSampledValue(
720 sampledValueTemplate
,
721 Utils
.roundTo(meterStop
/ unitDivider
, 4),
722 MeterValueContext
.TRANSACTION_END
728 public static buildTransactionDataMeterValues(
729 transactionBeginMeterValue
: OCPP16MeterValue
,
730 transactionEndMeterValue
: OCPP16MeterValue
731 ): OCPP16MeterValue
[] {
732 const meterValues
: OCPP16MeterValue
[] = [];
733 meterValues
.push(transactionBeginMeterValue
);
734 meterValues
.push(transactionEndMeterValue
);