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';
21 import MeasurandPerPhaseSampledValueTemplates
, {
23 } from
'../../../types/MeasurandPerPhaseSampledValueTemplates';
28 OCPP16MeterValueMeasurand
,
29 OCPP16MeterValuePhase
,
30 } from
'../../../types/ocpp/1.6/MeterValues';
32 import type ChargingStation from
'../../ChargingStation';
33 import Constants from
'../../../utils/Constants';
34 import { ErrorType
} from
'../../../types/ocpp/ErrorType';
35 import MeasurandValues from
'../../../types/MeasurandValues';
36 import { OCPP16BootNotificationResponse
} from
'../../../types/ocpp/1.6/Responses';
37 import { OCPP16ChargePointErrorCode
} from
'../../../types/ocpp/1.6/ChargePointErrorCode';
38 import { OCPP16ChargePointStatus
} from
'../../../types/ocpp/1.6/ChargePointStatus';
39 import { OCPP16DiagnosticsStatus
} from
'../../../types/ocpp/1.6/DiagnosticsStatus';
40 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
41 import OCPPError from
'../../../exception/OCPPError';
42 import OCPPRequestService from
'../OCPPRequestService';
43 import type OCPPResponseService from
'../OCPPResponseService';
44 import { SendParams
} from
'../../../types/ocpp/Requests';
45 import Utils from
'../../../utils/Utils';
46 import logger from
'../../../utils/Logger';
48 const moduleName
= 'OCPP16RequestService';
50 export default class OCPP16RequestService
extends OCPPRequestService
{
51 public constructor(chargingStation
: ChargingStation
, ocppResponseService
: OCPPResponseService
) {
52 if (new.target
?.name
=== moduleName
) {
53 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
55 super(chargingStation
, ocppResponseService
);
58 public async sendHeartbeat(params
?: SendParams
): Promise
<void> {
59 const payload
: HeartbeatRequest
= {};
60 await this.sendMessage(Utils
.generateUUID(), payload
, OCPP16RequestCommand
.HEARTBEAT
, params
);
63 public async sendBootNotification(
64 chargePointModel
: string,
65 chargePointVendor
: string,
66 chargeBoxSerialNumber
?: string,
67 firmwareVersion
?: string,
68 chargePointSerialNumber
?: string,
71 meterSerialNumber
?: string,
74 ): Promise
<OCPP16BootNotificationResponse
> {
75 const payload
: OCPP16BootNotificationRequest
= {
78 ...(!Utils
.isUndefined(chargeBoxSerialNumber
) && { chargeBoxSerialNumber
}),
79 ...(!Utils
.isUndefined(chargePointSerialNumber
) && { chargePointSerialNumber
}),
80 ...(!Utils
.isUndefined(firmwareVersion
) && { firmwareVersion
}),
81 ...(!Utils
.isUndefined(iccid
) && { iccid
}),
82 ...(!Utils
.isUndefined(imsi
) && { imsi
}),
83 ...(!Utils
.isUndefined(meterSerialNumber
) && { meterSerialNumber
}),
84 ...(!Utils
.isUndefined(meterType
) && { meterType
}),
86 return (await this.sendMessage(
89 OCPP16RequestCommand
.BOOT_NOTIFICATION
,
90 { ...params
, skipBufferingOnError
: true }
91 )) as OCPP16BootNotificationResponse
;
94 public async sendStatusNotification(
96 status: OCPP16ChargePointStatus
,
97 errorCode
: OCPP16ChargePointErrorCode
= OCPP16ChargePointErrorCode
.NO_ERROR
99 const payload
: StatusNotificationRequest
= {
104 await this.sendMessage(Utils
.generateUUID(), payload
, OCPP16RequestCommand
.STATUS_NOTIFICATION
);
107 public async sendAuthorize(
110 ): Promise
<OCPP16AuthorizeResponse
> {
111 const payload
: AuthorizeRequest
= {
112 ...(!Utils
.isUndefined(idTag
) ? { idTag
} : { idTag
: Constants
.DEFAULT_IDTAG
}),
114 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
= idTag
;
115 return (await this.sendMessage(
116 Utils
.generateUUID(),
118 OCPP16RequestCommand
.AUTHORIZE
119 )) as OCPP16AuthorizeResponse
;
122 public async sendStartTransaction(
125 ): Promise
<OCPP16StartTransactionResponse
> {
126 const payload
: StartTransactionRequest
= {
128 ...(!Utils
.isUndefined(idTag
) ? { idTag
} : { idTag
: Constants
.DEFAULT_IDTAG
}),
129 meterStart
: this.chargingStation
.getEnergyActiveImportRegisterByConnectorId(connectorId
),
130 timestamp
: new Date().toISOString(),
132 return (await this.sendMessage(
133 Utils
.generateUUID(),
135 OCPP16RequestCommand
.START_TRANSACTION
136 )) as OCPP16StartTransactionResponse
;
139 public async sendStopTransaction(
140 transactionId
: number,
143 reason
: OCPP16StopTransactionReason
= OCPP16StopTransactionReason
.NONE
144 ): Promise
<OCPP16StopTransactionResponse
> {
145 let connectorId
: number;
146 for (const id
of this.chargingStation
.connectors
.keys()) {
147 if (id
> 0 && this.chargingStation
.getConnectorStatus(id
)?.transactionId
=== transactionId
) {
152 const transactionEndMeterValue
= OCPP16ServiceUtils
.buildTransactionEndMeterValue(
153 this.chargingStation
,
157 // FIXME: should be a callback, each OCPP commands implementation must do only one job
158 this.chargingStation
.getBeginEndMeterValues() &&
159 this.chargingStation
.getOcppStrictCompliance() &&
160 !this.chargingStation
.getOutOfOrderEndMeterValues() &&
161 (await this.sendTransactionEndMeterValues(
164 transactionEndMeterValue
166 const payload
: StopTransactionRequest
= {
168 ...(!Utils
.isUndefined(idTag
) && { idTag
}),
170 timestamp
: new Date().toISOString(),
171 ...(reason
&& { reason
}),
172 ...(this.chargingStation
.getTransactionDataMeterValues() && {
173 transactionData
: OCPP16ServiceUtils
.buildTransactionDataMeterValues(
174 this.chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
,
175 transactionEndMeterValue
179 return (await this.sendMessage(
180 Utils
.generateUUID(),
182 OCPP16RequestCommand
.STOP_TRANSACTION
183 )) as OCPP16StartTransactionResponse
;
186 public async sendMeterValues(
188 transactionId
: number,
192 const meterValue
: OCPP16MeterValue
= {
193 timestamp
: new Date().toISOString(),
196 const connector
= this.chargingStation
.getConnectorStatus(connectorId
);
198 const socSampledValueTemplate
= this.chargingStation
.getSampledValueTemplate(
200 OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
202 if (socSampledValueTemplate
) {
203 const socSampledValueTemplateValue
= socSampledValueTemplate
.value
204 ? Utils
.getRandomFloatFluctuatedRounded(
205 parseInt(socSampledValueTemplate
.value
),
206 socSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
208 : Utils
.getRandomInteger(100);
209 meterValue
.sampledValue
.push(
210 OCPP16ServiceUtils
.buildSampledValue(socSampledValueTemplate
, socSampledValueTemplateValue
)
212 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
213 if (Utils
.convertToInt(meterValue
.sampledValue
[sampledValuesIndex
].value
) > 100 || debug
) {
215 `${this.chargingStation.logPrefix()} MeterValues measurand ${
216 meterValue.sampledValue[sampledValuesIndex].measurand ??
217 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
218 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
219 meterValue.sampledValue[sampledValuesIndex].value
225 const voltageSampledValueTemplate
= this.chargingStation
.getSampledValueTemplate(
227 OCPP16MeterValueMeasurand
.VOLTAGE
229 if (voltageSampledValueTemplate
) {
230 const voltageSampledValueTemplateValue
= voltageSampledValueTemplate
.value
231 ? parseInt(voltageSampledValueTemplate
.value
)
232 : this.chargingStation
.getVoltageOut();
233 const fluctuationPercent
=
234 voltageSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
;
235 const voltageMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
236 voltageSampledValueTemplateValue
,
240 this.chargingStation
.getNumberOfPhases() !== 3 ||
241 (this.chargingStation
.getNumberOfPhases() === 3 &&
242 this.chargingStation
.getMainVoltageMeterValues())
244 meterValue
.sampledValue
.push(
245 OCPP16ServiceUtils
.buildSampledValue(voltageSampledValueTemplate
, voltageMeasurandValue
)
250 this.chargingStation
.getNumberOfPhases() === 3 &&
251 phase
<= this.chargingStation
.getNumberOfPhases();
254 const phaseLineToNeutralValue
= `L${phase}-N`;
255 const voltagePhaseLineToNeutralSampledValueTemplate
=
256 this.chargingStation
.getSampledValueTemplate(
258 OCPP16MeterValueMeasurand
.VOLTAGE
,
259 phaseLineToNeutralValue
as OCPP16MeterValuePhase
261 let voltagePhaseLineToNeutralMeasurandValue
: number;
262 if (voltagePhaseLineToNeutralSampledValueTemplate
) {
263 const voltagePhaseLineToNeutralSampledValueTemplateValue
=
264 voltagePhaseLineToNeutralSampledValueTemplate
.value
265 ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate
.value
)
266 : this.chargingStation
.getVoltageOut();
267 const fluctuationPhaseToNeutralPercent
=
268 voltagePhaseLineToNeutralSampledValueTemplate
.fluctuationPercent
??
269 Constants
.DEFAULT_FLUCTUATION_PERCENT
;
270 voltagePhaseLineToNeutralMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
271 voltagePhaseLineToNeutralSampledValueTemplateValue
,
272 fluctuationPhaseToNeutralPercent
275 meterValue
.sampledValue
.push(
276 OCPP16ServiceUtils
.buildSampledValue(
277 voltagePhaseLineToNeutralSampledValueTemplate
?? voltageSampledValueTemplate
,
278 voltagePhaseLineToNeutralMeasurandValue
?? voltageMeasurandValue
,
280 phaseLineToNeutralValue
as OCPP16MeterValuePhase
283 if (this.chargingStation
.getPhaseLineToLineVoltageMeterValues()) {
284 const phaseLineToLineValue
= `L${phase}-L${
285 (phase + 1) % this.chargingStation.getNumberOfPhases() !== 0
286 ? (phase + 1) % this.chargingStation.getNumberOfPhases()
287 : this.chargingStation.getNumberOfPhases()
289 const voltagePhaseLineToLineSampledValueTemplate
=
290 this.chargingStation
.getSampledValueTemplate(
292 OCPP16MeterValueMeasurand
.VOLTAGE
,
293 phaseLineToLineValue
as OCPP16MeterValuePhase
295 let voltagePhaseLineToLineMeasurandValue
: number;
296 if (voltagePhaseLineToLineSampledValueTemplate
) {
297 const voltagePhaseLineToLineSampledValueTemplateValue
=
298 voltagePhaseLineToLineSampledValueTemplate
.value
299 ? parseInt(voltagePhaseLineToLineSampledValueTemplate
.value
)
300 : Voltage
.VOLTAGE_400
;
301 const fluctuationPhaseLineToLinePercent
=
302 voltagePhaseLineToLineSampledValueTemplate
.fluctuationPercent
??
303 Constants
.DEFAULT_FLUCTUATION_PERCENT
;
304 voltagePhaseLineToLineMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
305 voltagePhaseLineToLineSampledValueTemplateValue
,
306 fluctuationPhaseLineToLinePercent
309 const defaultVoltagePhaseLineToLineMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(
313 meterValue
.sampledValue
.push(
314 OCPP16ServiceUtils
.buildSampledValue(
315 voltagePhaseLineToLineSampledValueTemplate
?? voltageSampledValueTemplate
,
316 voltagePhaseLineToLineMeasurandValue
?? defaultVoltagePhaseLineToLineMeasurandValue
,
318 phaseLineToLineValue
as OCPP16MeterValuePhase
324 // Power.Active.Import measurand
325 const powerSampledValueTemplate
= this.chargingStation
.getSampledValueTemplate(
327 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
329 let powerPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
330 if (this.chargingStation
.getNumberOfPhases() === 3) {
331 powerPerPhaseSampledValueTemplates
= {
332 L1
: this.chargingStation
.getSampledValueTemplate(
334 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
335 OCPP16MeterValuePhase
.L1_N
337 L2
: this.chargingStation
.getSampledValueTemplate(
339 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
340 OCPP16MeterValuePhase
.L2_N
342 L3
: this.chargingStation
.getSampledValueTemplate(
344 OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
,
345 OCPP16MeterValuePhase
.L3_N
349 if (powerSampledValueTemplate
) {
350 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
351 this.chargingStation
,
352 powerSampledValueTemplate
.measurand
354 const errMsg
= `${this.chargingStation.logPrefix()} MeterValues measurand ${
355 powerSampledValueTemplate.measurand ??
356 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
357 }: Unknown ${this.chargingStation.getCurrentOutType()} currentOutType in template file ${
358 this.chargingStation.stationTemplateFile
359 }, cannot calculate ${
360 powerSampledValueTemplate.measurand ??
361 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
363 const powerMeasurandValues
= {} as MeasurandValues
;
364 const unitDivider
= powerSampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT
? 1000 : 1;
365 const maxPower
= Math.round(
366 this.chargingStation
.stationInfo
.maxPower
/ this.chargingStation
.stationInfo
.powerDivider
368 const maxPowerPerPhase
= Math.round(
369 this.chargingStation
.stationInfo
.maxPower
/
370 this.chargingStation
.stationInfo
.powerDivider
/
371 this.chargingStation
.getNumberOfPhases()
373 switch (this.chargingStation
.getCurrentOutType()) {
375 if (this.chargingStation
.getNumberOfPhases() === 3) {
376 const defaultFluctuatedPowerPerPhase
=
377 powerSampledValueTemplate
.value
&&
378 Utils
.getRandomFloatFluctuatedRounded(
379 parseInt(powerSampledValueTemplate
.value
) /
380 this.chargingStation
.getNumberOfPhases(),
381 powerSampledValueTemplate
.fluctuationPercent
??
382 Constants
.DEFAULT_FLUCTUATION_PERCENT
384 const phase1FluctuatedValue
=
385 powerPerPhaseSampledValueTemplates
?.L1
?.value
&&
386 Utils
.getRandomFloatFluctuatedRounded(
387 parseInt(powerPerPhaseSampledValueTemplates
.L1
.value
),
388 powerPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
??
389 Constants
.DEFAULT_FLUCTUATION_PERCENT
391 const phase2FluctuatedValue
=
392 powerPerPhaseSampledValueTemplates
?.L2
?.value
&&
393 Utils
.getRandomFloatFluctuatedRounded(
394 parseInt(powerPerPhaseSampledValueTemplates
.L2
.value
),
395 powerPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
??
396 Constants
.DEFAULT_FLUCTUATION_PERCENT
398 const phase3FluctuatedValue
=
399 powerPerPhaseSampledValueTemplates
?.L3
?.value
&&
400 Utils
.getRandomFloatFluctuatedRounded(
401 parseInt(powerPerPhaseSampledValueTemplates
.L3
.value
),
402 powerPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
??
403 Constants
.DEFAULT_FLUCTUATION_PERCENT
405 powerMeasurandValues
.L1
=
406 phase1FluctuatedValue
??
407 defaultFluctuatedPowerPerPhase
??
408 Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
409 powerMeasurandValues
.L2
=
410 phase2FluctuatedValue
??
411 defaultFluctuatedPowerPerPhase
??
412 Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
413 powerMeasurandValues
.L3
=
414 phase3FluctuatedValue
??
415 defaultFluctuatedPowerPerPhase
??
416 Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
418 powerMeasurandValues
.L1
= powerSampledValueTemplate
.value
419 ? Utils
.getRandomFloatFluctuatedRounded(
420 parseInt(powerSampledValueTemplate
.value
),
421 powerSampledValueTemplate
.fluctuationPercent
??
422 Constants
.DEFAULT_FLUCTUATION_PERCENT
424 : Utils
.getRandomFloatRounded(maxPower
/ unitDivider
);
425 powerMeasurandValues
.L2
= 0;
426 powerMeasurandValues
.L3
= 0;
428 powerMeasurandValues
.allPhases
= Utils
.roundTo(
429 powerMeasurandValues
.L1
+ powerMeasurandValues
.L2
+ powerMeasurandValues
.L3
,
434 powerMeasurandValues
.allPhases
= powerSampledValueTemplate
.value
435 ? Utils
.getRandomFloatFluctuatedRounded(
436 parseInt(powerSampledValueTemplate
.value
),
437 powerSampledValueTemplate
.fluctuationPercent
??
438 Constants
.DEFAULT_FLUCTUATION_PERCENT
440 : Utils
.getRandomFloatRounded(maxPower
/ unitDivider
);
443 logger
.error(errMsg
);
444 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
446 meterValue
.sampledValue
.push(
447 OCPP16ServiceUtils
.buildSampledValue(
448 powerSampledValueTemplate
,
449 powerMeasurandValues
.allPhases
452 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
453 const maxPowerRounded
= Utils
.roundTo(maxPower
/ unitDivider
, 2);
455 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxPowerRounded
||
459 `${this.chargingStation.logPrefix()} MeterValues measurand ${
460 meterValue.sampledValue[sampledValuesIndex].measurand ??
461 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
462 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
463 meterValue.sampledValue[sampledValuesIndex].value
464 }/${maxPowerRounded}`
469 this.chargingStation
.getNumberOfPhases() === 3 &&
470 phase
<= this.chargingStation
.getNumberOfPhases();
473 const phaseValue
= `L${phase}-N`;
474 meterValue
.sampledValue
.push(
475 OCPP16ServiceUtils
.buildSampledValue(
476 (powerPerPhaseSampledValueTemplates
[`L${phase}`] as SampledValueTemplate
) ??
477 powerSampledValueTemplate
,
478 powerMeasurandValues
[`L${phase}`] as number,
480 phaseValue
as OCPP16MeterValuePhase
483 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
484 const maxPowerPerPhaseRounded
= Utils
.roundTo(maxPowerPerPhase
/ unitDivider
, 2);
486 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) >
487 maxPowerPerPhaseRounded
||
491 `${this.chargingStation.logPrefix()} MeterValues measurand ${
492 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
493 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
495 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
496 }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
497 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
498 }/${maxPowerPerPhaseRounded}`
503 // Current.Import measurand
504 const currentSampledValueTemplate
= this.chargingStation
.getSampledValueTemplate(
506 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
508 let currentPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
509 if (this.chargingStation
.getNumberOfPhases() === 3) {
510 currentPerPhaseSampledValueTemplates
= {
511 L1
: this.chargingStation
.getSampledValueTemplate(
513 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
514 OCPP16MeterValuePhase
.L1
516 L2
: this.chargingStation
.getSampledValueTemplate(
518 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
519 OCPP16MeterValuePhase
.L2
521 L3
: this.chargingStation
.getSampledValueTemplate(
523 OCPP16MeterValueMeasurand
.CURRENT_IMPORT
,
524 OCPP16MeterValuePhase
.L3
528 if (currentSampledValueTemplate
) {
529 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
530 this.chargingStation
,
531 currentSampledValueTemplate
.measurand
533 const errMsg
= `${this.chargingStation.logPrefix()} MeterValues measurand ${
534 currentSampledValueTemplate.measurand ??
535 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
536 }: Unknown ${this.chargingStation.getCurrentOutType()} currentOutType in template file ${
537 this.chargingStation.stationTemplateFile
538 }, cannot calculate ${
539 currentSampledValueTemplate.measurand ??
540 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
542 const currentMeasurandValues
: MeasurandValues
= {} as MeasurandValues
;
543 let maxAmperage
: number;
544 switch (this.chargingStation
.getCurrentOutType()) {
546 maxAmperage
= ACElectricUtils
.amperagePerPhaseFromPower(
547 this.chargingStation
.getNumberOfPhases(),
548 this.chargingStation
.stationInfo
.maxPower
/
549 this.chargingStation
.stationInfo
.powerDivider
,
550 this.chargingStation
.getVoltageOut()
552 if (this.chargingStation
.getNumberOfPhases() === 3) {
553 const defaultFluctuatedAmperagePerPhase
=
554 currentSampledValueTemplate
.value
&&
555 Utils
.getRandomFloatFluctuatedRounded(
556 parseInt(currentSampledValueTemplate
.value
),
557 currentSampledValueTemplate
.fluctuationPercent
??
558 Constants
.DEFAULT_FLUCTUATION_PERCENT
560 const phase1FluctuatedValue
=
561 currentPerPhaseSampledValueTemplates
?.L1
?.value
&&
562 Utils
.getRandomFloatFluctuatedRounded(
563 parseInt(currentPerPhaseSampledValueTemplates
.L1
.value
),
564 currentPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
??
565 Constants
.DEFAULT_FLUCTUATION_PERCENT
567 const phase2FluctuatedValue
=
568 currentPerPhaseSampledValueTemplates
?.L2
?.value
&&
569 Utils
.getRandomFloatFluctuatedRounded(
570 parseInt(currentPerPhaseSampledValueTemplates
.L2
.value
),
571 currentPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
??
572 Constants
.DEFAULT_FLUCTUATION_PERCENT
574 const phase3FluctuatedValue
=
575 currentPerPhaseSampledValueTemplates
?.L3
?.value
&&
576 Utils
.getRandomFloatFluctuatedRounded(
577 parseInt(currentPerPhaseSampledValueTemplates
.L3
.value
),
578 currentPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
??
579 Constants
.DEFAULT_FLUCTUATION_PERCENT
581 currentMeasurandValues
.L1
=
582 phase1FluctuatedValue
??
583 defaultFluctuatedAmperagePerPhase
??
584 Utils
.getRandomFloatRounded(maxAmperage
);
585 currentMeasurandValues
.L2
=
586 phase2FluctuatedValue
??
587 defaultFluctuatedAmperagePerPhase
??
588 Utils
.getRandomFloatRounded(maxAmperage
);
589 currentMeasurandValues
.L3
=
590 phase3FluctuatedValue
??
591 defaultFluctuatedAmperagePerPhase
??
592 Utils
.getRandomFloatRounded(maxAmperage
);
594 currentMeasurandValues
.L1
= currentSampledValueTemplate
.value
595 ? Utils
.getRandomFloatFluctuatedRounded(
596 parseInt(currentSampledValueTemplate
.value
),
597 currentSampledValueTemplate
.fluctuationPercent
??
598 Constants
.DEFAULT_FLUCTUATION_PERCENT
600 : Utils
.getRandomFloatRounded(maxAmperage
);
601 currentMeasurandValues
.L2
= 0;
602 currentMeasurandValues
.L3
= 0;
604 currentMeasurandValues
.allPhases
= Utils
.roundTo(
605 (currentMeasurandValues
.L1
+ currentMeasurandValues
.L2
+ currentMeasurandValues
.L3
) /
606 this.chargingStation
.getNumberOfPhases(),
611 maxAmperage
= DCElectricUtils
.amperage(
612 this.chargingStation
.stationInfo
.maxPower
/
613 this.chargingStation
.stationInfo
.powerDivider
,
614 this.chargingStation
.getVoltageOut()
616 currentMeasurandValues
.allPhases
= currentSampledValueTemplate
.value
617 ? Utils
.getRandomFloatFluctuatedRounded(
618 parseInt(currentSampledValueTemplate
.value
),
619 currentSampledValueTemplate
.fluctuationPercent
??
620 Constants
.DEFAULT_FLUCTUATION_PERCENT
622 : Utils
.getRandomFloatRounded(maxAmperage
);
625 logger
.error(errMsg
);
626 throw new OCPPError(ErrorType
.INTERNAL_ERROR
, errMsg
, OCPP16RequestCommand
.METER_VALUES
);
628 meterValue
.sampledValue
.push(
629 OCPP16ServiceUtils
.buildSampledValue(
630 currentSampledValueTemplate
,
631 currentMeasurandValues
.allPhases
634 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
636 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxAmperage
||
640 `${this.chargingStation.logPrefix()} MeterValues measurand ${
641 meterValue.sampledValue[sampledValuesIndex].measurand ??
642 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
643 }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
644 meterValue.sampledValue[sampledValuesIndex].value
650 this.chargingStation
.getNumberOfPhases() === 3 &&
651 phase
<= this.chargingStation
.getNumberOfPhases();
654 const phaseValue
= `L${phase}`;
655 meterValue
.sampledValue
.push(
656 OCPP16ServiceUtils
.buildSampledValue(
657 (currentPerPhaseSampledValueTemplates
[phaseValue
] as SampledValueTemplate
) ??
658 currentSampledValueTemplate
,
659 currentMeasurandValues
[phaseValue
] as number,
661 phaseValue
as OCPP16MeterValuePhase
664 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
666 Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) >
671 `${this.chargingStation.logPrefix()} MeterValues measurand ${
672 meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ??
673 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
675 meterValue.sampledValue[sampledValuesPerPhaseIndex].phase
676 }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${
677 meterValue.sampledValue[sampledValuesPerPhaseIndex].value
683 // Energy.Active.Import.Register measurand (default)
684 const energySampledValueTemplate
= this.chargingStation
.getSampledValueTemplate(connectorId
);
685 if (energySampledValueTemplate
) {
686 OCPP16ServiceUtils
.checkMeasurandPowerDivider(
687 this.chargingStation
,
688 energySampledValueTemplate
.measurand
691 energySampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
692 const maxEnergyRounded
= Utils
.roundTo(
693 ((this.chargingStation
.stationInfo
.maxPower
/
694 this.chargingStation
.stationInfo
.powerDivider
) *
699 const energyValueRounded
= energySampledValueTemplate
.value
700 ? // Cumulate the fluctuated value around the static one
701 Utils
.getRandomFloatFluctuatedRounded(
702 parseInt(energySampledValueTemplate
.value
),
703 energySampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
705 : Utils
.getRandomFloatRounded(maxEnergyRounded
);
706 // Persist previous value on connector
709 !Utils
.isNullOrUndefined(connector
.energyActiveImportRegisterValue
) &&
710 connector
.energyActiveImportRegisterValue
>= 0 &&
711 !Utils
.isNullOrUndefined(connector
.transactionEnergyActiveImportRegisterValue
) &&
712 connector
.transactionEnergyActiveImportRegisterValue
>= 0
714 connector
.energyActiveImportRegisterValue
+= energyValueRounded
;
715 connector
.transactionEnergyActiveImportRegisterValue
+= energyValueRounded
;
717 connector
.energyActiveImportRegisterValue
= 0;
718 connector
.transactionEnergyActiveImportRegisterValue
= 0;
720 meterValue
.sampledValue
.push(
721 OCPP16ServiceUtils
.buildSampledValue(
722 energySampledValueTemplate
,
724 this.chargingStation
.getEnergyActiveImportRegisterByTransactionId(transactionId
) /
730 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
731 if (energyValueRounded
> maxEnergyRounded
|| debug
) {
733 `${this.chargingStation.logPrefix()} MeterValues measurand ${
734 meterValue.sampledValue[sampledValuesIndex].measurand ??
735 OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
736 }: connectorId ${connectorId}, transaction ${
737 connector.transactionId
738 }, value: ${energyValueRounded}/${maxEnergyRounded}, duration: ${Utils.roundTo(
739 interval / (3600 * 1000),
745 const payload
: MeterValuesRequest
= {
748 meterValue
: [meterValue
],
750 await this.sendMessage(Utils
.generateUUID(), payload
, OCPP16RequestCommand
.METER_VALUES
);
753 public async sendTransactionBeginMeterValues(
755 transactionId
: number,
756 beginMeterValue
: OCPP16MeterValue
758 const payload
: MeterValuesRequest
= {
761 meterValue
: [beginMeterValue
],
763 await this.sendMessage(Utils
.generateUUID(), payload
, OCPP16RequestCommand
.METER_VALUES
);
766 public async sendTransactionEndMeterValues(
768 transactionId
: number,
769 endMeterValue
: OCPP16MeterValue
771 const payload
: MeterValuesRequest
= {
774 meterValue
: [endMeterValue
],
776 await this.sendMessage(Utils
.generateUUID(), payload
, OCPP16RequestCommand
.METER_VALUES
);
779 public async sendDiagnosticsStatusNotification(
780 diagnosticsStatus
: OCPP16DiagnosticsStatus
782 const payload
: DiagnosticsStatusNotificationRequest
= {
783 status: diagnosticsStatus
,
785 await this.sendMessage(
786 Utils
.generateUUID(),
788 OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION