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
,
14 OCPP16HeartbeatRequest
,
16 OCPP16StatusNotificationRequest
,
17 } from
'../../../types/ocpp/1.6/Requests';
19 OCPP16BootNotificationResponse
,
20 OCPP16HeartbeatResponse
,
21 OCPP16RegistrationStatus
,
22 OCPP16StatusNotificationResponse
,
23 } from
'../../../types/ocpp/1.6/Responses';
25 OCPP16MeterValuesRequest
,
26 OCPP16MeterValuesResponse
,
27 } from
'../../../types/ocpp/1.6/MeterValues';
29 import type ChargingStation from
'../../ChargingStation';
30 import { ErrorType
} from
'../../../types/ocpp/ErrorType';
31 import { JsonType
} from
'../../../types/JsonType';
32 import { OCPP16ChargePointErrorCode
} from
'../../../types/ocpp/1.6/ChargePointErrorCode';
33 import { OCPP16ChargePointStatus
} from
'../../../types/ocpp/1.6/ChargePointStatus';
34 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
35 import { OCPP16StandardParametersKey
} from
'../../../types/ocpp/1.6/Configuration';
36 import OCPPError from
'../../../exception/OCPPError';
37 import OCPPResponseService from
'../OCPPResponseService';
38 import { ResponseHandler
} from
'../../../types/ocpp/Responses';
39 import Utils from
'../../../utils/Utils';
40 import logger from
'../../../utils/Logger';
42 const moduleName
= 'OCPP16ResponseService';
44 export default class OCPP16ResponseService
extends OCPPResponseService
{
45 private responseHandlers
: Map
<OCPP16RequestCommand
, ResponseHandler
>;
47 public constructor(chargingStation
: ChargingStation
) {
48 if (new.target
?.name
=== moduleName
) {
49 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
51 super(chargingStation
);
52 this.responseHandlers
= new Map
<OCPP16RequestCommand
, ResponseHandler
>([
53 [OCPP16RequestCommand
.BOOT_NOTIFICATION
, this.handleResponseBootNotification
.bind(this)],
54 [OCPP16RequestCommand
.HEARTBEAT
, this.handleResponseHeartbeat
.bind(this)],
55 [OCPP16RequestCommand
.AUTHORIZE
, this.handleResponseAuthorize
.bind(this)],
56 [OCPP16RequestCommand
.START_TRANSACTION
, this.handleResponseStartTransaction
.bind(this)],
57 [OCPP16RequestCommand
.STOP_TRANSACTION
, this.handleResponseStopTransaction
.bind(this)],
58 [OCPP16RequestCommand
.STATUS_NOTIFICATION
, this.handleResponseStatusNotification
.bind(this)],
59 [OCPP16RequestCommand
.METER_VALUES
, this.handleResponseMeterValues
.bind(this)],
63 public async handleResponse(
64 commandName
: OCPP16RequestCommand
,
65 payload
: JsonType
| string,
66 requestPayload
: JsonType
69 this.chargingStation
.isRegistered() ||
70 commandName
=== OCPP16RequestCommand
.BOOT_NOTIFICATION
72 if (this.responseHandlers
.has(commandName
)) {
74 await this.responseHandlers
.get(commandName
)(payload
, requestPayload
);
77 this.chargingStation
.logPrefix() + ' Handle request response error: %j',
85 ErrorType
.NOT_IMPLEMENTED
,
86 `${commandName} is not implemented to handle request response payload ${JSON.stringify(
96 ErrorType
.SECURITY_ERROR
,
97 `${commandName} cannot be issued to handle request response payload ${JSON.stringify(
101 )} while the charging station is not registered on the central server. `,
107 private handleResponseBootNotification(payload
: OCPP16BootNotificationResponse
): void {
108 if (payload
.status === OCPP16RegistrationStatus
.ACCEPTED
) {
109 this.chargingStation
.addConfigurationKey(
110 OCPP16StandardParametersKey
.HeartBeatInterval
,
111 payload
.interval
.toString(),
113 { overwrite
: true, save
: true }
115 this.chargingStation
.addConfigurationKey(
116 OCPP16StandardParametersKey
.HeartbeatInterval
,
117 payload
.interval
.toString(),
119 { overwrite
: true, save
: true }
121 this.chargingStation
.heartbeatSetInterval
122 ? this.chargingStation
.restartHeartbeat()
123 : this.chargingStation
.startHeartbeat();
125 if (Object.values(OCPP16RegistrationStatus
).includes(payload
.status)) {
126 const logMsg
= `${this.chargingStation.logPrefix()} Charging station in '${
128 }' state on the central server`;
129 payload
.status === OCPP16RegistrationStatus
.REJECTED
130 ? logger
.warn(logMsg
)
131 : logger
.info(logMsg
);
134 this.chargingStation
.logPrefix() +
135 ' Charging station boot notification response received: %j with undefined registration status',
141 // eslint-disable-next-line @typescript-eslint/no-empty-function
142 private handleResponseHeartbeat(): void {}
144 private handleResponseAuthorize(
145 payload
: OCPP16AuthorizeResponse
,
146 requestPayload
: OCPP16AuthorizeRequest
148 let authorizeConnectorId
: number;
149 for (const connectorId
of this.chargingStation
.connectors
.keys()) {
152 this.chargingStation
.getConnectorStatus(connectorId
)?.authorizeIdTag
===
155 authorizeConnectorId
= connectorId
;
159 if (payload
.idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
160 this.chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= true;
162 `${this.chargingStation.logPrefix()} IdTag ${
164 } authorized on connector ${authorizeConnectorId}`
167 this.chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= false;
168 delete this.chargingStation
.getConnectorStatus(authorizeConnectorId
).authorizeIdTag
;
170 `${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status '${
171 payload.idTagInfo.status
172 }' on connector ${authorizeConnectorId}`
177 private async handleResponseStartTransaction(
178 payload
: OCPP16StartTransactionResponse
,
179 requestPayload
: OCPP16StartTransactionRequest
181 const connectorId
= requestPayload
.connectorId
;
183 let transactionConnectorId
: number;
184 for (const id
of this.chargingStation
.connectors
.keys()) {
185 if (id
> 0 && id
=== connectorId
) {
186 transactionConnectorId
= id
;
190 if (!transactionConnectorId
) {
192 this.chargingStation
.logPrefix() +
193 ' Trying to start a transaction on a non existing connector Id ' +
194 connectorId
.toString()
199 this.chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
200 this.chargingStation
.getAuthorizeRemoteTxRequests() &&
201 this.chargingStation
.getLocalAuthListEnabled() &&
202 this.chargingStation
.hasAuthorizedTags() &&
203 !this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
206 this.chargingStation
.logPrefix() +
207 ' Trying to start a transaction with a not local authorized idTag ' +
208 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
209 ' on connector Id ' +
210 connectorId
.toString()
212 await this.resetConnectorOnStartTransactionError(connectorId
);
216 this.chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
217 this.chargingStation
.getAuthorizeRemoteTxRequests() &&
218 this.chargingStation
.getMayAuthorizeAtRemoteStart() &&
219 !this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
220 !this.chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
223 this.chargingStation
.logPrefix() +
224 ' Trying to start a transaction with a not authorized idTag ' +
225 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
226 ' on connector Id ' +
227 connectorId
.toString()
229 await this.resetConnectorOnStartTransactionError(connectorId
);
233 this.chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
&&
234 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
!== requestPayload
.idTag
237 this.chargingStation
.logPrefix() +
238 ' Trying to start a transaction with an idTag ' +
239 requestPayload
.idTag
+
240 ' different from the authorize request one ' +
241 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
242 ' on connector Id ' +
243 connectorId
.toString()
245 await this.resetConnectorOnStartTransactionError(connectorId
);
249 this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
250 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
!==
254 this.chargingStation
.logPrefix() +
255 ' Trying to start a transaction with an idTag ' +
256 requestPayload
.idTag
+
257 ' different from the local authorized one ' +
258 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
259 ' on connector Id ' +
260 connectorId
.toString()
262 await this.resetConnectorOnStartTransactionError(connectorId
);
265 if (this.chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
) {
267 this.chargingStation
.logPrefix() +
268 ' Trying to start a transaction on an already used connector ' +
269 connectorId
.toString() +
271 this.chargingStation
.getConnectorStatus(connectorId
)
276 this.chargingStation
.getConnectorStatus(connectorId
)?.status !==
277 OCPP16ChargePointStatus
.AVAILABLE
&&
278 this.chargingStation
.getConnectorStatus(connectorId
)?.status !==
279 OCPP16ChargePointStatus
.PREPARING
282 `${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
283 this.chargingStation.getConnectorStatus(connectorId)?.status
288 if (!Number.isInteger(payload
.transactionId
)) {
290 `${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${
291 payload.transactionId
292 }, converting to integer`
294 payload
.transactionId
= Utils
.convertToInt(payload
.transactionId
);
297 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
298 this.chargingStation
.getConnectorStatus(connectorId
).transactionStarted
= true;
299 this.chargingStation
.getConnectorStatus(connectorId
).transactionId
= payload
.transactionId
;
300 this.chargingStation
.getConnectorStatus(connectorId
).transactionIdTag
= requestPayload
.idTag
;
301 this.chargingStation
.getConnectorStatus(
303 ).transactionEnergyActiveImportRegisterValue
= 0;
304 this.chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
=
305 OCPP16ServiceUtils
.buildTransactionBeginMeterValue(
306 this.chargingStation
,
308 requestPayload
.meterStart
310 this.chargingStation
.getBeginEndMeterValues() &&
311 (await this.chargingStation
.ocppRequestService
.sendMessageHandler
<
312 OCPP16MeterValuesRequest
,
313 OCPP16MeterValuesResponse
314 >(OCPP16RequestCommand
.METER_VALUES
, {
316 transactionId
: payload
.transactionId
,
318 this.chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
,
320 await this.chargingStation
.ocppRequestService
.sendMessageHandler
<
321 OCPP16StatusNotificationRequest
,
322 OCPP16StatusNotificationResponse
323 >(OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
325 status: OCPP16ChargePointStatus
.CHARGING
,
326 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
328 this.chargingStation
.getConnectorStatus(connectorId
).status =
329 OCPP16ChargePointStatus
.CHARGING
;
331 this.chargingStation
.logPrefix() +
333 payload
.transactionId
.toString() +
335 this.chargingStation
.stationInfo
.chargingStationId
+
337 connectorId
.toString() +
341 if (this.chargingStation
.stationInfo
.powerSharedByConnectors
) {
342 this.chargingStation
.stationInfo
.powerDivider
++;
344 const configuredMeterValueSampleInterval
= this.chargingStation
.getConfigurationKey(
345 OCPP16StandardParametersKey
.MeterValueSampleInterval
347 this.chargingStation
.startMeterValues(
349 configuredMeterValueSampleInterval
350 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
355 this.chargingStation
.logPrefix() +
356 ' Starting transaction id ' +
357 payload
.transactionId
.toString() +
358 " REJECTED with status '" +
359 payload
?.idTagInfo
?.status +
363 await this.resetConnectorOnStartTransactionError(connectorId
);
367 private async resetConnectorOnStartTransactionError(connectorId
: number): Promise
<void> {
368 this.chargingStation
.resetConnectorStatus(connectorId
);
370 this.chargingStation
.getConnectorStatus(connectorId
).status !==
371 OCPP16ChargePointStatus
.AVAILABLE
373 await this.chargingStation
.ocppRequestService
.sendMessageHandler
<
374 OCPP16StatusNotificationRequest
,
375 OCPP16StatusNotificationResponse
376 >(OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
378 status: OCPP16ChargePointStatus
.AVAILABLE
,
379 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
381 this.chargingStation
.getConnectorStatus(connectorId
).status =
382 OCPP16ChargePointStatus
.AVAILABLE
;
386 private async handleResponseStopTransaction(
387 payload
: OCPP16StopTransactionResponse
,
388 requestPayload
: OCPP16StopTransactionRequest
390 const transactionConnectorId
= this.chargingStation
.getConnectorIdByTransactionId(
391 requestPayload
.transactionId
393 if (!transactionConnectorId
) {
395 this.chargingStation
.logPrefix() +
396 ' Trying to stop a non existing transaction ' +
397 requestPayload
.transactionId
.toString()
401 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
402 this.chargingStation
.getBeginEndMeterValues() &&
403 !this.chargingStation
.getOcppStrictCompliance() &&
404 this.chargingStation
.getOutOfOrderEndMeterValues() &&
405 (await this.chargingStation
.ocppRequestService
.sendMessageHandler
<
406 OCPP16MeterValuesRequest
,
407 OCPP16MeterValuesResponse
408 >(OCPP16RequestCommand
.METER_VALUES
, {
409 connectorId
: transactionConnectorId
,
410 transactionId
: requestPayload
.transactionId
,
411 meterValue
: OCPP16ServiceUtils
.buildTransactionEndMeterValue(
412 this.chargingStation
,
413 transactionConnectorId
,
414 requestPayload
.meterStop
418 !this.chargingStation
.isChargingStationAvailable() ||
419 !this.chargingStation
.isConnectorAvailable(transactionConnectorId
)
421 await this.chargingStation
.ocppRequestService
.sendMessageHandler
<
422 OCPP16StatusNotificationRequest
,
423 OCPP16StatusNotificationResponse
424 >(OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
425 connectorId
: transactionConnectorId
,
426 status: OCPP16ChargePointStatus
.UNAVAILABLE
,
427 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
429 this.chargingStation
.getConnectorStatus(transactionConnectorId
).status =
430 OCPP16ChargePointStatus
.UNAVAILABLE
;
432 await this.chargingStation
.ocppRequestService
.sendMessageHandler
<
433 OCPP16BootNotificationRequest
,
434 OCPP16BootNotificationResponse
435 >(OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
436 connectorId
: transactionConnectorId
,
437 status: OCPP16ChargePointStatus
.AVAILABLE
,
438 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
440 this.chargingStation
.getConnectorStatus(transactionConnectorId
).status =
441 OCPP16ChargePointStatus
.AVAILABLE
;
443 if (this.chargingStation
.stationInfo
.powerSharedByConnectors
) {
444 this.chargingStation
.stationInfo
.powerDivider
--;
447 this.chargingStation
.logPrefix() +
449 requestPayload
.transactionId
.toString() +
451 this.chargingStation
.stationInfo
.chargingStationId
+
453 transactionConnectorId
.toString()
455 this.chargingStation
.resetConnectorStatus(transactionConnectorId
);
458 this.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 {}