1 import { ACElectricUtils
, DCElectricUtils
} from
'../../../utils/ElectricUtils';
2 import { AuthorizeRequest
, OCPP16AuthorizeResponse
, OCPP16StartTransactionResponse
, OCPP16StopTransactionReason
, OCPP16StopTransactionResponse
, StartTransactionRequest
, StopTransactionRequest
} from
'../../../types/ocpp/1.6/Transaction';
3 import { HeartbeatRequest
, OCPP16BootNotificationRequest
, OCPP16IncomingRequestCommand
, OCPP16RequestCommand
, StatusNotificationRequest
} from
'../../../types/ocpp/1.6/Requests';
4 import { MeterValue
, MeterValueContext
, MeterValuePhase
, MeterValueUnit
, MeterValuesRequest
, OCPP16MeterValueMeasurand
, OCPP16SampledValue
} from
'../../../types/ocpp/1.6/MeterValues';
6 import Constants from
'../../../utils/Constants';
7 import { CurrentOutType
} from
'../../../types/ChargingStationTemplate';
8 import MeasurandValues from
'../../../types/MeasurandValues';
9 import { MessageType
} from
'../../../types/ocpp/MessageType';
10 import { OCPP16BootNotificationResponse
} from
'../../../types/ocpp/1.6/Responses';
11 import { OCPP16ChargePointErrorCode
} from
'../../../types/ocpp/1.6/ChargePointErrorCode';
12 import { OCPP16ChargePointStatus
} from
'../../../types/ocpp/1.6/ChargePointStatus';
13 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
14 import { OCPP16StandardParametersKey
} from
'../../../types/ocpp/1.6/Configuration';
15 import OCPPError from
'../../OcppError';
16 import OCPPRequestService from
'../OCPPRequestService';
17 import Utils from
'../../../utils/Utils';
18 import logger from
'../../../utils/Logger';
20 export default class OCPP16RequestService
extends OCPPRequestService
{
21 public async sendHeartbeat(): Promise
<void> {
23 const payload
: HeartbeatRequest
= {};
24 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.HEARTBEAT
);
26 this.handleRequestError(OCPP16RequestCommand
.HEARTBEAT
, error
);
30 public async sendBootNotification(chargePointModel
: string, chargePointVendor
: string, chargeBoxSerialNumber
?: string, firmwareVersion
?: string, chargePointSerialNumber
?: string, iccid
?: string, imsi
?: string, meterSerialNumber
?: string, meterType
?: string): Promise
<OCPP16BootNotificationResponse
> {
32 const payload
: OCPP16BootNotificationRequest
= {
35 ...!Utils
.isUndefined(chargeBoxSerialNumber
) && { chargeBoxSerialNumber
},
36 ...!Utils
.isUndefined(chargePointSerialNumber
) && { chargePointSerialNumber
},
37 ...!Utils
.isUndefined(firmwareVersion
) && { firmwareVersion
},
38 ...!Utils
.isUndefined(iccid
) && { iccid
},
39 ...!Utils
.isUndefined(imsi
) && { imsi
},
40 ...!Utils
.isUndefined(meterSerialNumber
) && { meterSerialNumber
},
41 ...!Utils
.isUndefined(meterType
) && { meterType
}
43 return await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.BOOT_NOTIFICATION
) as OCPP16BootNotificationResponse
;
45 this.handleRequestError(OCPP16RequestCommand
.BOOT_NOTIFICATION
, error
);
49 public async sendStatusNotification(connectorId
: number, status: OCPP16ChargePointStatus
,
50 errorCode
: OCPP16ChargePointErrorCode
= OCPP16ChargePointErrorCode
.NO_ERROR
): Promise
<void> {
52 const payload
: StatusNotificationRequest
= {
57 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.STATUS_NOTIFICATION
);
59 this.handleRequestError(OCPP16RequestCommand
.STATUS_NOTIFICATION
, error
);
63 public async sendAuthorize(idTag
?: string): Promise
<OCPP16AuthorizeResponse
> {
65 const payload
: AuthorizeRequest
= {
66 ...!Utils
.isUndefined(idTag
) ? { idTag
} : { idTag
: Constants
.TRANSACTION_DEFAULT_IDTAG
},
68 return await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.AUTHORIZE
) as OCPP16AuthorizeResponse
;
70 this.handleRequestError(OCPP16RequestCommand
.AUTHORIZE
, error
);
74 public async sendStartTransaction(connectorId
: number, idTag
?: string): Promise
<OCPP16StartTransactionResponse
> {
76 const payload
: StartTransactionRequest
= {
78 ...!Utils
.isUndefined(idTag
) ? { idTag
} : { idTag
: Constants
.TRANSACTION_DEFAULT_IDTAG
},
79 meterStart
: this.chargingStation
.getEnergyActiveImportRegisterByConnectorId(connectorId
),
80 timestamp
: new Date().toISOString(),
82 return await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.START_TRANSACTION
) as OCPP16StartTransactionResponse
;
84 this.handleRequestError(OCPP16RequestCommand
.START_TRANSACTION
, error
);
88 public async sendStopTransaction(transactionId
: number, meterStop
: number, idTag
?: string,
89 reason
: OCPP16StopTransactionReason
= OCPP16StopTransactionReason
.NONE
): Promise
<OCPP16StopTransactionResponse
> {
91 const payload
: StopTransactionRequest
= {
93 ...!Utils
.isUndefined(idTag
) && { idTag
},
95 timestamp
: new Date().toISOString(),
96 ...reason
&& { reason
},
98 let connectorId
: number;
99 for (const connector
in this.chargingStation
.connectors
) {
100 if (Utils
.convertToInt(connector
) > 0 && this.chargingStation
.getConnector(Utils
.convertToInt(connector
))?.transactionId
=== transactionId
) {
101 connectorId
= Utils
.convertToInt(connector
);
104 (this.chargingStation
.getBeginEndMeterValues() && !this.chargingStation
.getOutOfOrderEndMeterValues())
105 && await this.sendTransactionEndMeterValues(connectorId
, transactionId
, meterStop
);
106 return await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.STOP_TRANSACTION
) as OCPP16StartTransactionResponse
;
108 this.handleRequestError(OCPP16RequestCommand
.STOP_TRANSACTION
, error
);
112 // eslint-disable-next-line consistent-this
113 public async sendMeterValues(connectorId
: number, transactionId
: number, interval
: number, self: OCPPRequestService
, debug
= false): Promise
<void> {
115 const meterValue
: MeterValue
= {
116 timestamp
: new Date().toISOString(),
119 const meterValuesTemplate
: OCPP16SampledValue
[] = self.chargingStation
.getConnector(connectorId
).MeterValues
;
120 for (let index
= 0; index
< meterValuesTemplate
.length
; index
++) {
121 const connector
= self.chargingStation
.getConnector(connectorId
);
123 if (meterValuesTemplate
[index
].measurand
&& meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
&& self.chargingStation
.getConfigurationKey(OCPP16StandardParametersKey
.MeterValuesSampledData
).value
.includes(OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
)) {
124 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(meterValuesTemplate
[index
], Utils
.getRandomInt(100)));
125 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
126 if (Utils
.convertToInt(meterValue
.sampledValue
[sampledValuesIndex
].value
) > 100 || debug
) {
127 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`);
130 } else if (meterValuesTemplate
[index
].measurand
&& meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.VOLTAGE
&& self.chargingStation
.getConfigurationKey(OCPP16StandardParametersKey
.MeterValuesSampledData
).value
.includes(OCPP16MeterValueMeasurand
.VOLTAGE
)) {
131 const voltageMeasurandValue
= Utils
.getRandomFloatRounded(self.chargingStation
.getVoltageOut() + self.chargingStation
.getVoltageOut() * 0.1, self.chargingStation
.getVoltageOut() - self.chargingStation
.getVoltageOut() * 0.1);
132 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(meterValuesTemplate
[index
], voltageMeasurandValue
));
133 for (let phase
= 1; self.chargingStation
.getNumberOfPhases() === 3 && phase
<= self.chargingStation
.getNumberOfPhases(); phase
++) {
134 let phaseValue
: string;
135 if (self.chargingStation
.getVoltageOut() >= 0 && self.chargingStation
.getVoltageOut() <= 250) {
136 phaseValue
= `L${phase}-N`;
137 } else if (self.chargingStation
.getVoltageOut() > 250) {
138 phaseValue
= `L${phase}-L${(phase + 1) % self.chargingStation.getNumberOfPhases() !== 0 ? (phase + 1) % self.chargingStation.getNumberOfPhases() : self.chargingStation.getNumberOfPhases()}`;
140 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(meterValuesTemplate
[index
], voltageMeasurandValue
, null,
141 phaseValue
as MeterValuePhase
));
143 // Power.Active.Import measurand
144 } else if (meterValuesTemplate
[index
].measurand
&& meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
&& self.chargingStation
.getConfigurationKey(OCPP16StandardParametersKey
.MeterValuesSampledData
).value
.includes(OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
)) {
145 OCPP16ServiceUtils
.checkMeasurandPowerDivider(self.chargingStation
, meterValuesTemplate
[index
].measurand
);
146 const errMsg
= `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${self.chargingStation.getCurrentOutType()} currentOutType in template file ${self.chargingStation.stationTemplateFile}, cannot calculate ${meterValuesTemplate[index].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`;
147 const powerMeasurandValues
= {} as MeasurandValues
;
148 const unitDivider
= meterValuesTemplate
[index
]?.unit
=== MeterValueUnit
.KILO_WATT
? 1000 : 1;
149 const maxPower
= Math.round(self.chargingStation
.stationInfo
.maxPower
/ self.chargingStation
.stationInfo
.powerDivider
);
150 const maxPowerPerPhase
= Math.round((self.chargingStation
.stationInfo
.maxPower
/ self.chargingStation
.stationInfo
.powerDivider
) / self.chargingStation
.getNumberOfPhases());
151 switch (self.chargingStation
.getCurrentOutType()) {
152 case CurrentOutType
.AC
:
153 if (Utils
.isUndefined(meterValuesTemplate
[index
].value
)) {
154 powerMeasurandValues
.L1
= Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
155 powerMeasurandValues
.L2
= 0;
156 powerMeasurandValues
.L3
= 0;
157 if (self.chargingStation
.getNumberOfPhases() === 3) {
158 powerMeasurandValues
.L2
= Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
159 powerMeasurandValues
.L3
= Utils
.getRandomFloatRounded(maxPowerPerPhase
/ unitDivider
);
161 powerMeasurandValues
.allPhases
= Utils
.roundTo(powerMeasurandValues
.L1
+ powerMeasurandValues
.L2
+ powerMeasurandValues
.L3
, 2);
164 case CurrentOutType
.DC
:
165 if (Utils
.isUndefined(meterValuesTemplate
[index
].value
)) {
166 powerMeasurandValues
.allPhases
= Utils
.getRandomFloatRounded(maxPower
/ unitDivider
);
170 logger
.error(errMsg
);
173 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(meterValuesTemplate
[index
], powerMeasurandValues
.allPhases
));
174 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
175 const maxPowerRounded
= Utils
.roundTo(maxPower
/ unitDivider
, 2);
176 if (Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxPowerRounded
|| debug
) {
177 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}`);
179 for (let phase
= 1; self.chargingStation
.getNumberOfPhases() === 3 && phase
<= self.chargingStation
.getNumberOfPhases(); phase
++) {
180 const phaseValue
= `L${phase}-N`;
181 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(meterValuesTemplate
[index
], powerMeasurandValues
[`L${phase}`], null,
182 phaseValue
as MeterValuePhase
));
184 // Current.Import measurand
185 } else if (meterValuesTemplate
[index
].measurand
&& meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.CURRENT_IMPORT
&& self.chargingStation
.getConfigurationKey(OCPP16StandardParametersKey
.MeterValuesSampledData
).value
.includes(OCPP16MeterValueMeasurand
.CURRENT_IMPORT
)) {
186 OCPP16ServiceUtils
.checkMeasurandPowerDivider(self.chargingStation
, meterValuesTemplate
[index
].measurand
);
187 const errMsg
= `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${self.chargingStation.getCurrentOutType()} currentOutType in template file ${self.chargingStation.stationTemplateFile}, cannot calculate ${meterValuesTemplate[index].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`;
188 const currentMeasurandValues
: MeasurandValues
= {} as MeasurandValues
;
189 let maxAmperage
: number;
190 switch (self.chargingStation
.getCurrentOutType()) {
191 case CurrentOutType
.AC
:
192 maxAmperage
= ACElectricUtils
.amperagePerPhaseFromPower(self.chargingStation
.getNumberOfPhases(), self.chargingStation
.stationInfo
.maxPower
/ self.chargingStation
.stationInfo
.powerDivider
, self.chargingStation
.getVoltageOut());
193 if (Utils
.isUndefined(meterValuesTemplate
[index
].value
)) {
194 currentMeasurandValues
.L1
= Utils
.getRandomFloatRounded(maxAmperage
);
195 currentMeasurandValues
.L2
= 0;
196 currentMeasurandValues
.L3
= 0;
197 if (self.chargingStation
.getNumberOfPhases() === 3) {
198 currentMeasurandValues
.L2
= Utils
.getRandomFloatRounded(maxAmperage
);
199 currentMeasurandValues
.L3
= Utils
.getRandomFloatRounded(maxAmperage
);
201 currentMeasurandValues
.allPhases
= Utils
.roundTo((currentMeasurandValues
.L1
+ currentMeasurandValues
.L2
+ currentMeasurandValues
.L3
) / self.chargingStation
.getNumberOfPhases(), 2);
204 case CurrentOutType
.DC
:
205 maxAmperage
= DCElectricUtils
.amperage(self.chargingStation
.stationInfo
.maxPower
/ self.chargingStation
.stationInfo
.powerDivider
, self.chargingStation
.getVoltageOut());
206 if (Utils
.isUndefined(meterValuesTemplate
[index
].value
)) {
207 currentMeasurandValues
.allPhases
= Utils
.getRandomFloatRounded(maxAmperage
);
211 logger
.error(errMsg
);
214 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(meterValuesTemplate
[index
], currentMeasurandValues
.allPhases
));
215 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
216 if (Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxAmperage
|| debug
) {
217 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}`);
219 for (let phase
= 1; self.chargingStation
.getNumberOfPhases() === 3 && phase
<= self.chargingStation
.getNumberOfPhases(); phase
++) {
220 const phaseValue
= `L${phase}`;
221 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(meterValuesTemplate
[index
], currentMeasurandValues
[phaseValue
], null,
222 phaseValue
as MeterValuePhase
));
224 // Energy.Active.Import.Register measurand (default)
225 } else if (!meterValuesTemplate
[index
].measurand
|| meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
) {
226 OCPP16ServiceUtils
.checkMeasurandPowerDivider(self.chargingStation
, meterValuesTemplate
[index
].measurand
);
227 const unitDivider
= meterValuesTemplate
[index
]?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
228 if (Utils
.isUndefined(meterValuesTemplate
[index
].value
)) {
229 const energyMeasurandValue
= Utils
.getRandomInt(self.chargingStation
.stationInfo
.maxPower
/ (self.chargingStation
.stationInfo
.powerDivider
* 3600000) * interval
);
230 // Persist previous value in connector
231 if (connector
&& !Utils
.isNullOrUndefined(connector
.energyActiveImportRegisterValue
) && connector
.energyActiveImportRegisterValue
>= 0 &&
232 !Utils
.isNullOrUndefined(connector
.transactionEnergyActiveImportRegisterValue
) && connector
.transactionEnergyActiveImportRegisterValue
>= 0) {
233 connector
.energyActiveImportRegisterValue
+= energyMeasurandValue
;
234 connector
.transactionEnergyActiveImportRegisterValue
+= energyMeasurandValue
;
236 connector
.energyActiveImportRegisterValue
= 0;
237 connector
.transactionEnergyActiveImportRegisterValue
= 0;
240 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(meterValuesTemplate
[index
],
241 Utils
.roundTo(self.chargingStation
.getEnergyActiveImportRegisterByTransactionId(transactionId
) / unitDivider
, 4)));
242 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
243 const maxEnergy
= Math.round(self.chargingStation
.stationInfo
.maxPower
* 3600 / (self.chargingStation
.stationInfo
.powerDivider
* interval
));
244 const maxEnergyRounded
= Utils
.roundTo(maxEnergy
/ unitDivider
, 4);
245 if (Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxEnergyRounded
|| debug
) {
246 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}`);
248 // Unsupported measurand
250 logger
.info(`${self.chargingStation.logPrefix()} Unsupported MeterValues measurand ${meterValuesTemplate[index].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} on connectorId ${connectorId}`);
253 const payload
: MeterValuesRequest
= {
258 await self.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.METER_VALUES
);
260 self.handleRequestError(OCPP16RequestCommand
.METER_VALUES
, error
);
264 public async sendTransactionBeginMeterValues(connectorId
: number, transactionId
: number, meterBegin
: number): Promise
<void> {
265 const meterValue
: MeterValue
= {
266 timestamp
: new Date().toISOString(),
269 const meterValuesTemplate
: OCPP16SampledValue
[] = this.chargingStation
.getConnector(connectorId
).MeterValues
;
270 for (let index
= 0; index
< meterValuesTemplate
.length
; index
++) {
271 // Energy.Active.Import.Register measurand (default)
272 if (!meterValuesTemplate
[index
].measurand
|| meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
) {
273 const unitDivider
= meterValuesTemplate
[index
]?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
274 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(meterValuesTemplate
[index
],
275 Utils
.roundTo(meterBegin
/ unitDivider
, 4), MeterValueContext
.TRANSACTION_BEGIN
));
278 const payload
: MeterValuesRequest
= {
283 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.METER_VALUES
);
286 public async sendTransactionEndMeterValues(connectorId
: number, transactionId
: number, meterEnd
: number): Promise
<void> {
287 const meterValue
: MeterValue
= {
288 timestamp
: new Date().toISOString(),
291 const meterValuesTemplate
: OCPP16SampledValue
[] = this.chargingStation
.getConnector(connectorId
).MeterValues
;
292 for (let index
= 0; index
< meterValuesTemplate
.length
; index
++) {
293 // Energy.Active.Import.Register measurand (default)
294 if (!meterValuesTemplate
[index
].measurand
|| meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
) {
295 const unitDivider
= meterValuesTemplate
[index
]?.unit
=== MeterValueUnit
.KILO_WATT_HOUR
? 1000 : 1;
296 meterValue
.sampledValue
.push(OCPP16ServiceUtils
.buildSampledValue(meterValuesTemplate
[index
], Utils
.roundTo(meterEnd
/ unitDivider
, 4), MeterValueContext
.TRANSACTION_END
));
299 const payload
: MeterValuesRequest
= {
304 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.METER_VALUES
);
307 public async sendError(messageId
: string, error
: OCPPError
, commandName
: OCPP16RequestCommand
| OCPP16IncomingRequestCommand
): Promise
<unknown
> {
309 return this.sendMessage(messageId
, error
, MessageType
.CALL_ERROR_MESSAGE
, commandName
);