1 // Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
3 import { ACElectricUtils
, DCElectricUtils
} from
'../../../utils/ElectricUtils';
6 OCPP16AuthorizeResponse
,
7 OCPP16StartTransactionResponse
,
8 OCPP16StopTransactionReason
,
9 OCPP16StopTransactionResponse
,
10 StartTransactionRequest
,
11 StopTransactionRequest
,
12 } from
'../../../types/ocpp/1.6/Transaction';
13 import { CurrentType
, Voltage
} from
'../../../types/ChargingStationTemplate';
15 DiagnosticsStatusNotificationRequest
,
17 OCPP16BootNotificationRequest
,
19 StatusNotificationRequest
,
20 } from
'../../../types/ocpp/1.6/Requests';
25 OCPP16MeterValueMeasurand
,
26 OCPP16MeterValuePhase
,
27 } from
'../../../types/ocpp/1.6/MeterValues';
29 import type ChargingStation from
'../../ChargingStation';
30 import Constants from
'../../../utils/Constants';
31 import { ErrorType
} from
'../../../types/ocpp/ErrorType';
32 import MeasurandPerPhaseSampledValueTemplates from
'../../../types/MeasurandPerPhaseSampledValueTemplates';
33 import MeasurandValues from
'../../../types/MeasurandValues';
34 import { OCPP16BootNotificationResponse
} from
'../../../types/ocpp/1.6/Responses';
35 import { OCPP16ChargePointErrorCode
} from
'../../../types/ocpp/1.6/ChargePointErrorCode';
36 import { OCPP16ChargePointStatus
} from
'../../../types/ocpp/1.6/ChargePointStatus';
37 import { OCPP16DiagnosticsStatus
} from
'../../../types/ocpp/1.6/DiagnosticsStatus';
38 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
39 import OCPPError from
'../../../exception/OCPPError';
40 import OCPPRequestService from
'../OCPPRequestService';
41 import type OCPPResponseService from
'../OCPPResponseService';
42 import { SendParams
} from
'../../../types/ocpp/Requests';
43 import Utils from
'../../../utils/Utils';
44 import logger from
'../../../utils/Logger';
46 const moduleName
= 'OCPP16RequestService';
48 export default class OCPP16RequestService
extends OCPPRequestService
{
49 public constructor(chargingStation
: ChargingStation
, ocppResponseService
: OCPPResponseService
) {
50 if (new.target
?.name
=== moduleName
) {
51 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
53 super(chargingStation
, ocppResponseService
);
56 public async sendHeartbeat(params
?: SendParams
): Promise
<void> {
57 const payload
: HeartbeatRequest
= {};
58 await this.sendMessage(Utils
.generateUUID(), payload
, OCPP16RequestCommand
.HEARTBEAT
, params
);
61 public async sendBootNotification(
62 chargePointModel
: string,
63 chargePointVendor
: string,
64 chargeBoxSerialNumber
?: string,
65 firmwareVersion
?: string,
66 chargePointSerialNumber
?: string,
69 meterSerialNumber
?: string,
72 ): Promise
<OCPP16BootNotificationResponse
> {
73 const payload
: OCPP16BootNotificationRequest
= {
76 ...(!Utils
.isUndefined(chargeBoxSerialNumber
) && { chargeBoxSerialNumber
}),
77 ...(!Utils
.isUndefined(chargePointSerialNumber
) && { chargePointSerialNumber
}),
78 ...(!Utils
.isUndefined(firmwareVersion
) && { firmwareVersion
}),
79 ...(!Utils
.isUndefined(iccid
) && { iccid
}),
80 ...(!Utils
.isUndefined(imsi
) && { imsi
}),
81 ...(!Utils
.isUndefined(meterSerialNumber
) && { meterSerialNumber
}),
82 ...(!Utils
.isUndefined(meterType
) && { meterType
}),
84 return (await this.sendMessage(
87 OCPP16RequestCommand
.BOOT_NOTIFICATION
,
88 { ...params
, skipBufferingOnError
: true }
89 )) as OCPP16BootNotificationResponse
;
92 public async sendStatusNotification(
94 status: OCPP16ChargePointStatus
,
95 errorCode
: OCPP16ChargePointErrorCode
= OCPP16ChargePointErrorCode
.NO_ERROR
97 const payload
: StatusNotificationRequest
= {
102 await this.sendMessage(Utils
.generateUUID(), payload
, OCPP16RequestCommand
.STATUS_NOTIFICATION
);
105 public async sendAuthorize(
108 ): Promise
<OCPP16AuthorizeResponse
> {
109 const payload
: AuthorizeRequest
= {
110 ...(!Utils
.isUndefined(idTag
) ? { idTag
} : { idTag
: Constants
.DEFAULT_IDTAG
}),
112 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
= idTag
;
113 return (await this.sendMessage(
114 Utils
.generateUUID(),
116 OCPP16RequestCommand
.AUTHORIZE
117 )) as OCPP16AuthorizeResponse
;
120 public async sendStartTransaction(
123 ): Promise
<OCPP16StartTransactionResponse
> {
124 const payload
: StartTransactionRequest
= {
126 ...(!Utils
.isUndefined(idTag
) ? { idTag
} : { idTag
: Constants
.DEFAULT_IDTAG
}),
127 meterStart
: this.chargingStation
.getEnergyActiveImportRegisterByConnectorId(connectorId
),
128 timestamp
: new Date().toISOString(),
130 return (await this.sendMessage(
131 Utils
.generateUUID(),
133 OCPP16RequestCommand
.START_TRANSACTION
134 )) as OCPP16StartTransactionResponse
;
137 public async sendStopTransaction(
138 transactionId
: number,
141 reason
: OCPP16StopTransactionReason
= OCPP16StopTransactionReason
.NONE
142 ): Promise
<OCPP16StopTransactionResponse
> {
143 let connectorId
: number;
144 for (const id
of this.chargingStation
.connectors
.keys()) {
145 if (id
> 0 && this.chargingStation
.getConnectorStatus(id
)?.transactionId
=== transactionId
) {
150 const transactionEndMeterValue
= OCPP16ServiceUtils
.buildTransactionEndMeterValue(
151 this.chargingStation
,
155 // FIXME: should be a callback, each OCPP commands implementation must do only one job
156 this.chargingStation
.getBeginEndMeterValues() &&
157 this.chargingStation
.getOcppStrictCompliance() &&
158 !this.chargingStation
.getOutOfOrderEndMeterValues() &&
159 (await this.sendTransactionEndMeterValues(
162 transactionEndMeterValue
164 const payload
: StopTransactionRequest
= {
166 ...(!Utils
.isUndefined(idTag
) && { idTag
}),
168 timestamp
: new Date().toISOString(),
169 ...(reason
&& { reason
}),
170 ...(this.chargingStation
.getTransactionDataMeterValues() && {
171 transactionData
: OCPP16ServiceUtils
.buildTransactionDataMeterValues(
172 this.chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
,
173 transactionEndMeterValue
177 return (await this.sendMessage(
178 Utils
.generateUUID(),
180 OCPP16RequestCommand
.STOP_TRANSACTION
181 )) as OCPP16StartTransactionResponse
;
184 public async sendMeterValues(
186 transactionId
: number,
190 const meterValue
: OCPP16MeterValue
= {
191 timestamp
: new Date().toISOString(),
194 const connector
= this.chargingStation
.getConnectorStatus(connectorId
);
196 const socSampledValueTemplate
= this.chargingStation
.getSampledValueTemplate(
198 OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
200 if (socSampledValueTemplate
) {
201 const socSampledValueTemplateValue
= socSampledValueTemplate
.value
202 ? Utils
.getRandomFloatFluctuatedRounded(
203 parseInt(socSampledValueTemplate
.value
),
204 socSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
206 : Utils
.getRandomInteger(100);
207 meterValue
.sampledValue
.push(
208 OCPP16ServiceUtils
.buildSampledValue(socSampledValueTemplate
, socSampledValueTemplateValue
)
210 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
211 if (Utils
.convertToInt(meterValue
.sampledValue
[sampledValuesIndex
].value
) > 100 || debug
) {
213 `${this.chargingStation.logPrefix()} MeterValues measurand ${
214 meterValue.sampledValue[sampledValuesIndex].measurand ??
215 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
216 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
217 meterValue.sampledValue[sampledValuesIndex].value
223 const voltageSampledValueTemplate
= this.chargingStation
.getSampledValueTemplate(
225 OCPP16MeterValueMeasurand
.VOLTAGE
227 if (voltageSampledValueTemplate
) {
228 const voltageSampledValueTemplateValue
= voltageSampledValueTemplate
.value
229 ? parseInt(voltageSampledValueTemplate
.value
)
230 : this.chargingStation
.getVoltageOut();
231 const fluctuationPercent
=
232 voltageSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
;
233 const voltageMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
234 voltageSampledValueTemplateValue
,
238 this.chargingStation
.getNumberOfPhases() !== 3 ||
239 (this.chargingStation
.getNumberOfPhases() === 3 &&
240 this.chargingStation
.getMainVoltageMeterValues())
242 meterValue
.sampledValue
.push(
243 OCPP16ServiceUtils
.buildSampledValue(voltageSampledValueTemplate
, voltageMeasurandValue
)
248 this.chargingStation
.getNumberOfPhases() === 3 &&
249 phase
<= this.chargingStation
.getNumberOfPhases();
252 const phaseLineToNeutralValue
= `L${phase}-N`;
253 const voltagePhaseLineToNeutralSampledValueTemplate
=
254 this.chargingStation
.getSampledValueTemplate(
256 OCPP16MeterValueMeasurand
.VOLTAGE
,
257 phaseLineToNeutralValue
as OCPP16MeterValuePhase
259 let voltagePhaseLineToNeutralMeasurandValue
: number;
260 if (voltagePhaseLineToNeutralSampledValueTemplate
) {
261 const voltagePhaseLineToNeutralSampledValueTemplateValue
=
262 voltagePhaseLineToNeutralSampledValueTemplate
.value
263 ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate
.value
)
264 : this.chargingStation
.getVoltageOut();
265 const fluctuationPhaseToNeutralPercent
=
266 voltagePhaseLineToNeutralSampledValueTemplate
.fluctuationPercent
??
267 Constants
.DEFAULT_FLUCTUATION_PERCENT
;
268 voltagePhaseLineToNeutralMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
269 voltagePhaseLineToNeutralSampledValueTemplateValue
,
270 fluctuationPhaseToNeutralPercent
273 meterValue
.sampledValue
.push(
274 OCPP16ServiceUtils
.buildSampledValue(
275 voltagePhaseLineToNeutralSampledValueTemplate
?? voltageSampledValueTemplate
,
276 voltagePhaseLineToNeutralMeasurandValue
?? voltageMeasurandValue
,
278 phaseLineToNeutralValue
as OCPP16MeterValuePhase
281 if (this.chargingStation
.getPhaseLineToLineVoltageMeterValues()) {
282 const phaseLineToLineValue
= `L${phase}-L${
283 (phase + 1) % this.chargingStation.getNumberOfPhases() !== 0
284 ? (phase + 1) % this.chargingStation.getNumberOfPhases()
285 : this.chargingStation.getNumberOfPhases()
287 const voltagePhaseLineToLineSampledValueTemplate
=
288 this.chargingStation
.getSampledValueTemplate(
290 OCPP16MeterValueMeasurand
.VOLTAGE
,
291 phaseLineToLineValue
as OCPP16MeterValuePhase
293 let voltagePhaseLineToLineMeasurandValue
: number;
294 if (voltagePhaseLineToLineSampledValueTemplate
) {
295 const voltagePhaseLineToLineSampledValueTemplateValue
=
296 voltagePhaseLineToLineSampledValueTemplate
.value
297 ? parseInt(voltagePhaseLineToLineSampledValueTemplate
.value
)
298 : Voltage
.VOLTAGE_400
;
299 const fluctuationPhaseLineToLinePercent
=
300 voltagePhaseLineToLineSampledValueTemplate
.fluctuationPercent
??
301 Constants
.DEFAULT_FLUCTUATION_PERCENT
;
302 voltagePhaseLineToLineMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
303 voltagePhaseLineToLineSampledValueTemplateValue
,
304 fluctuationPhaseLineToLinePercent
307 const defaultVoltagePhaseLineToLineMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
311 meterValue
.sampledValue
.push(
312 OCPP16ServiceUtils
.buildSampledValue(
313 voltagePhaseLineToLineSampledValueTemplate
?? voltageSampledValueTemplate
,
314 voltagePhaseLineToLineMeasurandValue
?? defaultVoltagePhaseLineToLineMeasurandValue
,
316 phaseLineToLineValue
as OCPP16MeterValuePhase
322 // Power.Active.Import measurand
323 const powerSampledValueTemplate
= this.chargingStation
.getSampledValueTemplate(
325 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
327 let powerPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
328 if (this.chargingStation
.getNumberOfPhases() === 3) {
329 powerPerPhaseSampledValueTemplates
= {
330 L1
: this.chargingStation
.getSampledValueTemplate(
332 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
333 OCPP16MeterValuePhase
.L1_N
335 L2
: this.chargingStation
.getSampledValueTemplate(
337 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
338 OCPP16MeterValuePhase
.L2_N
340 L3
: this.chargingStation
.getSampledValueTemplate(
342 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
343 OCPP16MeterValuePhase
.L3_N
347 if (powerSampledValueTemplate
) {
348 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
349 this.chargingStation
,
350 powerSampledValueTemplate
.measurand
352 const errMsg
= `${this.chargingStation.logPrefix()} MeterValues measurand ${
353 powerSampledValueTemplate.measurand ??
354 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
355 }: Unknown ${this.chargingStation.getCurrentOutType()} currentOutType in template file ${
356 this.chargingStation.stationTemplateFile
357 }, cannot calculate ${
358 powerSampledValueTemplate.measurand ??
359 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
361 const powerMeasurandValues
= {} as MeasurandValues
;
362 const unitDivider
= powerSampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT
? 1000 : 1;
363 const maxPower
= Math.round(
364 this.chargingStation
.stationInfo
.maxPower
/ this.chargingStation
.stationInfo
.powerDivider
366 const maxPowerPerPhase
= Math.round(
367 this.chargingStation
.stationInfo
.maxPower
/
368 this.chargingStation
.stationInfo
.powerDivider
/
369 this.chargingStation
.getNumberOfPhases()
371 switch (this.chargingStation
.getCurrentOutType()) {
373 if (this.chargingStation
.getNumberOfPhases() === 3) {
374 const defaultFluctuatedPowerPerPhase
=
375 powerSampledValueTemplate
.value
&&
376 Utils
.getRandomFloatFluctuatedRounded(
377 parseInt(powerSampledValueTemplate
.value
) /
378 this.chargingStation
.getNumberOfPhases(),
379 powerSampledValueTemplate
.fluctuationPercent
??
380 Constants
.DEFAULT_FLUCTUATION_PERCENT
382 const phase1FluctuatedValue
=
383 powerPerPhaseSampledValueTemplates
?.L1
?.value
&&
384 Utils
.getRandomFloatFluctuatedRounded(
385 parseInt(powerPerPhaseSampledValueTemplates
.L1
.value
),
386 powerPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
??
387 Constants
.DEFAULT_FLUCTUATION_PERCENT
389 const phase2FluctuatedValue
=
390 powerPerPhaseSampledValueTemplates
?.L2
?.value
&&
391 Utils
.getRandomFloatFluctuatedRounded(
392 parseInt(powerPerPhaseSampledValueTemplates
.L2
.value
),
393 powerPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
??
394 Constants
.DEFAULT_FLUCTUATION_PERCENT
396 const phase3FluctuatedValue
=
397 powerPerPhaseSampledValueTemplates
?.L3
?.value
&&
398 Utils
.getRandomFloatFluctuatedRounded(
399 parseInt(powerPerPhaseSampledValueTemplates
.L3
.value
),
400 powerPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
??
401 Constants
.DEFAULT_FLUCTUATION_PERCENT
403 powerMeasurandValues
.L1
=
404 phase1FluctuatedValue
??
405 defaultFluctuatedPowerPerPhase
??
406 Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
407 powerMeasurandValues
.L2
=
408 phase2FluctuatedValue
??
409 defaultFluctuatedPowerPerPhase
??
410 Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
411 powerMeasurandValues
.L3
=
412 phase3FluctuatedValue
??
413 defaultFluctuatedPowerPerPhase
??
414 Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
416 powerMeasurandValues
.L1
= powerSampledValueTemplate
.value
417 ? Utils
.getRandomFloatFluctuatedRounded(
418 parseInt(powerSampledValueTemplate
.value
),
419 powerSampledValueTemplate
.fluctuationPercent
??
420 Constants
.DEFAULT_FLUCTUATION_PERCENT
422 : Utils
.getRandomFloatRounded(maxPower
/ unitDivider
);
423 powerMeasurandValues
.L2
= 0;
424 powerMeasurandValues
.L3
= 0;
426 powerMeasurandValues
.allPhases
= Utils
.roundTo(
427 powerMeasurandValues
.L1
+ powerMeasurandValues
.L2
+ powerMeasurandValues
.L3
,
432 powerMeasurandValues
.allPhases
= powerSampledValueTemplate
.value
433 ? Utils
.getRandomFloatFluctuatedRounded(
434 parseInt(powerSampledValueTemplate
.value
),
435 powerSampledValueTemplate
.fluctuationPercent
??
436 Constants
.DEFAULT_FLUCTUATION_PERCENT
438 : Utils
.getRandomFloatRounded(maxPower
/ unitDivider
);
441 logger
.error(errMsg
);
442 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
444 meterValue
.sampledValue
.push(
445 OCPP16ServiceUtils
.buildSampledValue(
446 powerSampledValueTemplate
,
447 powerMeasurandValues
.allPhases
450 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
451 const maxPowerRounded
= Utils
.roundTo(maxPower
/ unitDivider
, 2);
453 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxPowerRounded
||
457 `${this.chargingStation.logPrefix()} MeterValues measurand ${
458 meterValue.sampledValue[sampledValuesIndex].measurand ??
459 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
460 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
461 meterValue.sampledValue[sampledValuesIndex].value
462 }/${maxPowerRounded}`
467 this.chargingStation
.getNumberOfPhases() === 3 &&
468 phase
<= this.chargingStation
.getNumberOfPhases();
471 const phaseValue
= `L${phase}-N`;
472 meterValue
.sampledValue
.push(
473 OCPP16ServiceUtils
.buildSampledValue(
474 powerPerPhaseSampledValueTemplates
[`L${phase}`] ?? powerSampledValueTemplate
,
475 powerMeasurandValues
[`L${phase}`],
477 phaseValue
as OCPP16MeterValuePhase
480 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
481 const maxPowerPerPhaseRounded
= Utils
.roundTo(maxPowerPerPhase
/ unitDivider
, 2);
483 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) >
484 maxPowerPerPhaseRounded
||
488 `${this.chargingStation.logPrefix()} MeterValues measurand ${
489 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
490 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
492 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
493 }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
494 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
495 }/${maxPowerPerPhaseRounded}`
500 // Current.Import measurand
501 const currentSampledValueTemplate
= this.chargingStation
.getSampledValueTemplate(
503 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
505 let currentPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
506 if (this.chargingStation
.getNumberOfPhases() === 3) {
507 currentPerPhaseSampledValueTemplates
= {
508 L1
: this.chargingStation
.getSampledValueTemplate(
510 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
511 OCPP16MeterValuePhase
.L1
513 L2
: this.chargingStation
.getSampledValueTemplate(
515 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
516 OCPP16MeterValuePhase
.L2
518 L3
: this.chargingStation
.getSampledValueTemplate(
520 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
521 OCPP16MeterValuePhase
.L3
525 if (currentSampledValueTemplate
) {
526 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
527 this.chargingStation
,
528 currentSampledValueTemplate
.measurand
530 const errMsg
= `${this.chargingStation.logPrefix()} MeterValues measurand ${
531 currentSampledValueTemplate.measurand ??
532 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
533 }: Unknown ${this.chargingStation.getCurrentOutType()} currentOutType in template file ${
534 this.chargingStation.stationTemplateFile
535 }, cannot calculate ${
536 currentSampledValueTemplate.measurand ??
537 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
539 const currentMeasurandValues
: MeasurandValues
= {} as MeasurandValues
;
540 let maxAmperage
: number;
541 switch (this.chargingStation
.getCurrentOutType()) {
543 maxAmperage
= ACElectricUtils
.amperagePerPhaseFromPower(
544 this.chargingStation
.getNumberOfPhases(),
545 this.chargingStation
.stationInfo
.maxPower
/
546 this.chargingStation
.stationInfo
.powerDivider
,
547 this.chargingStation
.getVoltageOut()
549 if (this.chargingStation
.getNumberOfPhases() === 3) {
550 const defaultFluctuatedAmperagePerPhase
=
551 currentSampledValueTemplate
.value
&&
552 Utils
.getRandomFloatFluctuatedRounded(
553 parseInt(currentSampledValueTemplate
.value
),
554 currentSampledValueTemplate
.fluctuationPercent
??
555 Constants
.DEFAULT_FLUCTUATION_PERCENT
557 const phase1FluctuatedValue
=
558 currentPerPhaseSampledValueTemplates
?.L1
?.value
&&
559 Utils
.getRandomFloatFluctuatedRounded(
560 parseInt(currentPerPhaseSampledValueTemplates
.L1
.value
),
561 currentPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
??
562 Constants
.DEFAULT_FLUCTUATION_PERCENT
564 const phase2FluctuatedValue
=
565 currentPerPhaseSampledValueTemplates
?.L2
?.value
&&
566 Utils
.getRandomFloatFluctuatedRounded(
567 parseInt(currentPerPhaseSampledValueTemplates
.L2
.value
),
568 currentPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
??
569 Constants
.DEFAULT_FLUCTUATION_PERCENT
571 const phase3FluctuatedValue
=
572 currentPerPhaseSampledValueTemplates
?.L3
?.value
&&
573 Utils
.getRandomFloatFluctuatedRounded(
574 parseInt(currentPerPhaseSampledValueTemplates
.L3
.value
),
575 currentPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
??
576 Constants
.DEFAULT_FLUCTUATION_PERCENT
578 currentMeasurandValues
.L1
=
579 phase1FluctuatedValue
??
580 defaultFluctuatedAmperagePerPhase
??
581 Utils
.getRandomFloatRounded(maxAmperage
);
582 currentMeasurandValues
.L2
=
583 phase2FluctuatedValue
??
584 defaultFluctuatedAmperagePerPhase
??
585 Utils
.getRandomFloatRounded(maxAmperage
);
586 currentMeasurandValues
.L3
=
587 phase3FluctuatedValue
??
588 defaultFluctuatedAmperagePerPhase
??
589 Utils
.getRandomFloatRounded(maxAmperage
);
591 currentMeasurandValues
.L1
= currentSampledValueTemplate
.value
592 ? Utils
.getRandomFloatFluctuatedRounded(
593 parseInt(currentSampledValueTemplate
.value
),
594 currentSampledValueTemplate
.fluctuationPercent
??
595 Constants
.DEFAULT_FLUCTUATION_PERCENT
597 : Utils
.getRandomFloatRounded(maxAmperage
);
598 currentMeasurandValues
.L2
= 0;
599 currentMeasurandValues
.L3
= 0;
601 currentMeasurandValues
.allPhases
= Utils
.roundTo(
602 (currentMeasurandValues
.L1
+ currentMeasurandValues
.L2
+ currentMeasurandValues
.L3
) /
603 this.chargingStation
.getNumberOfPhases(),
608 maxAmperage
= DCElectricUtils
.amperage(
609 this.chargingStation
.stationInfo
.maxPower
/
610 this.chargingStation
.stationInfo
.powerDivider
,
611 this.chargingStation
.getVoltageOut()
613 currentMeasurandValues
.allPhases
= currentSampledValueTemplate
.value
614 ? Utils
.getRandomFloatFluctuatedRounded(
615 parseInt(currentSampledValueTemplate
.value
),
616 currentSampledValueTemplate
.fluctuationPercent
??
617 Constants
.DEFAULT_FLUCTUATION_PERCENT
619 : Utils
.getRandomFloatRounded(maxAmperage
);
622 logger
.error(errMsg
);
623 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
625 meterValue
.sampledValue
.push(
626 OCPP16ServiceUtils
.buildSampledValue(
627 currentSampledValueTemplate
,
628 currentMeasurandValues
.allPhases
631 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
633 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxAmperage
||
637 `${this.chargingStation.logPrefix()} MeterValues measurand ${
638 meterValue.sampledValue[sampledValuesIndex].measurand ??
639 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
640 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
641 meterValue.sampledValue[sampledValuesIndex].value
647 this.chargingStation
.getNumberOfPhases() === 3 &&
648 phase
<= this.chargingStation
.getNumberOfPhases();
651 const phaseValue
= `L${phase}`;
652 meterValue
.sampledValue
.push(
653 OCPP16ServiceUtils
.buildSampledValue(
654 currentPerPhaseSampledValueTemplates
[phaseValue
] ?? currentSampledValueTemplate
,
655 currentMeasurandValues
[phaseValue
],
657 phaseValue
as OCPP16MeterValuePhase
660 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
662 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) >
667 `${this.chargingStation.logPrefix()} MeterValues measurand ${
668 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
669 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
671 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
672 }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
673 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
679 // Energy.Active.Import.Register measurand (default)
680 const energySampledValueTemplate
= this.chargingStation
.getSampledValueTemplate(connectorId
);
681 if (energySampledValueTemplate
) {
682 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
683 this.chargingStation
,
684 energySampledValueTemplate
.measurand
687 energySampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
688 const maxEnergyRounded
= Utils
.roundTo(
689 ((this.chargingStation
.stationInfo
.maxPower
/
690 this.chargingStation
.stationInfo
.powerDivider
) *
695 const energyValueRounded
= energySampledValueTemplate
.value
696 ? // Cumulate the fluctuated value around the static one
697 Utils
.getRandomFloatFluctuatedRounded(
698 parseInt(energySampledValueTemplate
.value
),
699 energySampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
701 : Utils
.getRandomFloatRounded(maxEnergyRounded
);
702 // Persist previous value on connector
705 !Utils
.isNullOrUndefined(connector
.energyActiveImportRegisterValue
) &&
706 connector
.energyActiveImportRegisterValue
>= 0 &&
707 !Utils
.isNullOrUndefined(connector
.transactionEnergyActiveImportRegisterValue
) &&
708 connector
.transactionEnergyActiveImportRegisterValue
>= 0
710 connector
.energyActiveImportRegisterValue
+= energyValueRounded
;
711 connector
.transactionEnergyActiveImportRegisterValue
+= energyValueRounded
;
713 connector
.energyActiveImportRegisterValue
= 0;
714 connector
.transactionEnergyActiveImportRegisterValue
= 0;
716 meterValue
.sampledValue
.push(
717 OCPP16ServiceUtils
.buildSampledValue(
718 energySampledValueTemplate
,
720 this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(transactionId
) /
726 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
727 if (energyValueRounded
> maxEnergyRounded
|| debug
) {
729 `${this.chargingStation.logPrefix()} MeterValues measurand ${
730 meterValue.sampledValue[sampledValuesIndex].measurand ??
731 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
732 }: connectorId ${connectorId}, transaction ${
733 connector.transactionId
734 }, value: ${energyValueRounded}/${maxEnergyRounded}, duration: ${Utils.roundTo(
735 interval / (3600 * 1000),
741 const payload
: MeterValuesRequest
= {
744 meterValue
: [meterValue
],
746 await this.sendMessage(Utils
.generateUUID(), payload
, OCPP16RequestCommand
.METER_VALUES
);
749 public async sendTransactionBeginMeterValues(
751 transactionId
: number,
752 beginMeterValue
: OCPP16MeterValue
754 const payload
: MeterValuesRequest
= {
757 meterValue
: [beginMeterValue
],
759 await this.sendMessage(Utils
.generateUUID(), payload
, OCPP16RequestCommand
.METER_VALUES
);
762 public async sendTransactionEndMeterValues(
764 transactionId
: number,
765 endMeterValue
: OCPP16MeterValue
767 const payload
: MeterValuesRequest
= {
770 meterValue
: [endMeterValue
],
772 await this.sendMessage(Utils
.generateUUID(), payload
, OCPP16RequestCommand
.METER_VALUES
);
775 public async sendDiagnosticsStatusNotification(
776 diagnosticsStatus
: OCPP16DiagnosticsStatus
778 const payload
: DiagnosticsStatusNotificationRequest
= {
779 status: diagnosticsStatus
,
781 await this.sendMessage(
782 Utils
.generateUUID(),
784 OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION