1 // Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
3 import OCPPError from
'../../../exception/OCPPError';
4 import { JsonType
} from
'../../../types/JsonType';
5 import { OCPP16ChargePointErrorCode
} from
'../../../types/ocpp/1.6/ChargePointErrorCode';
6 import { OCPP16ChargePointStatus
} from
'../../../types/ocpp/1.6/ChargePointStatus';
7 import { OCPP16StandardParametersKey
} from
'../../../types/ocpp/1.6/Configuration';
9 OCPP16MeterValuesRequest
,
10 OCPP16MeterValuesResponse
,
11 } from
'../../../types/ocpp/1.6/MeterValues';
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 OCPP16AuthorizationStatus
,
24 OCPP16AuthorizeRequest
,
25 OCPP16AuthorizeResponse
,
26 OCPP16StartTransactionRequest
,
27 OCPP16StartTransactionResponse
,
28 OCPP16StopTransactionRequest
,
29 OCPP16StopTransactionResponse
,
30 } from
'../../../types/ocpp/1.6/Transaction';
31 import { ErrorType
} from
'../../../types/ocpp/ErrorType';
32 import { ResponseHandler
} from
'../../../types/ocpp/Responses';
33 import logger from
'../../../utils/Logger';
34 import Utils from
'../../../utils/Utils';
35 import type ChargingStation from
'../../ChargingStation';
36 import { ChargingStationConfigurationUtils
} from
'../../ChargingStationConfigurationUtils';
37 import OCPPResponseService from
'../OCPPResponseService';
38 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
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 ChargingStationConfigurationUtils
.addConfigurationKey(
107 OCPP16StandardParametersKey
.HeartbeatInterval
,
108 payload
.interval
.toString(),
110 { overwrite
: true, save
: true }
112 ChargingStationConfigurationUtils
.addConfigurationKey(
114 OCPP16StandardParametersKey
.HeartBeatInterval
,
115 payload
.interval
.toString(),
117 { overwrite
: true, save
: true }
119 chargingStation
.heartbeatSetInterval
120 ? chargingStation
.restartHeartbeat()
121 : chargingStation
.startHeartbeat();
123 if (Object.values(OCPP16RegistrationStatus
).includes(payload
.status)) {
124 const logMsg
= `${chargingStation.logPrefix()} Charging station in '${
126 }' state on the central server`;
127 payload
.status === OCPP16RegistrationStatus
.REJECTED
128 ? logger
.warn(logMsg
)
129 : logger
.info(logMsg
);
132 chargingStation
.logPrefix() +
133 ' Charging station boot notification response received: %j with undefined registration status',
139 // eslint-disable-next-line @typescript-eslint/no-empty-function
140 private handleResponseHeartbeat(): void {}
142 private handleResponseAuthorize(
143 chargingStation
: ChargingStation
,
144 payload
: OCPP16AuthorizeResponse
,
145 requestPayload
: OCPP16AuthorizeRequest
147 let authorizeConnectorId
: number;
148 for (const connectorId
of chargingStation
.connectors
.keys()) {
151 chargingStation
.getConnectorStatus(connectorId
)?.authorizeIdTag
=== requestPayload
.idTag
153 authorizeConnectorId
= connectorId
;
157 if (payload
.idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
158 chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= true;
160 `${chargingStation.logPrefix()} IdTag ${
162 } authorized on connector ${authorizeConnectorId}`
165 chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= false;
166 delete chargingStation
.getConnectorStatus(authorizeConnectorId
).authorizeIdTag
;
168 `${chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status '${
169 payload.idTagInfo.status
170 }' on connector ${authorizeConnectorId}`
175 private async handleResponseStartTransaction(
176 chargingStation
: ChargingStation
,
177 payload
: OCPP16StartTransactionResponse
,
178 requestPayload
: OCPP16StartTransactionRequest
180 const connectorId
= requestPayload
.connectorId
;
182 let transactionConnectorId
: number;
183 for (const id
of chargingStation
.connectors
.keys()) {
184 if (id
> 0 && id
=== connectorId
) {
185 transactionConnectorId
= id
;
189 if (!transactionConnectorId
) {
191 chargingStation
.logPrefix() +
192 ' Trying to start a transaction on a non existing connector Id ' +
193 connectorId
.toString()
198 chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
199 chargingStation
.getAuthorizeRemoteTxRequests() &&
200 chargingStation
.getLocalAuthListEnabled() &&
201 chargingStation
.hasAuthorizedTags() &&
202 !chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
205 chargingStation
.logPrefix() +
206 ' Trying to start a transaction with a not local authorized idTag ' +
207 chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
208 ' on connector Id ' +
209 connectorId
.toString()
211 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
215 chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
216 chargingStation
.getAuthorizeRemoteTxRequests() &&
217 chargingStation
.getMayAuthorizeAtRemoteStart() &&
218 !chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
219 !chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
222 chargingStation
.logPrefix() +
223 ' Trying to start a transaction with a not authorized idTag ' +
224 chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
225 ' on connector Id ' +
226 connectorId
.toString()
228 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
232 chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
&&
233 chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
!== requestPayload
.idTag
236 chargingStation
.logPrefix() +
237 ' Trying to start a transaction with an idTag ' +
238 requestPayload
.idTag
+
239 ' different from the authorize request one ' +
240 chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
241 ' on connector Id ' +
242 connectorId
.toString()
244 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
248 chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
249 chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
!== requestPayload
.idTag
252 chargingStation
.logPrefix() +
253 ' Trying to start a transaction with an idTag ' +
254 requestPayload
.idTag
+
255 ' different from the local authorized one ' +
256 chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
257 ' on connector Id ' +
258 connectorId
.toString()
260 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
263 if (chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
) {
265 chargingStation
.logPrefix() +
266 ' Trying to start a transaction on an already used connector ' +
267 connectorId
.toString() +
269 chargingStation
.getConnectorStatus(connectorId
)
274 chargingStation
.getConnectorStatus(connectorId
)?.status !==
275 OCPP16ChargePointStatus
.AVAILABLE
&&
276 chargingStation
.getConnectorStatus(connectorId
)?.status !== OCPP16ChargePointStatus
.PREPARING
279 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
280 chargingStation.getConnectorStatus(connectorId)?.status
285 if (!Number.isInteger(payload
.transactionId
)) {
287 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${
288 payload.transactionId
289 }, converting to integer`
291 payload
.transactionId
= Utils
.convertToInt(payload
.transactionId
);
294 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
295 chargingStation
.getConnectorStatus(connectorId
).transactionStarted
= true;
296 chargingStation
.getConnectorStatus(connectorId
).transactionId
= payload
.transactionId
;
297 chargingStation
.getConnectorStatus(connectorId
).transactionIdTag
= requestPayload
.idTag
;
298 chargingStation
.getConnectorStatus(
300 ).transactionEnergyActiveImportRegisterValue
= 0;
301 chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
=
302 OCPP16ServiceUtils
.buildTransactionBeginMeterValue(
305 requestPayload
.meterStart
307 chargingStation
.getBeginEndMeterValues() &&
308 (await chargingStation
.ocppRequestService
.requestHandler
<
309 OCPP16MeterValuesRequest
,
310 OCPP16MeterValuesResponse
311 >(chargingStation
, OCPP16RequestCommand
.METER_VALUES
, {
313 transactionId
: payload
.transactionId
,
314 meterValue
: chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
,
316 await chargingStation
.ocppRequestService
.requestHandler
<
317 OCPP16StatusNotificationRequest
,
318 OCPP16StatusNotificationResponse
319 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
321 status: OCPP16ChargePointStatus
.CHARGING
,
322 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
324 chargingStation
.getConnectorStatus(connectorId
).status = OCPP16ChargePointStatus
.CHARGING
;
326 chargingStation
.logPrefix() +
328 payload
.transactionId
.toString() +
330 chargingStation
.stationInfo
.chargingStationId
+
332 connectorId
.toString() +
336 if (chargingStation
.stationInfo
.powerSharedByConnectors
) {
337 chargingStation
.powerDivider
++;
339 const configuredMeterValueSampleInterval
=
340 ChargingStationConfigurationUtils
.getConfigurationKey(
342 OCPP16StandardParametersKey
.MeterValueSampleInterval
344 chargingStation
.startMeterValues(
346 configuredMeterValueSampleInterval
347 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
352 chargingStation
.logPrefix() +
353 ' Starting transaction id ' +
354 payload
.transactionId
.toString() +
355 " REJECTED with status '" +
356 payload
?.idTagInfo
?.status +
360 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
364 private async resetConnectorOnStartTransactionError(
365 chargingStation
: ChargingStation
,
368 chargingStation
.resetConnectorStatus(connectorId
);
370 chargingStation
.getConnectorStatus(connectorId
).status !== OCPP16ChargePointStatus
.AVAILABLE
372 await chargingStation
.ocppRequestService
.requestHandler
<
373 OCPP16StatusNotificationRequest
,
374 OCPP16StatusNotificationResponse
375 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
377 status: OCPP16ChargePointStatus
.AVAILABLE
,
378 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
380 chargingStation
.getConnectorStatus(connectorId
).status = OCPP16ChargePointStatus
.AVAILABLE
;
384 private async handleResponseStopTransaction(
385 chargingStation
: ChargingStation
,
386 payload
: OCPP16StopTransactionResponse
,
387 requestPayload
: OCPP16StopTransactionRequest
389 const transactionConnectorId
= chargingStation
.getConnectorIdByTransactionId(
390 requestPayload
.transactionId
392 if (!transactionConnectorId
) {
394 chargingStation
.logPrefix() +
395 ' Trying to stop a non existing transaction ' +
396 requestPayload
.transactionId
.toString()
400 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
401 chargingStation
.getBeginEndMeterValues() &&
402 !chargingStation
.getOcppStrictCompliance() &&
403 chargingStation
.getOutOfOrderEndMeterValues() &&
404 (await chargingStation
.ocppRequestService
.requestHandler
<
405 OCPP16MeterValuesRequest
,
406 OCPP16MeterValuesResponse
407 >(chargingStation
, OCPP16RequestCommand
.METER_VALUES
, {
408 connectorId
: transactionConnectorId
,
409 transactionId
: requestPayload
.transactionId
,
410 meterValue
: OCPP16ServiceUtils
.buildTransactionEndMeterValue(
412 transactionConnectorId
,
413 requestPayload
.meterStop
417 !chargingStation
.isChargingStationAvailable() ||
418 !chargingStation
.isConnectorAvailable(transactionConnectorId
)
420 await chargingStation
.ocppRequestService
.requestHandler
<
421 OCPP16StatusNotificationRequest
,
422 OCPP16StatusNotificationResponse
423 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
424 connectorId
: transactionConnectorId
,
425 status: OCPP16ChargePointStatus
.UNAVAILABLE
,
426 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
428 chargingStation
.getConnectorStatus(transactionConnectorId
).status =
429 OCPP16ChargePointStatus
.UNAVAILABLE
;
431 await chargingStation
.ocppRequestService
.requestHandler
<
432 OCPP16BootNotificationRequest
,
433 OCPP16BootNotificationResponse
434 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
435 connectorId
: transactionConnectorId
,
436 status: OCPP16ChargePointStatus
.AVAILABLE
,
437 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
439 chargingStation
.getConnectorStatus(transactionConnectorId
).status =
440 OCPP16ChargePointStatus
.AVAILABLE
;
442 if (chargingStation
.stationInfo
.powerSharedByConnectors
) {
443 chargingStation
.powerDivider
--;
446 chargingStation
.logPrefix() +
448 requestPayload
.transactionId
.toString() +
450 chargingStation
.stationInfo
.chargingStationId
+
452 transactionConnectorId
.toString()
454 chargingStation
.resetConnectorStatus(transactionConnectorId
);
457 chargingStation
.logPrefix() +
458 ' Stopping transaction id ' +
459 requestPayload
.transactionId
.toString() +
460 " REJECTED with status '" +
461 payload
.idTagInfo
?.status +
467 // eslint-disable-next-line @typescript-eslint/no-empty-function
468 private handleResponseStatusNotification(): void {}
470 // eslint-disable-next-line @typescript-eslint/no-empty-function
471 private handleResponseMeterValues(): void {}