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(chargingStation
: ChargingStation
) {
46 if (new.target
?.name
=== moduleName
) {
47 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
49 super(chargingStation
);
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 commandName
: OCPP16RequestCommand
,
64 requestPayload
: JsonType
67 this.chargingStation
.isRegistered() ||
68 commandName
=== OCPP16RequestCommand
.BOOT_NOTIFICATION
70 if (this.responseHandlers
.has(commandName
)) {
72 await this.responseHandlers
.get(commandName
)(payload
, requestPayload
);
75 this.chargingStation
.logPrefix() + ' Handle request response error: %j',
83 ErrorType
.NOT_IMPLEMENTED
,
84 `${commandName} is not implemented to handle request response payload ${JSON.stringify(
94 ErrorType
.SECURITY_ERROR
,
95 `${commandName} cannot be issued to handle request response payload ${JSON.stringify(
99 )} while the charging station is not registered on the central server. `,
105 private handleResponseBootNotification(payload
: OCPP16BootNotificationResponse
): void {
106 if (payload
.status === OCPP16RegistrationStatus
.ACCEPTED
) {
107 this.chargingStation
.addConfigurationKey(
108 OCPP16StandardParametersKey
.HeartbeatInterval
,
109 payload
.interval
.toString(),
111 { overwrite
: true, save
: true }
113 this.chargingStation
.addConfigurationKey(
114 OCPP16StandardParametersKey
.HeartBeatInterval
,
115 payload
.interval
.toString(),
117 { overwrite
: true, save
: true }
119 this.chargingStation
.heartbeatSetInterval
120 ? this.chargingStation
.restartHeartbeat()
121 : this.chargingStation
.startHeartbeat();
123 if (Object.values(OCPP16RegistrationStatus
).includes(payload
.status)) {
124 const logMsg
= `${this.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 this.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 payload
: OCPP16AuthorizeResponse
,
144 requestPayload
: OCPP16AuthorizeRequest
146 let authorizeConnectorId
: number;
147 for (const connectorId
of this.chargingStation
.connectors
.keys()) {
150 this.chargingStation
.getConnectorStatus(connectorId
)?.authorizeIdTag
===
153 authorizeConnectorId
= connectorId
;
157 if (payload
.idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
158 this.chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= true;
160 `${this.chargingStation.logPrefix()} IdTag ${
162 } authorized on connector ${authorizeConnectorId}`
165 this.chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= false;
166 delete this.chargingStation
.getConnectorStatus(authorizeConnectorId
).authorizeIdTag
;
168 `${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status '${
169 payload.idTagInfo.status
170 }' on connector ${authorizeConnectorId}`
175 private async handleResponseStartTransaction(
176 payload
: OCPP16StartTransactionResponse
,
177 requestPayload
: OCPP16StartTransactionRequest
179 const connectorId
= requestPayload
.connectorId
;
181 let transactionConnectorId
: number;
182 for (const id
of this.chargingStation
.connectors
.keys()) {
183 if (id
> 0 && id
=== connectorId
) {
184 transactionConnectorId
= id
;
188 if (!transactionConnectorId
) {
190 this.chargingStation
.logPrefix() +
191 ' Trying to start a transaction on a non existing connector Id ' +
192 connectorId
.toString()
197 this.chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
198 this.chargingStation
.getAuthorizeRemoteTxRequests() &&
199 this.chargingStation
.getLocalAuthListEnabled() &&
200 this.chargingStation
.hasAuthorizedTags() &&
201 !this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
204 this.chargingStation
.logPrefix() +
205 ' Trying to start a transaction with a not local authorized idTag ' +
206 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
207 ' on connector Id ' +
208 connectorId
.toString()
210 await this.resetConnectorOnStartTransactionError(connectorId
);
214 this.chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
215 this.chargingStation
.getAuthorizeRemoteTxRequests() &&
216 this.chargingStation
.getMayAuthorizeAtRemoteStart() &&
217 !this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
218 !this.chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
221 this.chargingStation
.logPrefix() +
222 ' Trying to start a transaction with a not authorized idTag ' +
223 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
224 ' on connector Id ' +
225 connectorId
.toString()
227 await this.resetConnectorOnStartTransactionError(connectorId
);
231 this.chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
&&
232 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
!== requestPayload
.idTag
235 this.chargingStation
.logPrefix() +
236 ' Trying to start a transaction with an idTag ' +
237 requestPayload
.idTag
+
238 ' different from the authorize request one ' +
239 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
240 ' on connector Id ' +
241 connectorId
.toString()
243 await this.resetConnectorOnStartTransactionError(connectorId
);
247 this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
248 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
!==
252 this.chargingStation
.logPrefix() +
253 ' Trying to start a transaction with an idTag ' +
254 requestPayload
.idTag
+
255 ' different from the local authorized one ' +
256 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
257 ' on connector Id ' +
258 connectorId
.toString()
260 await this.resetConnectorOnStartTransactionError(connectorId
);
263 if (this.chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
) {
265 this.chargingStation
.logPrefix() +
266 ' Trying to start a transaction on an already used connector ' +
267 connectorId
.toString() +
269 this.chargingStation
.getConnectorStatus(connectorId
)
274 this.chargingStation
.getConnectorStatus(connectorId
)?.status !==
275 OCPP16ChargePointStatus
.AVAILABLE
&&
276 this.chargingStation
.getConnectorStatus(connectorId
)?.status !==
277 OCPP16ChargePointStatus
.PREPARING
280 `${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
281 this.chargingStation.getConnectorStatus(connectorId)?.status
286 if (!Number.isInteger(payload
.transactionId
)) {
288 `${this.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 this.chargingStation
.getConnectorStatus(connectorId
).transactionStarted
= true;
297 this.chargingStation
.getConnectorStatus(connectorId
).transactionId
= payload
.transactionId
;
298 this.chargingStation
.getConnectorStatus(connectorId
).transactionIdTag
= requestPayload
.idTag
;
299 this.chargingStation
.getConnectorStatus(
301 ).transactionEnergyActiveImportRegisterValue
= 0;
302 this.chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
=
303 OCPP16ServiceUtils
.buildTransactionBeginMeterValue(
304 this.chargingStation
,
306 requestPayload
.meterStart
308 this.chargingStation
.getBeginEndMeterValues() &&
309 (await this.chargingStation
.ocppRequestService
.requestHandler
<
310 OCPP16MeterValuesRequest
,
311 OCPP16MeterValuesResponse
312 >(OCPP16RequestCommand
.METER_VALUES
, {
314 transactionId
: payload
.transactionId
,
316 this.chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
,
318 await this.chargingStation
.ocppRequestService
.requestHandler
<
319 OCPP16StatusNotificationRequest
,
320 OCPP16StatusNotificationResponse
321 >(OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
323 status: OCPP16ChargePointStatus
.CHARGING
,
324 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
326 this.chargingStation
.getConnectorStatus(connectorId
).status =
327 OCPP16ChargePointStatus
.CHARGING
;
329 this.chargingStation
.logPrefix() +
331 payload
.transactionId
.toString() +
333 this.chargingStation
.stationInfo
.chargingStationId
+
335 connectorId
.toString() +
339 if (this.chargingStation
.stationInfo
.powerSharedByConnectors
) {
340 this.chargingStation
.stationInfo
.powerDivider
++;
342 const configuredMeterValueSampleInterval
= this.chargingStation
.getConfigurationKey(
343 OCPP16StandardParametersKey
.MeterValueSampleInterval
345 this.chargingStation
.startMeterValues(
347 configuredMeterValueSampleInterval
348 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
353 this.chargingStation
.logPrefix() +
354 ' Starting transaction id ' +
355 payload
.transactionId
.toString() +
356 " REJECTED with status '" +
357 payload
?.idTagInfo
?.status +
361 await this.resetConnectorOnStartTransactionError(connectorId
);
365 private async resetConnectorOnStartTransactionError(connectorId
: number): Promise
<void> {
366 this.chargingStation
.resetConnectorStatus(connectorId
);
368 this.chargingStation
.getConnectorStatus(connectorId
).status !==
369 OCPP16ChargePointStatus
.AVAILABLE
371 await this.chargingStation
.ocppRequestService
.requestHandler
<
372 OCPP16StatusNotificationRequest
,
373 OCPP16StatusNotificationResponse
374 >(OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
376 status: OCPP16ChargePointStatus
.AVAILABLE
,
377 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
379 this.chargingStation
.getConnectorStatus(connectorId
).status =
380 OCPP16ChargePointStatus
.AVAILABLE
;
384 private async handleResponseStopTransaction(
385 payload
: OCPP16StopTransactionResponse
,
386 requestPayload
: OCPP16StopTransactionRequest
388 const transactionConnectorId
= this.chargingStation
.getConnectorIdByTransactionId(
389 requestPayload
.transactionId
391 if (!transactionConnectorId
) {
393 this.chargingStation
.logPrefix() +
394 ' Trying to stop a non existing transaction ' +
395 requestPayload
.transactionId
.toString()
399 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
400 this.chargingStation
.getBeginEndMeterValues() &&
401 !this.chargingStation
.getOcppStrictCompliance() &&
402 this.chargingStation
.getOutOfOrderEndMeterValues() &&
403 (await this.chargingStation
.ocppRequestService
.requestHandler
<
404 OCPP16MeterValuesRequest
,
405 OCPP16MeterValuesResponse
406 >(OCPP16RequestCommand
.METER_VALUES
, {
407 connectorId
: transactionConnectorId
,
408 transactionId
: requestPayload
.transactionId
,
409 meterValue
: OCPP16ServiceUtils
.buildTransactionEndMeterValue(
410 this.chargingStation
,
411 transactionConnectorId
,
412 requestPayload
.meterStop
416 !this.chargingStation
.isChargingStationAvailable() ||
417 !this.chargingStation
.isConnectorAvailable(transactionConnectorId
)
419 await this.chargingStation
.ocppRequestService
.requestHandler
<
420 OCPP16StatusNotificationRequest
,
421 OCPP16StatusNotificationResponse
422 >(OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
423 connectorId
: transactionConnectorId
,
424 status: OCPP16ChargePointStatus
.UNAVAILABLE
,
425 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
427 this.chargingStation
.getConnectorStatus(transactionConnectorId
).status =
428 OCPP16ChargePointStatus
.UNAVAILABLE
;
430 await this.chargingStation
.ocppRequestService
.requestHandler
<
431 OCPP16BootNotificationRequest
,
432 OCPP16BootNotificationResponse
433 >(OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
434 connectorId
: transactionConnectorId
,
435 status: OCPP16ChargePointStatus
.AVAILABLE
,
436 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
438 this.chargingStation
.getConnectorStatus(transactionConnectorId
).status =
439 OCPP16ChargePointStatus
.AVAILABLE
;
441 if (this.chargingStation
.stationInfo
.powerSharedByConnectors
) {
442 this.chargingStation
.stationInfo
.powerDivider
--;
445 this.chargingStation
.logPrefix() +
447 requestPayload
.transactionId
.toString() +
449 this.chargingStation
.stationInfo
.chargingStationId
+
451 transactionConnectorId
.toString()
453 this.chargingStation
.resetConnectorStatus(transactionConnectorId
);
456 this.chargingStation
.logPrefix() +
457 ' Stopping transaction id ' +
458 requestPayload
.transactionId
.toString() +
459 " REJECTED with status '" +
460 payload
.idTagInfo
?.status +
466 // eslint-disable-next-line @typescript-eslint/no-empty-function
467 private handleResponseStatusNotification(): void {}
469 // eslint-disable-next-line @typescript-eslint/no-empty-function
470 private handleResponseMeterValues(): void {}