1 // Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
4 OCPP16AuthorizationStatus
,
5 OCPP16AuthorizeRequest
,
6 OCPP16AuthorizeResponse
,
7 OCPP16StartTransactionRequest
,
8 OCPP16StartTransactionResponse
,
9 OCPP16StopTransactionRequest
,
10 OCPP16StopTransactionResponse
,
11 } from
'../../../types/ocpp/1.6/Transaction';
13 OCPP16BootNotificationRequest
,
15 OCPP16StatusNotificationRequest
,
16 } from
'../../../types/ocpp/1.6/Requests';
18 OCPP16BootNotificationResponse
,
19 OCPP16RegistrationStatus
,
20 OCPP16StatusNotificationResponse
,
21 } from
'../../../types/ocpp/1.6/Responses';
23 OCPP16MeterValuesRequest
,
24 OCPP16MeterValuesResponse
,
25 } from
'../../../types/ocpp/1.6/MeterValues';
27 import type ChargingStation from
'../../ChargingStation';
28 import { ChargingStationConfigurationUtils
} from
'../../ChargingStationConfigurationUtils';
29 import { ErrorType
} from
'../../../types/ocpp/ErrorType';
30 import { JsonType
} from
'../../../types/JsonType';
31 import { OCPP16ChargePointErrorCode
} from
'../../../types/ocpp/1.6/ChargePointErrorCode';
32 import { OCPP16ChargePointStatus
} from
'../../../types/ocpp/1.6/ChargePointStatus';
33 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
34 import { OCPP16StandardParametersKey
} from
'../../../types/ocpp/1.6/Configuration';
35 import OCPPError from
'../../../exception/OCPPError';
36 import OCPPResponseService from
'../OCPPResponseService';
37 import { ResponseHandler
} from
'../../../types/ocpp/Responses';
38 import Utils from
'../../../utils/Utils';
39 import logger from
'../../../utils/Logger';
41 const moduleName
= 'OCPP16ResponseService';
43 export default class OCPP16ResponseService
extends OCPPResponseService
{
44 private responseHandlers
: Map
<OCPP16RequestCommand
, ResponseHandler
>;
46 public constructor() {
47 if (new.target
?.name
=== moduleName
) {
48 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
51 this.responseHandlers
= new Map
<OCPP16RequestCommand
, ResponseHandler
>([
52 [OCPP16RequestCommand
.BOOT_NOTIFICATION
, this.handleResponseBootNotification
.bind(this)],
53 [OCPP16RequestCommand
.HEARTBEAT
, this.handleResponseHeartbeat
.bind(this)],
54 [OCPP16RequestCommand
.AUTHORIZE
, this.handleResponseAuthorize
.bind(this)],
55 [OCPP16RequestCommand
.START_TRANSACTION
, this.handleResponseStartTransaction
.bind(this)],
56 [OCPP16RequestCommand
.STOP_TRANSACTION
, this.handleResponseStopTransaction
.bind(this)],
57 [OCPP16RequestCommand
.STATUS_NOTIFICATION
, this.handleResponseStatusNotification
.bind(this)],
58 [OCPP16RequestCommand
.METER_VALUES
, this.handleResponseMeterValues
.bind(this)],
62 public async responseHandler(
63 chargingStation
: ChargingStation
,
64 commandName
: OCPP16RequestCommand
,
66 requestPayload
: JsonType
68 if (chargingStation
.isRegistered() || commandName
=== OCPP16RequestCommand
.BOOT_NOTIFICATION
) {
69 if (this.responseHandlers
.has(commandName
)) {
71 await this.responseHandlers
.get(commandName
)(chargingStation
, payload
, requestPayload
);
73 logger
.error(chargingStation
.logPrefix() + ' Handle request response error: %j', error
);
79 ErrorType
.NOT_IMPLEMENTED
,
80 `${commandName} is not implemented to handle request response payload ${JSON.stringify(
90 ErrorType
.SECURITY_ERROR
,
91 `${commandName} cannot be issued to handle request response payload ${JSON.stringify(
95 )} while the charging station is not registered on the central server. `,
101 private handleResponseBootNotification(
102 chargingStation
: ChargingStation
,
103 payload
: OCPP16BootNotificationResponse
105 if (payload
.status === OCPP16RegistrationStatus
.ACCEPTED
) {
106 ChargingStationConfigurationUtils
.addConfigurationKey(
108 OCPP16StandardParametersKey
.HeartbeatInterval
,
109 payload
.interval
.toString(),
111 { overwrite
: true, save
: true }
113 ChargingStationConfigurationUtils
.addConfigurationKey(
115 OCPP16StandardParametersKey
.HeartBeatInterval
,
116 payload
.interval
.toString(),
118 { overwrite
: true, save
: true }
120 chargingStation
.heartbeatSetInterval
121 ? chargingStation
.restartHeartbeat()
122 : chargingStation
.startHeartbeat();
124 if (Object.values(OCPP16RegistrationStatus
).includes(payload
.status)) {
125 const logMsg
= `${chargingStation.logPrefix()} Charging station in '${
127 }' state on the central server`;
128 payload
.status === OCPP16RegistrationStatus
.REJECTED
129 ? logger
.warn(logMsg
)
130 : logger
.info(logMsg
);
133 chargingStation
.logPrefix() +
134 ' Charging station boot notification response received: %j with undefined registration status',
140 // eslint-disable-next-line @typescript-eslint/no-empty-function
141 private handleResponseHeartbeat(): void {}
143 private handleResponseAuthorize(
144 chargingStation
: ChargingStation
,
145 payload
: OCPP16AuthorizeResponse
,
146 requestPayload
: OCPP16AuthorizeRequest
148 let authorizeConnectorId
: number;
149 for (const connectorId
of chargingStation
.connectors
.keys()) {
152 chargingStation
.getConnectorStatus(connectorId
)?.authorizeIdTag
=== requestPayload
.idTag
154 authorizeConnectorId
= connectorId
;
158 if (payload
.idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
159 chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= true;
161 `${chargingStation.logPrefix()} IdTag ${
163 } authorized on connector ${authorizeConnectorId}`
166 chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= false;
167 delete chargingStation
.getConnectorStatus(authorizeConnectorId
).authorizeIdTag
;
169 `${chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status '${
170 payload.idTagInfo.status
171 }' on connector ${authorizeConnectorId}`
176 private async handleResponseStartTransaction(
177 chargingStation
: ChargingStation
,
178 payload
: OCPP16StartTransactionResponse
,
179 requestPayload
: OCPP16StartTransactionRequest
181 const connectorId
= requestPayload
.connectorId
;
183 let transactionConnectorId
: number;
184 for (const id
of chargingStation
.connectors
.keys()) {
185 if (id
> 0 && id
=== connectorId
) {
186 transactionConnectorId
= id
;
190 if (!transactionConnectorId
) {
192 chargingStation
.logPrefix() +
193 ' Trying to start a transaction on a non existing connector Id ' +
194 connectorId
.toString()
199 chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
200 chargingStation
.getAuthorizeRemoteTxRequests() &&
201 chargingStation
.getLocalAuthListEnabled() &&
202 chargingStation
.hasAuthorizedTags() &&
203 !chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
206 chargingStation
.logPrefix() +
207 ' Trying to start a transaction with a not local authorized idTag ' +
208 chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
209 ' on connector Id ' +
210 connectorId
.toString()
212 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
216 chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
217 chargingStation
.getAuthorizeRemoteTxRequests() &&
218 chargingStation
.getMayAuthorizeAtRemoteStart() &&
219 !chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
220 !chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
223 chargingStation
.logPrefix() +
224 ' Trying to start a transaction with a not authorized idTag ' +
225 chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
226 ' on connector Id ' +
227 connectorId
.toString()
229 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
233 chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
&&
234 chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
!== requestPayload
.idTag
237 chargingStation
.logPrefix() +
238 ' Trying to start a transaction with an idTag ' +
239 requestPayload
.idTag
+
240 ' different from the authorize request one ' +
241 chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
242 ' on connector Id ' +
243 connectorId
.toString()
245 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
249 chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
250 chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
!== requestPayload
.idTag
253 chargingStation
.logPrefix() +
254 ' Trying to start a transaction with an idTag ' +
255 requestPayload
.idTag
+
256 ' different from the local authorized one ' +
257 chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
258 ' on connector Id ' +
259 connectorId
.toString()
261 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
264 if (chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
) {
266 chargingStation
.logPrefix() +
267 ' Trying to start a transaction on an already used connector ' +
268 connectorId
.toString() +
270 chargingStation
.getConnectorStatus(connectorId
)
275 chargingStation
.getConnectorStatus(connectorId
)?.status !==
276 OCPP16ChargePointStatus
.AVAILABLE
&&
277 chargingStation
.getConnectorStatus(connectorId
)?.status !== OCPP16ChargePointStatus
.PREPARING
280 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
281 chargingStation.getConnectorStatus(connectorId)?.status
286 if (!Number.isInteger(payload
.transactionId
)) {
288 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${
289 payload.transactionId
290 }, converting to integer`
292 payload
.transactionId
= Utils
.convertToInt(payload
.transactionId
);
295 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
296 chargingStation
.getConnectorStatus(connectorId
).transactionStarted
= true;
297 chargingStation
.getConnectorStatus(connectorId
).transactionId
= payload
.transactionId
;
298 chargingStation
.getConnectorStatus(connectorId
).transactionIdTag
= requestPayload
.idTag
;
299 chargingStation
.getConnectorStatus(
301 ).transactionEnergyActiveImportRegisterValue
= 0;
302 chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
=
303 OCPP16ServiceUtils
.buildTransactionBeginMeterValue(
306 requestPayload
.meterStart
308 chargingStation
.getBeginEndMeterValues() &&
309 (await chargingStation
.ocppRequestService
.requestHandler
<
310 OCPP16MeterValuesRequest
,
311 OCPP16MeterValuesResponse
312 >(chargingStation
, OCPP16RequestCommand
.METER_VALUES
, {
314 transactionId
: payload
.transactionId
,
315 meterValue
: chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
,
317 await chargingStation
.ocppRequestService
.requestHandler
<
318 OCPP16StatusNotificationRequest
,
319 OCPP16StatusNotificationResponse
320 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
322 status: OCPP16ChargePointStatus
.CHARGING
,
323 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
325 chargingStation
.getConnectorStatus(connectorId
).status = OCPP16ChargePointStatus
.CHARGING
;
327 chargingStation
.logPrefix() +
329 payload
.transactionId
.toString() +
331 chargingStation
.stationInfo
.chargingStationId
+
333 connectorId
.toString() +
337 if (chargingStation
.stationInfo
.powerSharedByConnectors
) {
338 chargingStation
.powerDivider
++;
340 const configuredMeterValueSampleInterval
=
341 ChargingStationConfigurationUtils
.getConfigurationKey(
343 OCPP16StandardParametersKey
.MeterValueSampleInterval
345 chargingStation
.startMeterValues(
347 configuredMeterValueSampleInterval
348 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
353 chargingStation
.logPrefix() +
354 ' Starting transaction id ' +
355 payload
.transactionId
.toString() +
356 " REJECTED with status '" +
357 payload
?.idTagInfo
?.status +
361 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
365 private async resetConnectorOnStartTransactionError(
366 chargingStation
: ChargingStation
,
369 chargingStation
.resetConnectorStatus(connectorId
);
371 chargingStation
.getConnectorStatus(connectorId
).status !== OCPP16ChargePointStatus
.AVAILABLE
373 await chargingStation
.ocppRequestService
.requestHandler
<
374 OCPP16StatusNotificationRequest
,
375 OCPP16StatusNotificationResponse
376 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
378 status: OCPP16ChargePointStatus
.AVAILABLE
,
379 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
381 chargingStation
.getConnectorStatus(connectorId
).status = OCPP16ChargePointStatus
.AVAILABLE
;
385 private async handleResponseStopTransaction(
386 chargingStation
: ChargingStation
,
387 payload
: OCPP16StopTransactionResponse
,
388 requestPayload
: OCPP16StopTransactionRequest
390 const transactionConnectorId
= chargingStation
.getConnectorIdByTransactionId(
391 requestPayload
.transactionId
393 if (!transactionConnectorId
) {
395 chargingStation
.logPrefix() +
396 ' Trying to stop a non existing transaction ' +
397 requestPayload
.transactionId
.toString()
401 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
402 chargingStation
.getBeginEndMeterValues() &&
403 !chargingStation
.getOcppStrictCompliance() &&
404 chargingStation
.getOutOfOrderEndMeterValues() &&
405 (await chargingStation
.ocppRequestService
.requestHandler
<
406 OCPP16MeterValuesRequest
,
407 OCPP16MeterValuesResponse
408 >(chargingStation
, OCPP16RequestCommand
.METER_VALUES
, {
409 connectorId
: transactionConnectorId
,
410 transactionId
: requestPayload
.transactionId
,
411 meterValue
: OCPP16ServiceUtils
.buildTransactionEndMeterValue(
413 transactionConnectorId
,
414 requestPayload
.meterStop
418 !chargingStation
.isChargingStationAvailable() ||
419 !chargingStation
.isConnectorAvailable(transactionConnectorId
)
421 await chargingStation
.ocppRequestService
.requestHandler
<
422 OCPP16StatusNotificationRequest
,
423 OCPP16StatusNotificationResponse
424 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
425 connectorId
: transactionConnectorId
,
426 status: OCPP16ChargePointStatus
.UNAVAILABLE
,
427 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
429 chargingStation
.getConnectorStatus(transactionConnectorId
).status =
430 OCPP16ChargePointStatus
.UNAVAILABLE
;
432 await chargingStation
.ocppRequestService
.requestHandler
<
433 OCPP16BootNotificationRequest
,
434 OCPP16BootNotificationResponse
435 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
436 connectorId
: transactionConnectorId
,
437 status: OCPP16ChargePointStatus
.AVAILABLE
,
438 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
440 chargingStation
.getConnectorStatus(transactionConnectorId
).status =
441 OCPP16ChargePointStatus
.AVAILABLE
;
443 if (chargingStation
.stationInfo
.powerSharedByConnectors
) {
444 chargingStation
.powerDivider
--;
447 chargingStation
.logPrefix() +
449 requestPayload
.transactionId
.toString() +
451 chargingStation
.stationInfo
.chargingStationId
+
453 transactionConnectorId
.toString()
455 chargingStation
.resetConnectorStatus(transactionConnectorId
);
458 chargingStation
.logPrefix() +
459 ' Stopping transaction id ' +
460 requestPayload
.transactionId
.toString() +
461 " REJECTED with status '" +
462 payload
.idTagInfo
?.status +
468 // eslint-disable-next-line @typescript-eslint/no-empty-function
469 private handleResponseStatusNotification(): void {}
471 // eslint-disable-next-line @typescript-eslint/no-empty-function
472 private handleResponseMeterValues(): void {}