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 this.chargingStation
.addConfigurationKey(
110 OCPP16StandardParametersKey
.HeartbeatInterval
,
111 payload
.interval
.toString(),
114 this.chargingStation
.heartbeatSetInterval
115 ? this.chargingStation
.restartHeartbeat()
116 : this.chargingStation
.startHeartbeat();
118 if (Object.values(OCPP16RegistrationStatus
).includes(payload
.status)) {
119 const logMsg
= `${this.chargingStation.logPrefix()} Charging station in '${
121 }' state on the central server`;
122 payload
.status === OCPP16RegistrationStatus
.REJECTED
123 ? logger
.warn(logMsg
)
124 : logger
.info(logMsg
);
127 this.chargingStation
.logPrefix() +
128 ' Charging station boot notification response received: %j with undefined registration status',
134 private handleResponseHeartbeat(
135 payload
: HeartbeatResponse
,
136 requestPayload
: HeartbeatRequest
139 this.chargingStation
.logPrefix() +
140 ' Heartbeat response received: %j to Heartbeat request: %j',
146 private handleResponseAuthorize(
147 payload
: OCPP16AuthorizeResponse
,
148 requestPayload
: AuthorizeRequest
150 let authorizeConnectorId
: number;
151 for (const connectorId
of this.chargingStation
.connectors
.keys()) {
154 this.chargingStation
.getConnectorStatus(connectorId
)?.authorizeIdTag
===
157 authorizeConnectorId
= connectorId
;
161 if (payload
.idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
162 this.chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= true;
164 `${this.chargingStation.logPrefix()} IdTag ${
166 } authorized on connector ${authorizeConnectorId}`
169 this.chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= false;
170 delete this.chargingStation
.getConnectorStatus(authorizeConnectorId
).authorizeIdTag
;
172 `${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status ${
173 payload.idTagInfo.status
174 } on connector ${authorizeConnectorId}`
179 private async handleResponseStartTransaction(
180 payload
: OCPP16StartTransactionResponse
,
181 requestPayload
: StartTransactionRequest
183 const connectorId
= requestPayload
.connectorId
;
185 let transactionConnectorId
: number;
186 for (const id
of this.chargingStation
.connectors
.keys()) {
187 if (id
> 0 && id
=== connectorId
) {
188 transactionConnectorId
= id
;
192 if (!transactionConnectorId
) {
194 this.chargingStation
.logPrefix() +
195 ' Trying to start a transaction on a non existing connector Id ' +
196 connectorId
.toString()
201 this.chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
202 this.chargingStation
.getAuthorizeRemoteTxRequests() &&
203 this.chargingStation
.getLocalAuthListEnabled() &&
204 this.chargingStation
.hasAuthorizedTags() &&
205 !this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
208 this.chargingStation
.logPrefix() +
209 ' Trying to start a transaction with a not local authorized idTag ' +
210 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
211 ' on connector Id ' +
212 connectorId
.toString()
214 await this.resetConnectorOnStartTransactionError(connectorId
);
218 this.chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
219 this.chargingStation
.getAuthorizeRemoteTxRequests() &&
220 this.chargingStation
.getMayAuthorizeAtRemoteStart() &&
221 !this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
222 !this.chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
225 this.chargingStation
.logPrefix() +
226 ' Trying to start a transaction with a not authorized idTag ' +
227 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
228 ' on connector Id ' +
229 connectorId
.toString()
231 await this.resetConnectorOnStartTransactionError(connectorId
);
235 this.chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
&&
236 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
!== requestPayload
.idTag
239 this.chargingStation
.logPrefix() +
240 ' Trying to start a transaction with an idTag ' +
241 requestPayload
.idTag
+
242 ' different from the authorize request one ' +
243 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
244 ' on connector Id ' +
245 connectorId
.toString()
247 await this.resetConnectorOnStartTransactionError(connectorId
);
251 this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
252 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
!==
256 this.chargingStation
.logPrefix() +
257 ' Trying to start a transaction with an idTag ' +
258 requestPayload
.idTag
+
259 ' different from the local authorized one ' +
260 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
261 ' on connector Id ' +
262 connectorId
.toString()
264 await this.resetConnectorOnStartTransactionError(connectorId
);
267 if (this.chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
) {
269 this.chargingStation
.logPrefix() +
270 ' Trying to start a transaction on an already used connector ' +
271 connectorId
.toString() +
273 this.chargingStation
.getConnectorStatus(connectorId
)
278 this.chargingStation
.getConnectorStatus(connectorId
)?.status !==
279 OCPP16ChargePointStatus
.AVAILABLE
&&
280 this.chargingStation
.getConnectorStatus(connectorId
)?.status !==
281 OCPP16ChargePointStatus
.PREPARING
284 `${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
285 this.chargingStation.getConnectorStatus(connectorId)?.status
290 if (!Number.isInteger(payload
.transactionId
)) {
292 `${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${
293 payload.transactionId
294 }, converting to integer`
296 payload
.transactionId
= Utils
.convertToInt(payload
.transactionId
);
299 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
300 this.chargingStation
.getConnectorStatus(connectorId
).transactionStarted
= true;
301 this.chargingStation
.getConnectorStatus(connectorId
).transactionId
= payload
.transactionId
;
302 this.chargingStation
.getConnectorStatus(connectorId
).transactionIdTag
= requestPayload
.idTag
;
303 this.chargingStation
.getConnectorStatus(
305 ).transactionEnergyActiveImportRegisterValue
= 0;
306 this.chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
=
307 OCPP16ServiceUtils
.buildTransactionBeginMeterValue(
308 this.chargingStation
,
310 requestPayload
.meterStart
312 this.chargingStation
.getBeginEndMeterValues() &&
313 (await this.chargingStation
.ocppRequestService
.sendTransactionBeginMeterValues(
315 payload
.transactionId
,
316 this.chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
318 await this.chargingStation
.ocppRequestService
.sendMessageHandler(
319 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
322 status: OCPP16ChargePointStatus
.CHARGING
,
323 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
.sendMessageHandler(
372 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
375 status: OCPP16ChargePointStatus
.AVAILABLE
,
376 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
379 this.chargingStation
.getConnectorStatus(connectorId
).status =
380 OCPP16ChargePointStatus
.AVAILABLE
;
384 private async handleResponseStopTransaction(
385 payload
: OCPP16StopTransactionResponse
,
386 requestPayload
: StopTransactionRequest
388 let transactionConnectorId
: number;
389 for (const connectorId
of this.chargingStation
.connectors
.keys()) {
392 this.chargingStation
.getConnectorStatus(connectorId
)?.transactionId
===
393 requestPayload
.transactionId
395 transactionConnectorId
= connectorId
;
399 if (!transactionConnectorId
) {
401 this.chargingStation
.logPrefix() +
402 ' Trying to stop a non existing transaction ' +
403 requestPayload
.transactionId
.toString()
407 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
408 this.chargingStation
.getBeginEndMeterValues() &&
409 !this.chargingStation
.getOcppStrictCompliance() &&
410 this.chargingStation
.getOutOfOrderEndMeterValues() &&
411 (await this.chargingStation
.ocppRequestService
.sendTransactionEndMeterValues(
412 transactionConnectorId
,
413 requestPayload
.transactionId
,
414 OCPP16ServiceUtils
.buildTransactionEndMeterValue(
415 this.chargingStation
,
416 transactionConnectorId
,
417 requestPayload
.meterStop
421 !this.chargingStation
.isChargingStationAvailable() ||
422 !this.chargingStation
.isConnectorAvailable(transactionConnectorId
)
424 await this.chargingStation
.ocppRequestService
.sendMessageHandler(
425 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
427 connectorId
: transactionConnectorId
,
428 status: OCPP16ChargePointStatus
.UNAVAILABLE
,
429 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
432 this.chargingStation
.getConnectorStatus(transactionConnectorId
).status =
433 OCPP16ChargePointStatus
.UNAVAILABLE
;
435 await this.chargingStation
.ocppRequestService
.sendMessageHandler(
436 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
438 connectorId
: transactionConnectorId
,
439 status: OCPP16ChargePointStatus
.AVAILABLE
,
440 errorCode
: OCPP16ChargePointErrorCode
.NO_ERROR
,
443 this.chargingStation
.getConnectorStatus(transactionConnectorId
).status =
444 OCPP16ChargePointStatus
.AVAILABLE
;
446 if (this.chargingStation
.stationInfo
.powerSharedByConnectors
) {
447 this.chargingStation
.stationInfo
.powerDivider
--;
450 this.chargingStation
.logPrefix() +
452 requestPayload
.transactionId
.toString() +
454 this.chargingStation
.stationInfo
.chargingStationId
+
456 transactionConnectorId
.toString()
458 this.chargingStation
.resetConnectorStatus(transactionConnectorId
);
461 this.chargingStation
.logPrefix() +
462 ' Stopping transaction id ' +
463 requestPayload
.transactionId
.toString() +
464 ' REJECTED with status ' +
465 payload
.idTagInfo
?.status
470 private handleResponseStatusNotification(
471 payload
: StatusNotificationRequest
,
472 requestPayload
: StatusNotificationResponse
475 this.chargingStation
.logPrefix() +
476 ' Status notification response received: %j to StatusNotification request: %j',
482 private handleResponseMeterValues(
483 payload
: MeterValuesRequest
,
484 requestPayload
: MeterValuesResponse
487 this.chargingStation
.logPrefix() +
488 ' MeterValues response received: %j to MeterValues request: %j',