1 import { ACElectricUtils
, DCElectricUtils
} from
'../../../utils/ElectricUtils';
2 import { AuthorizeRequest
, OCPP16AuthorizeResponse
, OCPP16StartTransactionResponse
, OCPP16StopTransactionReason
, OCPP16StopTransactionResponse
, StartTransactionRequest
, StopTransactionRequest
} from
'../../../types/ocpp/1.6/Transaction';
3 import { CurrentType
, Voltage
} from
'../../../types/ChargingStationTemplate';
4 import { DiagnosticsStatusNotificationRequest
, HeartbeatRequest
, OCPP16BootNotificationRequest
, OCPP16IncomingRequestCommand
, OCPP16RequestCommand
, StatusNotificationRequest
} from
'../../../types/ocpp/1.6/Requests';
5 import { MeterValueUnit
, MeterValuesRequest
, OCPP16MeterValue
, OCPP16MeterValueMeasurand
, OCPP16MeterValuePhase
} from
'../../../types/ocpp/1.6/MeterValues';
7 import Constants from
'../../../utils/Constants';
8 import MeasurandPerPhaseSampledValueTemplates from
'../../../types/MeasurandPerPhaseSampledValueTemplates';
9 import MeasurandValues from
'../../../types/MeasurandValues';
10 import { MessageType
} from
'../../../types/ocpp/MessageType';
11 import { OCPP16BootNotificationResponse
} from
'../../../types/ocpp/1.6/Responses';
12 import { OCPP16ChargePointErrorCode
} from
'../../../types/ocpp/1.6/ChargePointErrorCode';
13 import { OCPP16ChargePointStatus
} from
'../../../types/ocpp/1.6/ChargePointStatus';
14 import { OCPP16DiagnosticsStatus
} from
'../../../types/ocpp/1.6/DiagnosticsStatus';
15 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
16 import OCPPError from
'../../OcppError';
17 import OCPPRequestService from
'../OCPPRequestService';
18 import Utils from
'../../../utils/Utils';
19 import logger from
'../../../utils/Logger';
21 export default class OCPP16RequestService
extends OCPPRequestService
{
22 public async sendHeartbeat(): Promise
<void> {
24 const payload
: HeartbeatRequest
= {};
25 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.HEARTBEAT
);
27 this.handleRequestError(OCPP16RequestCommand
.HEARTBEAT
, error
);
31 public async sendBootNotification(chargePointModel
: string, chargePointVendor
: string, chargeBoxSerialNumber
?: string, firmwareVersion
?: string,
32 chargePointSerialNumber
?: string, iccid
?: string, imsi
?: string, meterSerialNumber
?: string, meterType
?: string): Promise
<OCPP16BootNotificationResponse
> {
34 const payload
: OCPP16BootNotificationRequest
= {
37 ...!Utils
.isUndefined(chargeBoxSerialNumber
) && { chargeBoxSerialNumber
},
38 ...!Utils
.isUndefined(chargePointSerialNumber
) && { chargePointSerialNumber
},
39 ...!Utils
.isUndefined(firmwareVersion
) && { firmwareVersion
},
40 ...!Utils
.isUndefined(iccid
) && { iccid
},
41 ...!Utils
.isUndefined(imsi
) && { imsi
},
42 ...!Utils
.isUndefined(meterSerialNumber
) && { meterSerialNumber
},
43 ...!Utils
.isUndefined(meterType
) && { meterType
}
45 return await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.BOOT_NOTIFICATION
) as OCPP16BootNotificationResponse
;
47 this.handleRequestError(OCPP16RequestCommand
.BOOT_NOTIFICATION
, error
);
51 public async sendStatusNotification(connectorId
: number, status: OCPP16ChargePointStatus
,
52 errorCode
: OCPP16ChargePointErrorCode
= OCPP16ChargePointErrorCode
.NO_ERROR
): Promise
<void> {
54 const payload
: StatusNotificationRequest
= {
59 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.STATUS_NOTIFICATION
);
61 this.handleRequestError(OCPP16RequestCommand
.STATUS_NOTIFICATION
, error
);
65 public async sendAuthorize(connectorId
: number, idTag
?: string): Promise
<OCPP16AuthorizeResponse
> {
67 const payload
: AuthorizeRequest
= {
68 ...!Utils
.isUndefined(idTag
) ? { idTag
} : { idTag
: Constants
.TRANSACTION_DEFAULT_IDTAG
},
70 this.chargingStation
.getConnector(connectorId
).authorizeIdTag
= idTag
;
71 return await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.AUTHORIZE
) as OCPP16AuthorizeResponse
;
73 this.handleRequestError(OCPP16RequestCommand
.AUTHORIZE
, error
);
77 public async sendStartTransaction(connectorId
: number, idTag
?: string): Promise
<OCPP16StartTransactionResponse
> {
79 const payload
: StartTransactionRequest
= {
81 ...!Utils
.isUndefined(idTag
) ? { idTag
} : { idTag
: Constants
.TRANSACTION_DEFAULT_IDTAG
},
82 meterStart
: this.chargingStation
.getEnergyActiveImportRegisterByConnectorId(connectorId
),
83 timestamp
: new Date().toISOString(),
85 return await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.START_TRANSACTION
) as OCPP16StartTransactionResponse
;
87 this.handleRequestError(OCPP16RequestCommand
.START_TRANSACTION
, error
);
91 public async sendStopTransaction(transactionId
: number, meterStop
: number, idTag
?: string,
92 reason
: OCPP16StopTransactionReason
= OCPP16StopTransactionReason
.NONE
): Promise
<OCPP16StopTransactionResponse
> {
94 let connectorId
: number;
95 for (const connector
in this.chargingStation
.connectors
) {
96 if (Utils
.convertToInt(connector
) > 0 && this.chargingStation
.getConnector(Utils
.convertToInt(connector
))?.transactionId
=== transactionId
) {
97 connectorId
= Utils
.convertToInt(connector
);
101 const transactionEndMeterValue
= OCPP16ServiceUtils
.buildTransactionEndMeterValue(this.chargingStation
, connectorId
, meterStop
);
102 // FIXME: should be a callback, each OCPP commands implementation must do only one job
103 (this.chargingStation
.getBeginEndMeterValues() && !this.chargingStation
.getOutOfOrderEndMeterValues())
104 && await this.sendTransactionEndMeterValues(connectorId
, transactionId
, transactionEndMeterValue
);
105 const payload
: StopTransactionRequest
= {
107 ...!Utils
.isUndefined(idTag
) && { idTag
},
109 timestamp
: new Date().toISOString(),
110 ...reason
&& { reason
},
111 ...this.chargingStation
.getTransactionDataMeterValues() && { transactionData
: OCPP16ServiceUtils
.buildTransactionDataMeterValues(this.chargingStation
.getConnector(connectorId
).transactionBeginMeterValue
, transactionEndMeterValue
) },
113 return await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.STOP_TRANSACTION
) as OCPP16StartTransactionResponse
;
115 this.handleRequestError(OCPP16RequestCommand
.STOP_TRANSACTION
, error
);
119 // eslint-disable-next-line consistent-this
120 public async sendMeterValues(connectorId
: number, transactionId
: number, interval
: number, self: OCPPRequestService
, debug
= false): Promise
<void> {
122 const meterValue
: OCPP16MeterValue
= {
123 timestamp
: new Date().toISOString(),
126 const connector
= self.chargingStation
.getConnector(connectorId
);
128 const socSampledValueTemplate
= self.chargingStation
.getSampledValueTemplate(connectorId
, OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
);
129 if (socSampledValueTemplate
) {
130 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(socSampledValueTemplate
, Utils
.getRandomInt(100)));
131 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
132 if (Utils
.convertToInt(meterValue
.sampledValue
[sampledValuesIndex
].value
) > 100 || debug
) {
133 logger
.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/100`);
137 const voltageSampledValueTemplate
= self.chargingStation
.getSampledValueTemplate(connectorId
, OCPP16MeterValueMeasurand
.VOLTAGE
);
138 if (voltageSampledValueTemplate
) {
139 const voltageSampledValueTemplateValue
= voltageSampledValueTemplate
.value
? parseInt(voltageSampledValueTemplate
.value
) : self.chargingStation
.getVoltageOut();
140 const fluctuationPercent
= voltageSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
;
141 const voltageMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(voltageSampledValueTemplateValue
, fluctuationPercent
);
142 if (self.chargingStation
.getNumberOfPhases() !== 3 || (self.chargingStation
.getNumberOfPhases() === 3 && self.chargingStation
.getMainVoltageMeterValues())) {
143 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(voltageSampledValueTemplate
, voltageMeasurandValue
));
145 for (let phase
= 1; self.chargingStation
.getNumberOfPhases() === 3 && phase
<= self.chargingStation
.getNumberOfPhases(); phase
++) {
146 const phaseLineToNeutralValue
= `L${phase}-N`;
147 const voltagePhaseLineToNeutralSampledValueTemplate
= self.chargingStation
.getSampledValueTemplate(connectorId
, OCPP16MeterValueMeasurand
.VOLTAGE
,
148 phaseLineToNeutralValue
as OCPP16MeterValuePhase
);
149 let voltagePhaseLineToNeutralMeasurandValue
: number;
150 if (voltagePhaseLineToNeutralSampledValueTemplate
) {
151 const voltagePhaseLineToNeutralSampledValueTemplateValue
= voltagePhaseLineToNeutralSampledValueTemplate
.value
? parseInt(voltagePhaseLineToNeutralSampledValueTemplate
.value
) : self.chargingStation
.getVoltageOut();
152 const fluctuationPhaseToNeutralPercent
= voltagePhaseLineToNeutralSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
;
153 voltagePhaseLineToNeutralMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(voltagePhaseLineToNeutralSampledValueTemplateValue
, fluctuationPhaseToNeutralPercent
);
155 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(voltagePhaseLineToNeutralSampledValueTemplate
?? voltageSampledValueTemplate
,
156 voltagePhaseLineToNeutralMeasurandValue
?? voltageMeasurandValue
, null, phaseLineToNeutralValue
as OCPP16MeterValuePhase
));
157 if (self.chargingStation
.getPhaseLineToLineVoltageMeterValues()) {
158 const phaseLineToLineValue
= `L${phase}-L${(phase + 1) % self.chargingStation.getNumberOfPhases() !== 0 ? (phase + 1) % self.chargingStation.getNumberOfPhases() : self.chargingStation.getNumberOfPhases()}`;
159 const voltagePhaseLineToLineSampledValueTemplate
= self.chargingStation
.getSampledValueTemplate(connectorId
, OCPP16MeterValueMeasurand
.VOLTAGE
, phaseLineToLineValue
as OCPP16MeterValuePhase
);
160 let voltagePhaseLineToLineMeasurandValue
: number;
161 if (voltagePhaseLineToLineSampledValueTemplate
) {
162 const voltagePhaseLineToLineSampledValueTemplateValue
= voltagePhaseLineToLineSampledValueTemplate
.value
? parseInt(voltagePhaseLineToLineSampledValueTemplate
.value
) : Voltage
.VOLTAGE_400
;
163 const fluctuationPhaseLineToLinePercent
= voltagePhaseLineToLineSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
;
164 voltagePhaseLineToLineMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(voltagePhaseLineToLineSampledValueTemplateValue
, fluctuationPhaseLineToLinePercent
);
166 const defaultVoltagePhaseLineToLineMeasurandValue
= Utils
.getRandomFloatFluctuatedRounded(Voltage
.VOLTAGE_400
, fluctuationPercent
);
167 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(voltagePhaseLineToLineSampledValueTemplate
?? voltageSampledValueTemplate
,
168 voltagePhaseLineToLineMeasurandValue
?? defaultVoltagePhaseLineToLineMeasurandValue
, null, phaseLineToLineValue
as OCPP16MeterValuePhase
));
172 // Power.Active.Import measurand
173 const powerSampledValueTemplate
= self.chargingStation
.getSampledValueTemplate(connectorId
, OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
);
174 let powerPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
175 if (self.chargingStation
.getNumberOfPhases() === 3) {
176 powerPerPhaseSampledValueTemplates
= {
177 L1
: self.chargingStation
.getSampledValueTemplate(connectorId
, OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
, OCPP16MeterValuePhase
.L1_N
),
178 L2
: self.chargingStation
.getSampledValueTemplate(connectorId
, OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
, OCPP16MeterValuePhase
.L2_N
),
179 L3
: self.chargingStation
.getSampledValueTemplate(connectorId
, OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
, OCPP16MeterValuePhase
.L3_N
),
182 if (powerSampledValueTemplate
) {
183 OCPP16ServiceUtils
.checkMeasurandPowerDivider(self.chargingStation
, powerSampledValueTemplate
.measurand
);
184 const errMsg
= `${self.chargingStation.logPrefix()} MeterValues measurand ${powerSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${self.chargingStation.getCurrentOutType()} currentOutType in template file ${self.chargingStation.stationTemplateFile}, cannot calculate ${powerSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`;
185 const powerMeasurandValues
= {} as MeasurandValues
;
186 const unitDivider
= powerSampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT
? 1000 : 1;
187 const maxPower
= Math.round(self.chargingStation
.stationInfo
.maxPower
/ self.chargingStation
.stationInfo
.powerDivider
);
188 const maxPowerPerPhase
= Math.round((self.chargingStation
.stationInfo
.maxPower
/ self.chargingStation
.stationInfo
.powerDivider
) / self.chargingStation
.getNumberOfPhases());
189 switch (self.chargingStation
.getCurrentOutType()) {
191 if (self.chargingStation
.getNumberOfPhases() === 3) {
192 const defaultFluctuatedPowerPerPhase
= powerSampledValueTemplate
.value
193 && Utils
.getRandomFloatFluctuatedRounded(parseInt(powerSampledValueTemplate
.value
) / self.chargingStation
.getNumberOfPhases(), powerSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
);
194 const phase1FluctuatedValue
= powerPerPhaseSampledValueTemplates
?.L1
?.value
195 && Utils
.getRandomFloatFluctuatedRounded(parseInt(powerPerPhaseSampledValueTemplates
.L1
.value
), powerPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
);
196 const phase2FluctuatedValue
= powerPerPhaseSampledValueTemplates
?.L2
?.value
197 && Utils
.getRandomFloatFluctuatedRounded(parseInt(powerPerPhaseSampledValueTemplates
.L2
.value
), powerPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
);
198 const phase3FluctuatedValue
= powerPerPhaseSampledValueTemplates
?.L3
?.value
199 && Utils
.getRandomFloatFluctuatedRounded(parseInt(powerPerPhaseSampledValueTemplates
.L3
.value
), powerPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
);
200 powerMeasurandValues
.L1
= (phase1FluctuatedValue
?? defaultFluctuatedPowerPerPhase
) ?? Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
201 powerMeasurandValues
.L2
= (phase2FluctuatedValue
?? defaultFluctuatedPowerPerPhase
) ?? Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
202 powerMeasurandValues
.L3
= (phase3FluctuatedValue
?? defaultFluctuatedPowerPerPhase
) ?? Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
204 powerMeasurandValues
.L1
= powerSampledValueTemplate
.value
205 ? Utils
.getRandomFloatFluctuatedRounded(parseInt(powerSampledValueTemplate
.value
), powerSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
)
206 : Utils
.getRandomFloatRounded(maxPower
/ unitDivider
);
207 powerMeasurandValues
.L2
= 0;
208 powerMeasurandValues
.L3
= 0;
210 powerMeasurandValues
.allPhases
= Utils
.roundTo(powerMeasurandValues
.L1
+ powerMeasurandValues
.L2
+ powerMeasurandValues
.L3
, 2);
213 powerMeasurandValues
.allPhases
= powerSampledValueTemplate
.value
214 ? Utils
.getRandomFloatFluctuatedRounded(parseInt(powerSampledValueTemplate
.value
), powerSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
)
215 : Utils
.getRandomFloatRounded(maxPower
/ unitDivider
);
218 logger
.error(errMsg
);
221 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(powerSampledValueTemplate
, powerMeasurandValues
.allPhases
));
222 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
223 const maxPowerRounded
= Utils
.roundTo(maxPower
/ unitDivider
, 2);
224 if (Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxPowerRounded
|| debug
) {
225 logger
.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxPowerRounded}`);
227 for (let phase
= 1; self.chargingStation
.getNumberOfPhases() === 3 && phase
<= self.chargingStation
.getNumberOfPhases(); phase
++) {
228 const phaseValue
= `L${phase}-N`;
229 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(powerPerPhaseSampledValueTemplates
[`L${phase}`] ?? powerSampledValueTemplate
, powerMeasurandValues
[`L${phase}`], null,
230 phaseValue
as OCPP16MeterValuePhase
));
231 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
232 const maxPowerPerPhaseRounded
= Utils
.roundTo(maxPowerPerPhase
/ unitDivider
, 2);
233 if (Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) > maxPowerPerPhaseRounded
|| debug
) {
234 logger
.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: phase ${meterValue.sampledValue[sampledValuesPerPhaseIndex].phase}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesPerPhaseIndex].value}/${maxPowerPerPhaseRounded}`);
238 // Current.Import measurand
239 const currentSampledValueTemplate
= self.chargingStation
.getSampledValueTemplate(connectorId
, OCPP16MeterValueMeasurand
.CURRENT_IMPORT
);
240 let currentPerPhaseSampledValueTemplates
: MeasurandPerPhaseSampledValueTemplates
= {};
241 if (self.chargingStation
.getNumberOfPhases() === 3) {
242 currentPerPhaseSampledValueTemplates
= {
243 L1
: self.chargingStation
.getSampledValueTemplate(connectorId
, OCPP16MeterValueMeasurand
.CURRENT_IMPORT
, OCPP16MeterValuePhase
.L1
),
244 L2
: self.chargingStation
.getSampledValueTemplate(connectorId
, OCPP16MeterValueMeasurand
.CURRENT_IMPORT
, OCPP16MeterValuePhase
.L2
),
245 L3
: self.chargingStation
.getSampledValueTemplate(connectorId
, OCPP16MeterValueMeasurand
.CURRENT_IMPORT
, OCPP16MeterValuePhase
.L3
),
248 if (currentSampledValueTemplate
) {
249 OCPP16ServiceUtils
.checkMeasurandPowerDivider(self.chargingStation
, currentSampledValueTemplate
.measurand
);
250 const errMsg
= `${self.chargingStation.logPrefix()} MeterValues measurand ${currentSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${self.chargingStation.getCurrentOutType()} currentOutType in template file ${self.chargingStation.stationTemplateFile}, cannot calculate ${currentSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`;
251 const currentMeasurandValues
: MeasurandValues
= {} as MeasurandValues
;
252 let maxAmperage
: number;
253 switch (self.chargingStation
.getCurrentOutType()) {
255 maxAmperage
= ACElectricUtils
.amperagePerPhaseFromPower(self.chargingStation
.getNumberOfPhases(), self.chargingStation
.stationInfo
.maxPower
/ self.chargingStation
.stationInfo
.powerDivider
, self.chargingStation
.getVoltageOut());
256 if (self.chargingStation
.getNumberOfPhases() === 3) {
257 const defaultFluctuatedAmperagePerPhase
= currentSampledValueTemplate
.value
258 && Utils
.getRandomFloatFluctuatedRounded(parseInt(currentSampledValueTemplate
.value
), currentSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
);
259 const phase1FluctuatedValue
= currentPerPhaseSampledValueTemplates
?.L1
?.value
260 && Utils
.getRandomFloatFluctuatedRounded(parseInt(currentPerPhaseSampledValueTemplates
.L1
.value
), currentPerPhaseSampledValueTemplates
.L1
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
);
261 const phase2FluctuatedValue
= currentPerPhaseSampledValueTemplates
?.L2
?.value
262 && Utils
.getRandomFloatFluctuatedRounded(parseInt(currentPerPhaseSampledValueTemplates
.L2
.value
), currentPerPhaseSampledValueTemplates
.L2
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
);
263 const phase3FluctuatedValue
= currentPerPhaseSampledValueTemplates
?.L3
?.value
264 && Utils
.getRandomFloatFluctuatedRounded(parseInt(currentPerPhaseSampledValueTemplates
.L3
.value
), currentPerPhaseSampledValueTemplates
.L3
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
);
265 currentMeasurandValues
.L1
= (phase1FluctuatedValue
?? defaultFluctuatedAmperagePerPhase
) ?? Utils
.getRandomFloatRounded(maxAmperage
);
266 currentMeasurandValues
.L2
= (phase2FluctuatedValue
?? defaultFluctuatedAmperagePerPhase
) ?? Utils
.getRandomFloatRounded(maxAmperage
);
267 currentMeasurandValues
.L3
= (phase3FluctuatedValue
?? defaultFluctuatedAmperagePerPhase
) ?? Utils
.getRandomFloatRounded(maxAmperage
);
269 currentMeasurandValues
.L1
= currentSampledValueTemplate
.value
270 ? Utils
.getRandomFloatFluctuatedRounded(parseInt(currentSampledValueTemplate
.value
), currentSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
)
271 : Utils
.getRandomFloatRounded(maxAmperage
);
272 currentMeasurandValues
.L2
= 0;
273 currentMeasurandValues
.L3
= 0;
275 currentMeasurandValues
.allPhases
= Utils
.roundTo((currentMeasurandValues
.L1
+ currentMeasurandValues
.L2
+ currentMeasurandValues
.L3
) / self.chargingStation
.getNumberOfPhases(), 2);
278 maxAmperage
= DCElectricUtils
.amperage(self.chargingStation
.stationInfo
.maxPower
/ self.chargingStation
.stationInfo
.powerDivider
, self.chargingStation
.getVoltageOut());
279 currentMeasurandValues
.allPhases
= currentSampledValueTemplate
.value
280 ? Utils
.getRandomFloatFluctuatedRounded(parseInt(currentSampledValueTemplate
.value
), currentSampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
)
281 : Utils
.getRandomFloatRounded(maxAmperage
);
284 logger
.error(errMsg
);
287 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(currentSampledValueTemplate
, currentMeasurandValues
.allPhases
));
288 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
289 if (Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxAmperage
|| debug
) {
290 logger
.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxAmperage}`);
292 for (let phase
= 1; self.chargingStation
.getNumberOfPhases() === 3 && phase
<= self.chargingStation
.getNumberOfPhases(); phase
++) {
293 const phaseValue
= `L${phase}`;
294 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(currentPerPhaseSampledValueTemplates
[phaseValue
] ?? currentSampledValueTemplate
,
295 currentMeasurandValues
[phaseValue
], null, phaseValue
as OCPP16MeterValuePhase
));
296 const sampledValuesPerPhaseIndex
= meterValue
.sampledValue
.length
- 1;
297 if (Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesPerPhaseIndex
].value
) > maxAmperage
|| debug
) {
298 logger
.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: phase ${meterValue.sampledValue[sampledValuesPerPhaseIndex].phase}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesPerPhaseIndex].value}/${maxAmperage}`);
302 // Energy.Active.Import.Register measurand (default)
303 const energySampledValueTemplate
= self.chargingStation
.getSampledValueTemplate(connectorId
);
304 if (energySampledValueTemplate
) {
305 OCPP16ServiceUtils
.checkMeasurandPowerDivider(self.chargingStation
, energySampledValueTemplate
.measurand
);
306 const unitDivider
= energySampledValueTemplate
?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
307 const energyMeasurandValue
= energySampledValueTemplate
.value
308 // Cumulate the fluctuated value around the static one
309 ? Utils
.getRandomFloatFluctuatedRounded(parseInt(energySampledValueTemplate
.value
), energySampledValueTemplate
.fluctuationPercent
?? Constants
.DEFAULT_FLUCTUATION_PERCENT
)
310 : Utils
.getRandomInt(self.chargingStation
.stationInfo
.maxPower
/ (self.chargingStation
.stationInfo
.powerDivider
* 3600000) * interval
);
311 // Persist previous value on connector
312 if (connector
&& !Utils
.isNullOrUndefined(connector
.energyActiveImportRegisterValue
) && connector
.energyActiveImportRegisterValue
>= 0 &&
313 !Utils
.isNullOrUndefined(connector
.transactionEnergyActiveImportRegisterValue
) && connector
.transactionEnergyActiveImportRegisterValue
>= 0) {
314 connector
.energyActiveImportRegisterValue
+= energyMeasurandValue
;
315 connector
.transactionEnergyActiveImportRegisterValue
+= energyMeasurandValue
;
317 connector
.energyActiveImportRegisterValue
= 0;
318 connector
.transactionEnergyActiveImportRegisterValue
= 0;
320 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(energySampledValueTemplate
,
321 Utils
.roundTo(self.chargingStation
.getEnergyActiveImportRegisterByTransactionId(transactionId
) / unitDivider
, 4)));
322 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
323 const maxEnergy
= Math.round(self.chargingStation
.stationInfo
.maxPower
* 3600 / (self.chargingStation
.stationInfo
.powerDivider
* interval
));
324 const maxEnergyRounded
= Utils
.roundTo(maxEnergy
/ unitDivider
, 4);
325 if (Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxEnergyRounded
|| debug
) {
326 logger
.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxEnergyRounded}`);
329 const payload
: MeterValuesRequest
= {
334 await self.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.METER_VALUES
);
336 self.handleRequestError(OCPP16RequestCommand
.METER_VALUES
, error
);
340 public async sendTransactionBeginMeterValues(connectorId
: number, transactionId
: number, beginMeterValue
: OCPP16MeterValue
): Promise
<void> {
341 const payload
: MeterValuesRequest
= {
344 meterValue
: beginMeterValue
,
346 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.METER_VALUES
);
349 public async sendTransactionEndMeterValues(connectorId
: number, transactionId
: number, endMeterValue
: OCPP16MeterValue
): Promise
<void> {
350 const payload
: MeterValuesRequest
= {
353 meterValue
: endMeterValue
,
355 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.METER_VALUES
);
358 public async sendDiagnosticsStatusNotification(diagnosticsStatus
: OCPP16DiagnosticsStatus
): Promise
<void> {
359 const payload
: DiagnosticsStatusNotificationRequest
= {
360 status: diagnosticsStatus
362 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
);
365 public async sendError(messageId
: string, error
: OCPPError
, commandName
: OCPP16RequestCommand
| OCPP16IncomingRequestCommand
): Promise
<unknown
> {
367 return this.sendMessage(messageId
, error
, MessageType
.CALL_ERROR_MESSAGE
, commandName
);