1 // Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
5 OCPP16AuthorizationStatus
,
6 OCPP16AuthorizeResponse
,
7 OCPP16StartTransactionResponse
,
8 OCPP16StopTransactionResponse
,
9 StartTransactionRequest
,
10 StopTransactionRequest
,
11 } from
'../../../types/ocpp/1.6/Transaction';
15 StatusNotificationRequest
,
16 } from
'../../../types/ocpp/1.6/Requests';
19 OCPP16BootNotificationResponse
,
20 OCPP16RegistrationStatus
,
21 StatusNotificationResponse
,
22 } from
'../../../types/ocpp/1.6/Responses';
23 import { MeterValuesRequest
, MeterValuesResponse
} from
'../../../types/ocpp/1.6/MeterValues';
25 import type ChargingStation from
'../../ChargingStation';
26 import { ErrorType
} from
'../../../types/ocpp/ErrorType';
27 import { JsonType
} from
'../../../types/JsonType';
28 import { OCPP16ChargePointErrorCode
} from
'../../../types/ocpp/1.6/ChargePointErrorCode';
29 import { OCPP16ChargePointStatus
} from
'../../../types/ocpp/1.6/ChargePointStatus';
30 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
31 import { OCPP16StandardParametersKey
} from
'../../../types/ocpp/1.6/Configuration';
32 import OCPPError from
'../../../exception/OCPPError';
33 import OCPPResponseService from
'../OCPPResponseService';
34 import { ResponseHandler
} from
'../../../types/ocpp/Responses';
35 import Utils from
'../../../utils/Utils';
36 import logger from
'../../../utils/Logger';
38 const moduleName
= 'OCPP16ResponseService';
40 export default class OCPP16ResponseService
extends OCPPResponseService
{
41 private responseHandlers
: Map
<OCPP16RequestCommand
, ResponseHandler
>;
43 public constructor(chargingStation
: ChargingStation
) {
44 if (new.target
?.name
=== moduleName
) {
45 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
47 super(chargingStation
);
48 this.responseHandlers
= new Map
<OCPP16RequestCommand
, ResponseHandler
>([
49 [OCPP16RequestCommand
.BOOT_NOTIFICATION
, this.handleResponseBootNotification
.bind(this)],
50 [OCPP16RequestCommand
.HEARTBEAT
, this.handleResponseHeartbeat
.bind(this)],
51 [OCPP16RequestCommand
.AUTHORIZE
, this.handleResponseAuthorize
.bind(this)],
52 [OCPP16RequestCommand
.START_TRANSACTION
, this.handleResponseStartTransaction
.bind(this)],
53 [OCPP16RequestCommand
.STOP_TRANSACTION
, this.handleResponseStopTransaction
.bind(this)],
54 [OCPP16RequestCommand
.STATUS_NOTIFICATION
, this.handleResponseStatusNotification
.bind(this)],
55 [OCPP16RequestCommand
.METER_VALUES
, this.handleResponseMeterValues
.bind(this)],
59 public async handleResponse(
60 commandName
: OCPP16RequestCommand
,
61 payload
: JsonType
| string,
62 requestPayload
: JsonType
65 this.chargingStation
.isRegistered() ||
66 commandName
=== OCPP16RequestCommand
.BOOT_NOTIFICATION
68 if (this.responseHandlers
.has(commandName
)) {
70 await this.responseHandlers
.get(commandName
)(payload
, requestPayload
);
73 this.chargingStation
.logPrefix() + ' Handle request response error: %j',
81 ErrorType
.NOT_IMPLEMENTED
,
82 `${commandName} is not implemented to handle request response payload ${JSON.stringify(
92 ErrorType
.SECURITY_ERROR
,
93 `${commandName} cannot be issued to handle request response payload ${JSON.stringify(
97 )} while the charging station is not registered on the central server. `,
103 private handleResponseBootNotification(payload
: OCPP16BootNotificationResponse
): void {
104 if (payload
.status === OCPP16RegistrationStatus
.ACCEPTED
) {
105 this.chargingStation
.addConfigurationKey(
106 OCPP16StandardParametersKey
.HeartBeatInterval
,
107 payload
.interval
.toString(),
109 { overwrite
: true, save
: true }
111 this.chargingStation
.addConfigurationKey(
112 OCPP16StandardParametersKey
.HeartbeatInterval
,
113 payload
.interval
.toString(),
115 { overwrite
: true, save
: true }
117 this.chargingStation
.heartbeatSetInterval
118 ? this.chargingStation
.restartHeartbeat()
119 : this.chargingStation
.startHeartbeat();
121 if (Object.values(OCPP16RegistrationStatus
).includes(payload
.status)) {
122 const logMsg
= `${this.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 this.chargingStation
.logPrefix() +
131 ' Charging station boot notification response received: %j with undefined registration status',
137 private handleResponseHeartbeat(
138 payload
: HeartbeatResponse
,
139 requestPayload
: HeartbeatRequest
142 this.chargingStation
.logPrefix() +
143 ' Heartbeat response received: %j to Heartbeat request: %j',
149 private handleResponseAuthorize(
150 payload
: OCPP16AuthorizeResponse
,
151 requestPayload
: AuthorizeRequest
153 let authorizeConnectorId
: number;
154 for (const connectorId
of this.chargingStation
.connectors
.keys()) {
157 this.chargingStation
.getConnectorStatus(connectorId
)?.authorizeIdTag
===
160 authorizeConnectorId
= connectorId
;
164 if (payload
.idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
165 this.chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= true;
167 `${this.chargingStation.logPrefix()} IdTag ${
169 } authorized on connector ${authorizeConnectorId}`
172 this.chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= false;
173 delete this.chargingStation
.getConnectorStatus(authorizeConnectorId
).authorizeIdTag
;
175 `${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status ${
176 payload.idTagInfo.status
177 } on connector ${authorizeConnectorId}`
182 private async handleResponseStartTransaction(
183 payload
: OCPP16StartTransactionResponse
,
184 requestPayload
: StartTransactionRequest
186 const connectorId
= requestPayload
.connectorId
;
188 let transactionConnectorId
: number;
189 for (const id
of this.chargingStation
.connectors
.keys()) {
190 if (id
> 0 && id
=== connectorId
) {
191 transactionConnectorId
= id
;
195 if (!transactionConnectorId
) {
197 this.chargingStation
.logPrefix() +
198 ' Trying to start a transaction on a non existing connector Id ' +
199 connectorId
.toString()
204 this.chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
205 this.chargingStation
.getAuthorizeRemoteTxRequests() &&
206 this.chargingStation
.getLocalAuthListEnabled() &&
207 this.chargingStation
.hasAuthorizedTags() &&
208 !this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
211 this.chargingStation
.logPrefix() +
212 ' Trying to start a transaction with a not local authorized idTag ' +
213 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
214 ' on connector Id ' +
215 connectorId
.toString()
217 await this.resetConnectorOnStartTransactionError(connectorId
);
221 this.chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
222 this.chargingStation
.getAuthorizeRemoteTxRequests() &&
223 this.chargingStation
.getMayAuthorizeAtRemoteStart() &&
224 !this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
225 !this.chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
228 this.chargingStation
.logPrefix() +
229 ' Trying to start a transaction with a not authorized idTag ' +
230 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
231 ' on connector Id ' +
232 connectorId
.toString()
234 await this.resetConnectorOnStartTransactionError(connectorId
);
238 this.chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
&&
239 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
!== requestPayload
.idTag
242 this.chargingStation
.logPrefix() +
243 ' Trying to start a transaction with an idTag ' +
244 requestPayload
.idTag
+
245 ' different from the authorize request one ' +
246 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
247 ' on connector Id ' +
248 connectorId
.toString()
250 await this.resetConnectorOnStartTransactionError(connectorId
);
254 this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
255 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
!==
259 this.chargingStation
.logPrefix() +
260 ' Trying to start a transaction with an idTag ' +
261 requestPayload
.idTag
+
262 ' different from the local authorized one ' +
263 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
264 ' on connector Id ' +
265 connectorId
.toString()
267 await this.resetConnectorOnStartTransactionError(connectorId
);
270 if (this.chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
) {
272 this.chargingStation
.logPrefix() +
273 ' Trying to start a transaction on an already used connector ' +
274 connectorId
.toString() +
276 this.chargingStation
.getConnectorStatus(connectorId
)
281 this.chargingStation
.getConnectorStatus(connectorId
)?.status !==
282 OCPP16ChargePointStatus
.AVAILABLE
&&
283 this.chargingStation
.getConnectorStatus(connectorId
)?.status !==
284 OCPP16ChargePointStatus
.PREPARING
287 `${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
288 this.chargingStation.getConnectorStatus(connectorId)?.status
293 if (!Number.isInteger(payload
.transactionId
)) {
295 `${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${
296 payload.transactionId
297 }, converting to integer`
299 payload
.transactionId
= Utils
.convertToInt(payload
.transactionId
);
302 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
303 this.chargingStation
.getConnectorStatus(connectorId
).transactionStarted
= true;
304 this.chargingStation
.getConnectorStatus(connectorId
).transactionId
= payload
.transactionId
;
305 this.chargingStation
.getConnectorStatus(connectorId
).transactionIdTag
= requestPayload
.idTag
;
306 this.chargingStation
.getConnectorStatus(
308 ).transactionEnergyActiveImportRegisterValue
= 0;
309 this.chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
=
310 OCPP16ServiceUtils
.buildTransactionBeginMeterValue(
311 this.chargingStation
,
313 requestPayload
.meterStart
315 this.chargingStation
.getBeginEndMeterValues() &&
316 (await this.chargingStation
.ocppRequestService
.sendMessageHandler(
317 OCPP16RequestCommand
.METER_VALUES
,
320 transactionId
: payload
.transactionId
,
322 this.chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
,
325 await this.chargingStation
.ocppRequestService
.sendMessageHandler(
326 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
329 status: OCPP16ChargePointStatus
.CHARGING
,
330 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
333 this.chargingStation
.getConnectorStatus(connectorId
).status =
334 OCPP16ChargePointStatus
.CHARGING
;
336 this.chargingStation
.logPrefix() +
338 payload
.transactionId
.toString() +
340 this.chargingStation
.stationInfo
.chargingStationId
+
342 connectorId
.toString() +
346 if (this.chargingStation
.stationInfo
.powerSharedByConnectors
) {
347 this.chargingStation
.stationInfo
.powerDivider
++;
349 const configuredMeterValueSampleInterval
= this.chargingStation
.getConfigurationKey(
350 OCPP16StandardParametersKey
.MeterValueSampleInterval
352 this.chargingStation
.startMeterValues(
354 configuredMeterValueSampleInterval
355 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
360 this.chargingStation
.logPrefix() +
361 ' Starting transaction id ' +
362 payload
.transactionId
.toString() +
363 ' REJECTED with status ' +
364 payload
?.idTagInfo
?.status +
368 await this.resetConnectorOnStartTransactionError(connectorId
);
372 private async resetConnectorOnStartTransactionError(connectorId
: number): Promise
<void> {
373 this.chargingStation
.resetConnectorStatus(connectorId
);
375 this.chargingStation
.getConnectorStatus(connectorId
).status !==
376 OCPP16ChargePointStatus
.AVAILABLE
378 await this.chargingStation
.ocppRequestService
.sendMessageHandler(
379 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
382 status: OCPP16ChargePointStatus
.AVAILABLE
,
383 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
386 this.chargingStation
.getConnectorStatus(connectorId
).status =
387 OCPP16ChargePointStatus
.AVAILABLE
;
391 private async handleResponseStopTransaction(
392 payload
: OCPP16StopTransactionResponse
,
393 requestPayload
: StopTransactionRequest
395 const transactionConnectorId
= this.chargingStation
.getConnectorIdByTransactionId(
396 requestPayload
.transactionId
398 if (!transactionConnectorId
) {
400 this.chargingStation
.logPrefix() +
401 ' Trying to stop a non existing transaction ' +
402 requestPayload
.transactionId
.toString()
406 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
407 this.chargingStation
.getBeginEndMeterValues() &&
408 !this.chargingStation
.getOcppStrictCompliance() &&
409 this.chargingStation
.getOutOfOrderEndMeterValues() &&
410 (await this.chargingStation
.ocppRequestService
.sendMessageHandler(
411 OCPP16RequestCommand
.METER_VALUES
,
413 connectorId
: transactionConnectorId
,
414 transactionId
: requestPayload
.transactionId
,
415 meterValue
: OCPP16ServiceUtils
.buildTransactionEndMeterValue(
416 this.chargingStation
,
417 transactionConnectorId
,
418 requestPayload
.meterStop
423 !this.chargingStation
.isChargingStationAvailable() ||
424 !this.chargingStation
.isConnectorAvailable(transactionConnectorId
)
426 await this.chargingStation
.ocppRequestService
.sendMessageHandler(
427 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
429 connectorId
: transactionConnectorId
,
430 status: OCPP16ChargePointStatus
.UNAVAILABLE
,
431 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
434 this.chargingStation
.getConnectorStatus(transactionConnectorId
).status =
435 OCPP16ChargePointStatus
.UNAVAILABLE
;
437 await this.chargingStation
.ocppRequestService
.sendMessageHandler(
438 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
440 connectorId
: transactionConnectorId
,
441 status: OCPP16ChargePointStatus
.AVAILABLE
,
442 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
445 this.chargingStation
.getConnectorStatus(transactionConnectorId
).status =
446 OCPP16ChargePointStatus
.AVAILABLE
;
448 if (this.chargingStation
.stationInfo
.powerSharedByConnectors
) {
449 this.chargingStation
.stationInfo
.powerDivider
--;
452 this.chargingStation
.logPrefix() +
454 requestPayload
.transactionId
.toString() +
456 this.chargingStation
.stationInfo
.chargingStationId
+
458 transactionConnectorId
.toString()
460 this.chargingStation
.resetConnectorStatus(transactionConnectorId
);
463 this.chargingStation
.logPrefix() +
464 ' Stopping transaction id ' +
465 requestPayload
.transactionId
.toString() +
466 ' REJECTED with status ' +
467 payload
.idTagInfo
?.status
472 private handleResponseStatusNotification(
473 payload
: StatusNotificationRequest
,
474 requestPayload
: StatusNotificationResponse
477 this.chargingStation
.logPrefix() +
478 ' Status notification response received: %j to StatusNotification request: %j',
484 private handleResponseMeterValues(
485 payload
: MeterValuesRequest
,
486 requestPayload
: MeterValuesResponse
489 this.chargingStation
.logPrefix() +
490 ' MeterValues response received: %j to MeterValues request: %j',