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 { ChargingStationUtils
} from
'../../ChargingStationUtils';
38 import OCPPResponseService from
'../OCPPResponseService';
39 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
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
) {
70 this.responseHandlers
.has(commandName
) &&
71 ChargingStationUtils
.isCommandSupported(commandName
, chargingStation
.stationInfo
)
74 await this.responseHandlers
.get(commandName
)(chargingStation
, payload
, requestPayload
);
76 logger
.error(chargingStation
.logPrefix() + ' Handle request response error: %j', error
);
82 ErrorType
.NOT_IMPLEMENTED
,
83 `${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. `,
106 private handleResponseBootNotification(
107 chargingStation
: ChargingStation
,
108 payload
: OCPP16BootNotificationResponse
110 if (payload
.status === OCPP16RegistrationStatus
.ACCEPTED
) {
111 ChargingStationConfigurationUtils
.addConfigurationKey(
113 OCPP16StandardParametersKey
.HeartbeatInterval
,
114 payload
.interval
.toString(),
116 { overwrite
: true, save
: true }
118 ChargingStationConfigurationUtils
.addConfigurationKey(
120 OCPP16StandardParametersKey
.HeartBeatInterval
,
121 payload
.interval
.toString(),
123 { overwrite
: true, save
: true }
125 chargingStation
.heartbeatSetInterval
126 ? chargingStation
.restartHeartbeat()
127 : chargingStation
.startHeartbeat();
129 if (Object.values(OCPP16RegistrationStatus
).includes(payload
.status)) {
130 const logMsg
= `${chargingStation.logPrefix()} Charging station in '${
132 }' state on the central server`;
133 payload
.status === OCPP16RegistrationStatus
.REJECTED
134 ? logger
.warn(logMsg
)
135 : logger
.info(logMsg
);
138 chargingStation
.logPrefix() +
139 ' Charging station boot notification response received: %j with undefined registration status',
145 // eslint-disable-next-line @typescript-eslint/no-empty-function
146 private handleResponseHeartbeat(): void {}
148 private handleResponseAuthorize(
149 chargingStation
: ChargingStation
,
150 payload
: OCPP16AuthorizeResponse
,
151 requestPayload
: OCPP16AuthorizeRequest
153 let authorizeConnectorId
: number;
154 for (const connectorId
of chargingStation
.connectors
.keys()) {
157 chargingStation
.getConnectorStatus(connectorId
)?.authorizeIdTag
=== requestPayload
.idTag
159 authorizeConnectorId
= connectorId
;
163 if (payload
.idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
164 chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= true;
166 `${chargingStation.logPrefix()} IdTag ${
168 } authorized on connector ${authorizeConnectorId}`
171 chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= false;
172 delete chargingStation
.getConnectorStatus(authorizeConnectorId
).authorizeIdTag
;
174 `${chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status '${
175 payload.idTagInfo.status
176 }' on connector ${authorizeConnectorId}`
181 private async handleResponseStartTransaction(
182 chargingStation
: ChargingStation
,
183 payload
: OCPP16StartTransactionResponse
,
184 requestPayload
: OCPP16StartTransactionRequest
186 const connectorId
= requestPayload
.connectorId
;
188 let transactionConnectorId
: number;
189 for (const id
of chargingStation
.connectors
.keys()) {
190 if (id
> 0 && id
=== connectorId
) {
191 transactionConnectorId
= id
;
195 if (!transactionConnectorId
) {
197 chargingStation
.logPrefix() +
198 ' Trying to start a transaction on a non existing connector Id ' +
199 connectorId
.toString()
204 chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
205 chargingStation
.getAuthorizeRemoteTxRequests() &&
206 chargingStation
.getLocalAuthListEnabled() &&
207 chargingStation
.hasAuthorizedTags() &&
208 !chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
211 chargingStation
.logPrefix() +
212 ' Trying to start a transaction with a not local authorized idTag ' +
213 chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
214 ' on connector Id ' +
215 connectorId
.toString()
217 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
221 chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
222 chargingStation
.getAuthorizeRemoteTxRequests() &&
223 chargingStation
.getMayAuthorizeAtRemoteStart() &&
224 !chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
225 !chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
228 chargingStation
.logPrefix() +
229 ' Trying to start a transaction with a not authorized idTag ' +
230 chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
231 ' on connector Id ' +
232 connectorId
.toString()
234 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
238 chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
&&
239 chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
!== requestPayload
.idTag
242 chargingStation
.logPrefix() +
243 ' Trying to start a transaction with an idTag ' +
244 requestPayload
.idTag
+
245 ' different from the authorize request one ' +
246 chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
247 ' on connector Id ' +
248 connectorId
.toString()
250 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
254 chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
255 chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
!== requestPayload
.idTag
258 chargingStation
.logPrefix() +
259 ' Trying to start a transaction with an idTag ' +
260 requestPayload
.idTag
+
261 ' different from the local authorized one ' +
262 chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
263 ' on connector Id ' +
264 connectorId
.toString()
266 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
269 if (chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
) {
271 chargingStation
.logPrefix() +
272 ' Trying to start a transaction on an already used connector ' +
273 connectorId
.toString() +
275 chargingStation
.getConnectorStatus(connectorId
)
280 chargingStation
.getConnectorStatus(connectorId
)?.status !==
281 OCPP16ChargePointStatus
.AVAILABLE
&&
282 chargingStation
.getConnectorStatus(connectorId
)?.status !== OCPP16ChargePointStatus
.PREPARING
285 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
286 chargingStation.getConnectorStatus(connectorId)?.status
291 if (!Number.isInteger(payload
.transactionId
)) {
293 `${chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${
294 payload.transactionId
295 }, converting to integer`
297 payload
.transactionId
= Utils
.convertToInt(payload
.transactionId
);
300 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
301 chargingStation
.getConnectorStatus(connectorId
).transactionStarted
= true;
302 chargingStation
.getConnectorStatus(connectorId
).transactionId
= payload
.transactionId
;
303 chargingStation
.getConnectorStatus(connectorId
).transactionIdTag
= requestPayload
.idTag
;
304 chargingStation
.getConnectorStatus(
306 ).transactionEnergyActiveImportRegisterValue
= 0;
307 chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
=
308 OCPP16ServiceUtils
.buildTransactionBeginMeterValue(
311 requestPayload
.meterStart
313 chargingStation
.getBeginEndMeterValues() &&
314 (await chargingStation
.ocppRequestService
.requestHandler
<
315 OCPP16MeterValuesRequest
,
316 OCPP16MeterValuesResponse
317 >(chargingStation
, OCPP16RequestCommand
.METER_VALUES
, {
319 transactionId
: payload
.transactionId
,
320 meterValue
: [chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
],
322 await chargingStation
.ocppRequestService
.requestHandler
<
323 OCPP16StatusNotificationRequest
,
324 OCPP16StatusNotificationResponse
325 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
327 status: OCPP16ChargePointStatus
.CHARGING
,
328 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
330 chargingStation
.getConnectorStatus(connectorId
).status = OCPP16ChargePointStatus
.CHARGING
;
332 chargingStation
.logPrefix() +
334 payload
.transactionId
.toString() +
336 chargingStation
.stationInfo
.chargingStationId
+
338 connectorId
.toString() +
342 if (chargingStation
.stationInfo
.powerSharedByConnectors
) {
343 chargingStation
.powerDivider
++;
345 const configuredMeterValueSampleInterval
=
346 ChargingStationConfigurationUtils
.getConfigurationKey(
348 OCPP16StandardParametersKey
.MeterValueSampleInterval
350 chargingStation
.startMeterValues(
352 configuredMeterValueSampleInterval
353 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
358 chargingStation
.logPrefix() +
359 ' Starting transaction id ' +
360 payload
.transactionId
.toString() +
361 " REJECTED with status '" +
362 payload
?.idTagInfo
?.status +
366 await this.resetConnectorOnStartTransactionError(chargingStation
, connectorId
);
370 private async resetConnectorOnStartTransactionError(
371 chargingStation
: ChargingStation
,
374 chargingStation
.resetConnectorStatus(connectorId
);
376 chargingStation
.getConnectorStatus(connectorId
).status !== OCPP16ChargePointStatus
.AVAILABLE
378 await chargingStation
.ocppRequestService
.requestHandler
<
379 OCPP16StatusNotificationRequest
,
380 OCPP16StatusNotificationResponse
381 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
383 status: OCPP16ChargePointStatus
.AVAILABLE
,
384 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
386 chargingStation
.getConnectorStatus(connectorId
).status = OCPP16ChargePointStatus
.AVAILABLE
;
390 private async handleResponseStopTransaction(
391 chargingStation
: ChargingStation
,
392 payload
: OCPP16StopTransactionResponse
,
393 requestPayload
: OCPP16StopTransactionRequest
395 const transactionConnectorId
= chargingStation
.getConnectorIdByTransactionId(
396 requestPayload
.transactionId
398 if (!transactionConnectorId
) {
400 chargingStation
.logPrefix() +
401 ' Trying to stop a non existing transaction ' +
402 requestPayload
.transactionId
.toString()
406 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
407 chargingStation
.getBeginEndMeterValues() &&
408 !chargingStation
.getOcppStrictCompliance() &&
409 chargingStation
.getOutOfOrderEndMeterValues() &&
410 (await chargingStation
.ocppRequestService
.requestHandler
<
411 OCPP16MeterValuesRequest
,
412 OCPP16MeterValuesResponse
413 >(chargingStation
, OCPP16RequestCommand
.METER_VALUES
, {
414 connectorId
: transactionConnectorId
,
415 transactionId
: requestPayload
.transactionId
,
417 OCPP16ServiceUtils
.buildTransactionEndMeterValue(
419 transactionConnectorId
,
420 requestPayload
.meterStop
425 !chargingStation
.isChargingStationAvailable() ||
426 !chargingStation
.isConnectorAvailable(transactionConnectorId
)
428 await chargingStation
.ocppRequestService
.requestHandler
<
429 OCPP16StatusNotificationRequest
,
430 OCPP16StatusNotificationResponse
431 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
432 connectorId
: transactionConnectorId
,
433 status: OCPP16ChargePointStatus
.UNAVAILABLE
,
434 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
436 chargingStation
.getConnectorStatus(transactionConnectorId
).status =
437 OCPP16ChargePointStatus
.UNAVAILABLE
;
439 await chargingStation
.ocppRequestService
.requestHandler
<
440 OCPP16BootNotificationRequest
,
441 OCPP16BootNotificationResponse
442 >(chargingStation
, OCPP16RequestCommand
.STATUS_NOTIFICATION
, {
443 connectorId
: transactionConnectorId
,
444 status: OCPP16ChargePointStatus
.AVAILABLE
,
445 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
447 chargingStation
.getConnectorStatus(transactionConnectorId
).status =
448 OCPP16ChargePointStatus
.AVAILABLE
;
450 if (chargingStation
.stationInfo
.powerSharedByConnectors
) {
451 chargingStation
.powerDivider
--;
454 chargingStation
.logPrefix() +
456 requestPayload
.transactionId
.toString() +
458 chargingStation
.stationInfo
.chargingStationId
+
460 transactionConnectorId
.toString()
462 chargingStation
.resetConnectorStatus(transactionConnectorId
);
465 chargingStation
.logPrefix() +
466 ' Stopping transaction id ' +
467 requestPayload
.transactionId
.toString() +
468 " REJECTED with status '" +
469 payload
.idTagInfo
?.status +
475 // eslint-disable-next-line @typescript-eslint/no-empty-function
476 private handleResponseStatusNotification(): void {}
478 // eslint-disable-next-line @typescript-eslint/no-empty-function
479 private handleResponseMeterValues(): void {}