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 { ErrorType
} from
'../../../types/ocpp/ErrorType';
29 import { JsonType
} from
'../../../types/JsonType';
30 import { OCPP16ChargePointErrorCode
} from
'../../../types/ocpp/1.6/ChargePointErrorCode';
31 import { OCPP16ChargePointStatus
} from
'../../../types/ocpp/1.6/ChargePointStatus';
32 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
33 import { OCPP16StandardParametersKey
} from
'../../../types/ocpp/1.6/Configuration';
34 import OCPPError from
'../../../exception/OCPPError';
35 import OCPPResponseService from
'../OCPPResponseService';
36 import { ResponseHandler
} from
'../../../types/ocpp/Responses';
37 import Utils from
'../../../utils/Utils';
38 import logger from
'../../../utils/Logger';
40 const moduleName
= 'OCPP16ResponseService';
42 export default class OCPP16ResponseService
extends OCPPResponseService
{
43 private responseHandlers
: Map
<OCPP16RequestCommand
, ResponseHandler
>;
45 public constructor() {
46 if (new.target
?.name
=== moduleName
) {
47 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
50 this.responseHandlers
= new Map
<OCPP16RequestCommand
, ResponseHandler
>([
51 [OCPP16RequestCommand
.BOOT_NOTIFICATION
, this.handleResponseBootNotification
.bind(this)],
52 [OCPP16RequestCommand
.HEARTBEAT
, this.handleResponseHeartbeat
.bind(this)],
53 [OCPP16RequestCommand
.AUTHORIZE
, this.handleResponseAuthorize
.bind(this)],
54 [OCPP16RequestCommand
.START_TRANSACTION
, this.handleResponseStartTransaction
.bind(this)],
55 [OCPP16RequestCommand
.STOP_TRANSACTION
, this.handleResponseStopTransaction
.bind(this)],
56 [OCPP16RequestCommand
.STATUS_NOTIFICATION
, this.handleResponseStatusNotification
.bind(this)],
57 [OCPP16RequestCommand
.METER_VALUES
, this.handleResponseMeterValues
.bind(this)],
61 public async responseHandler(
62 chargingStation
: ChargingStation
,
63 commandName
: OCPP16RequestCommand
,
65 requestPayload
: JsonType
67 if (chargingStation
.isRegistered() || commandName
=== OCPP16RequestCommand
.BOOT_NOTIFICATION
) {
68 if (this.responseHandlers
.has(commandName
)) {
70 await this.responseHandlers
.get(commandName
)(chargingStation
, payload
, requestPayload
);
72 logger
.error(chargingStation
.logPrefix() + ' Handle request response error: %j', error
);
78 ErrorType
.NOT_IMPLEMENTED
,
79 `${commandName} is not implemented to handle request response payload ${JSON.stringify(
89 ErrorType
.SECURITY_ERROR
,
90 `${commandName} cannot be issued to handle request response payload ${JSON.stringify(
94 )} while the charging station is not registered on the central server. `,
100 private handleResponseBootNotification(
101 chargingStation
: ChargingStation
,
102 payload
: OCPP16BootNotificationResponse
104 if (payload
.status === OCPP16RegistrationStatus
.ACCEPTED
) {
105 chargingStation
.addConfigurationKey(
106 OCPP16StandardParametersKey
.HeartbeatInterval
,
107 payload
.interval
.toString(),
109 { overwrite
: true, save
: true }
111 chargingStation
.addConfigurationKey(
112 OCPP16StandardParametersKey
.HeartBeatInterval
,
113 payload
.interval
.toString(),
115 { overwrite
: true, save
: true }
117 chargingStation
.heartbeatSetInterval
118 ? chargingStation
.restartHeartbeat()
119 : chargingStation
.startHeartbeat();
121 if (Object.values(OCPP16RegistrationStatus
).includes(payload
.status)) {
122 const logMsg
= `${chargingStation.logPrefix()} Charging station in '${
124 }' state on the central server`;
125 payload
.status === OCPP16RegistrationStatus
.REJECTED
126 ? logger
.warn(logMsg
)
127 : logger
.info(logMsg
);
130 chargingStation
.logPrefix() +
131 ' Charging station boot notification response received: %j with undefined registration status',
137 // eslint-disable-next-line @typescript-eslint/no-empty-function
138 private handleResponseHeartbeat(): void {}
140 private handleResponseAuthorize(
141 chargingStation
: ChargingStation
,
142 payload
: OCPP16AuthorizeResponse
,
143 requestPayload
: OCPP16AuthorizeRequest
145 let authorizeConnectorId
: number;
146 for (const connectorId
of chargingStation
.connectors
.keys()) {
149 chargingStation
.getConnectorStatus(connectorId
)?.authorizeIdTag
=== requestPayload
.idTag
151 authorizeConnectorId
= connectorId
;
155 if (payload
.idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
156 chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= true;
158 `${chargingStation.logPrefix()} IdTag ${
160 } authorized on connector ${authorizeConnectorId}`
163 chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= false;
164 delete chargingStation
.getConnectorStatus(authorizeConnectorId
).authorizeIdTag
;
166 `${chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status '${
167 payload.idTagInfo.status
168 }' on connector ${authorizeConnectorId}`
173 private async handleResponseStartTransaction(
174 chargingStation
: ChargingStation
,
175 payload
: OCPP16StartTransactionResponse
,
176 requestPayload
: OCPP16StartTransactionRequest
178 const connectorId
= requestPayload
.connectorId
;
180 let transactionConnectorId
: number;
181 for (const id
of chargingStation
.connectors
.keys()) {
182 if (id
> 0 && id
=== connectorId
) {
183 transactionConnectorId
= id
;
187 if (!transactionConnectorId
) {
189 chargingStation
.logPrefix() +
190 ' Trying to start a transaction on a non existing connector Id ' +
191 connectorId
.toString()
196 chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
197 chargingStation
.getAuthorizeRemoteTxRequests() &&
198 chargingStation
.getLocalAuthListEnabled() &&
199 chargingStation
.hasAuthorizedTags() &&
200 !chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
203 chargingStation
.logPrefix() +
204 ' Trying to start a transaction with a not local authorized idTag ' +
205 chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
206 ' on connector Id ' +
207 connectorId
.toString()
209 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
213 chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
214 chargingStation
.getAuthorizeRemoteTxRequests() &&
215 chargingStation
.getMayAuthorizeAtRemoteStart() &&
216 !chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
217 !chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
220 chargingStation
.logPrefix() +
221 ' Trying to start a transaction with a not authorized idTag ' +
222 chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
223 ' on connector Id ' +
224 connectorId
.toString()
226 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
230 chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
&&
231 chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
!== requestPayload
.idTag
234 chargingStation
.logPrefix() +
235 ' Trying to start a transaction with an idTag ' +
236 requestPayload
.idTag
+
237 ' different from the authorize request one ' +
238 chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
239 ' on connector Id ' +
240 connectorId
.toString()
242 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
246 chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
247 chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
!== requestPayload
.idTag
250 chargingStation
.logPrefix() +
251 ' Trying to start a transaction with an idTag ' +
252 requestPayload
.idTag
+
253 ' different from the local authorized one ' +
254 chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
255 ' on connector Id ' +
256 connectorId
.toString()
258 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
261 if (chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
) {
263 chargingStation
.logPrefix() +
264 ' Trying to start a transaction on an already used connector ' +
265 connectorId
.toString() +
267 chargingStation
.getConnectorStatus(connectorId
)
272 chargingStation
.getConnectorStatus(connectorId
)?.status !==
273 OCPP16ChargePointStatus
.AVAILABLE
&&
274 chargingStation
.getConnectorStatus(connectorId
)?.status !== OCPP16ChargePointStatus
.PREPARING
277 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
278 chargingStation.getConnectorStatus(connectorId)?.status
283 if (!Number.isInteger(payload
.transactionId
)) {
285 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${
286 payload.transactionId
287 }, converting to integer`
289 payload
.transactionId
= Utils
.convertToInt(payload
.transactionId
);
292 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
293 chargingStation
.getConnectorStatus(connectorId
).transactionStarted
= true;
294 chargingStation
.getConnectorStatus(connectorId
).transactionId
= payload
.transactionId
;
295 chargingStation
.getConnectorStatus(connectorId
).transactionIdTag
= requestPayload
.idTag
;
296 chargingStation
.getConnectorStatus(
298 ).transactionEnergyActiveImportRegisterValue
= 0;
299 chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
=
300 OCPP16ServiceUtils
.buildTransactionBeginMeterValue(
303 requestPayload
.meterStart
305 chargingStation
.getBeginEndMeterValues() &&
306 (await chargingStation
.ocppRequestService
.requestHandler
<
307 OCPP16MeterValuesRequest
,
308 OCPP16MeterValuesResponse
309 >(chargingStation
, OCPP16RequestCommand
.METER_VALUES
, {
311 transactionId
: payload
.transactionId
,
312 meterValue
: chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
,
314 await chargingStation
.ocppRequestService
.requestHandler
<
315 OCPP16StatusNotificationRequest
,
316 OCPP16StatusNotificationResponse
317 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
319 status: OCPP16ChargePointStatus
.CHARGING
,
320 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
322 chargingStation
.getConnectorStatus(connectorId
).status = OCPP16ChargePointStatus
.CHARGING
;
324 chargingStation
.logPrefix() +
326 payload
.transactionId
.toString() +
328 chargingStation
.stationInfo
.chargingStationId
+
330 connectorId
.toString() +
334 if (chargingStation
.stationInfo
.powerSharedByConnectors
) {
335 chargingStation
.stationInfo
.powerDivider
++;
337 const configuredMeterValueSampleInterval
= chargingStation
.getConfigurationKey(
338 OCPP16StandardParametersKey
.MeterValueSampleInterval
340 chargingStation
.startMeterValues(
342 configuredMeterValueSampleInterval
343 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
348 chargingStation
.logPrefix() +
349 ' Starting transaction id ' +
350 payload
.transactionId
.toString() +
351 " REJECTED with status '" +
352 payload
?.idTagInfo
?.status +
356 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
360 private async resetConnectorOnStartTransactionError(
361 chargingStation
: ChargingStation
,
364 chargingStation
.resetConnectorStatus(connectorId
);
366 chargingStation
.getConnectorStatus(connectorId
).status !== OCPP16ChargePointStatus
.AVAILABLE
368 await chargingStation
.ocppRequestService
.requestHandler
<
369 OCPP16StatusNotificationRequest
,
370 OCPP16StatusNotificationResponse
371 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
373 status: OCPP16ChargePointStatus
.AVAILABLE
,
374 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
376 chargingStation
.getConnectorStatus(connectorId
).status = OCPP16ChargePointStatus
.AVAILABLE
;
380 private async handleResponseStopTransaction(
381 chargingStation
: ChargingStation
,
382 payload
: OCPP16StopTransactionResponse
,
383 requestPayload
: OCPP16StopTransactionRequest
385 const transactionConnectorId
= chargingStation
.getConnectorIdByTransactionId(
386 requestPayload
.transactionId
388 if (!transactionConnectorId
) {
390 chargingStation
.logPrefix() +
391 ' Trying to stop a non existing transaction ' +
392 requestPayload
.transactionId
.toString()
396 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
397 chargingStation
.getBeginEndMeterValues() &&
398 !chargingStation
.getOcppStrictCompliance() &&
399 chargingStation
.getOutOfOrderEndMeterValues() &&
400 (await chargingStation
.ocppRequestService
.requestHandler
<
401 OCPP16MeterValuesRequest
,
402 OCPP16MeterValuesResponse
403 >(chargingStation
, OCPP16RequestCommand
.METER_VALUES
, {
404 connectorId
: transactionConnectorId
,
405 transactionId
: requestPayload
.transactionId
,
406 meterValue
: OCPP16ServiceUtils
.buildTransactionEndMeterValue(
408 transactionConnectorId
,
409 requestPayload
.meterStop
413 !chargingStation
.isChargingStationAvailable() ||
414 !chargingStation
.isConnectorAvailable(transactionConnectorId
)
416 await chargingStation
.ocppRequestService
.requestHandler
<
417 OCPP16StatusNotificationRequest
,
418 OCPP16StatusNotificationResponse
419 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
420 connectorId
: transactionConnectorId
,
421 status: OCPP16ChargePointStatus
.UNAVAILABLE
,
422 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
424 chargingStation
.getConnectorStatus(transactionConnectorId
).status =
425 OCPP16ChargePointStatus
.UNAVAILABLE
;
427 await chargingStation
.ocppRequestService
.requestHandler
<
428 OCPP16BootNotificationRequest
,
429 OCPP16BootNotificationResponse
430 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
431 connectorId
: transactionConnectorId
,
432 status: OCPP16ChargePointStatus
.AVAILABLE
,
433 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
435 chargingStation
.getConnectorStatus(transactionConnectorId
).status =
436 OCPP16ChargePointStatus
.AVAILABLE
;
438 if (chargingStation
.stationInfo
.powerSharedByConnectors
) {
439 chargingStation
.stationInfo
.powerDivider
--;
442 chargingStation
.logPrefix() +
444 requestPayload
.transactionId
.toString() +
446 chargingStation
.stationInfo
.chargingStationId
+
448 transactionConnectorId
.toString()
450 chargingStation
.resetConnectorStatus(transactionConnectorId
);
453 chargingStation
.logPrefix() +
454 ' Stopping transaction id ' +
455 requestPayload
.transactionId
.toString() +
456 " REJECTED with status '" +
457 payload
.idTagInfo
?.status +
463 // eslint-disable-next-line @typescript-eslint/no-empty-function
464 private handleResponseStatusNotification(): void {}
466 // eslint-disable-next-line @typescript-eslint/no-empty-function
467 private handleResponseMeterValues(): void {}