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 { OCPP16ChargePointStatus
} from
'../../../types/ocpp/1.6/ChargePointStatus';
29 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
30 import { OCPP16StandardParametersKey
} from
'../../../types/ocpp/1.6/Configuration';
31 import OCPPError from
'../../../exception/OCPPError';
32 import OCPPResponseService from
'../OCPPResponseService';
33 import { ResponseHandler
} from
'../../../types/ocpp/Responses';
34 import Utils from
'../../../utils/Utils';
35 import logger from
'../../../utils/Logger';
37 const moduleName
= 'OCPP16ResponseService';
39 export default class OCPP16ResponseService
extends OCPPResponseService
{
40 private responseHandlers
: Map
<OCPP16RequestCommand
, ResponseHandler
>;
42 public constructor(chargingStation
: ChargingStation
) {
43 if (new.target
?.name
=== moduleName
) {
44 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
46 super(chargingStation
);
47 this.responseHandlers
= new Map
<OCPP16RequestCommand
, ResponseHandler
>([
48 [OCPP16RequestCommand
.BOOT_NOTIFICATION
, this.handleResponseBootNotification
.bind(this)],
49 [OCPP16RequestCommand
.HEARTBEAT
, this.handleResponseHeartbeat
.bind(this)],
50 [OCPP16RequestCommand
.AUTHORIZE
, this.handleResponseAuthorize
.bind(this)],
51 [OCPP16RequestCommand
.START_TRANSACTION
, this.handleResponseStartTransaction
.bind(this)],
52 [OCPP16RequestCommand
.STOP_TRANSACTION
, this.handleResponseStopTransaction
.bind(this)],
53 [OCPP16RequestCommand
.STATUS_NOTIFICATION
, this.handleResponseStatusNotification
.bind(this)],
54 [OCPP16RequestCommand
.METER_VALUES
, this.handleResponseMeterValues
.bind(this)],
58 public async handleResponse(
59 commandName
: OCPP16RequestCommand
,
60 payload
: JsonType
| string,
61 requestPayload
: JsonType
64 this.chargingStation
.isRegistered() ||
65 commandName
=== OCPP16RequestCommand
.BOOT_NOTIFICATION
67 if (this.responseHandlers
.has(commandName
)) {
69 await this.responseHandlers
.get(commandName
)(payload
, requestPayload
);
72 this.chargingStation
.logPrefix() + ' Handle request response error: %j',
80 ErrorType
.NOT_IMPLEMENTED
,
81 `${commandName} is not implemented to handle request response payload ${JSON.stringify(
91 ErrorType
.SECURITY_ERROR
,
92 `${commandName} cannot be issued to handle request response payload ${JSON.stringify(
96 )} while the charging station is not registered on the central server. `,
102 private handleResponseBootNotification(payload
: OCPP16BootNotificationResponse
): void {
103 if (payload
.status === OCPP16RegistrationStatus
.ACCEPTED
) {
104 this.chargingStation
.addConfigurationKey(
105 OCPP16StandardParametersKey
.HeartBeatInterval
,
106 payload
.interval
.toString()
108 this.chargingStation
.addConfigurationKey(
109 OCPP16StandardParametersKey
.HeartbeatInterval
,
110 payload
.interval
.toString(),
113 this.chargingStation
.heartbeatSetInterval
114 ? this.chargingStation
.restartHeartbeat()
115 : this.chargingStation
.startHeartbeat();
117 if (Object.values(OCPP16RegistrationStatus
).includes(payload
.status)) {
118 const logMsg
= `${this.chargingStation.logPrefix()} Charging station in '${
120 }' state on the central server`;
121 payload
.status === OCPP16RegistrationStatus
.REJECTED
122 ? logger
.warn(logMsg
)
123 : logger
.info(logMsg
);
126 this.chargingStation
.logPrefix() +
127 ' Charging station boot notification response received: %j with undefined registration status',
133 private handleResponseHeartbeat(
134 payload
: HeartbeatResponse
,
135 requestPayload
: HeartbeatRequest
138 this.chargingStation
.logPrefix() +
139 ' Heartbeat response received: %j to Heartbeat request: %j',
145 private handleResponseAuthorize(
146 payload
: OCPP16AuthorizeResponse
,
147 requestPayload
: AuthorizeRequest
149 let authorizeConnectorId
: number;
150 for (const connectorId
of this.chargingStation
.connectors
.keys()) {
153 this.chargingStation
.getConnectorStatus(connectorId
)?.authorizeIdTag
===
156 authorizeConnectorId
= connectorId
;
160 if (payload
.idTagInfo
.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
161 this.chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= true;
163 `${this.chargingStation.logPrefix()} IdTag ${
165 } authorized on connector ${authorizeConnectorId}`
168 this.chargingStation
.getConnectorStatus(authorizeConnectorId
).idTagAuthorized
= false;
169 delete this.chargingStation
.getConnectorStatus(authorizeConnectorId
).authorizeIdTag
;
171 `${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status ${
172 payload.idTagInfo.status
173 } on connector ${authorizeConnectorId}`
178 private async handleResponseStartTransaction(
179 payload
: OCPP16StartTransactionResponse
,
180 requestPayload
: StartTransactionRequest
182 const connectorId
= requestPayload
.connectorId
;
184 let transactionConnectorId
: number;
185 for (const id
of this.chargingStation
.connectors
.keys()) {
186 if (id
> 0 && id
=== connectorId
) {
187 transactionConnectorId
= id
;
191 if (!transactionConnectorId
) {
193 this.chargingStation
.logPrefix() +
194 ' Trying to start a transaction on a non existing connector Id ' +
195 connectorId
.toString()
200 this.chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
201 this.chargingStation
.getAuthorizeRemoteTxRequests() &&
202 this.chargingStation
.getLocalAuthListEnabled() &&
203 this.chargingStation
.hasAuthorizedTags() &&
204 !this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
207 this.chargingStation
.logPrefix() +
208 ' Trying to start a transaction with a not local authorized idTag ' +
209 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
210 ' on connector Id ' +
211 connectorId
.toString()
213 await this.resetConnectorOnStartTransactionError(connectorId
);
217 this.chargingStation
.getConnectorStatus(connectorId
).transactionRemoteStarted
&&
218 this.chargingStation
.getAuthorizeRemoteTxRequests() &&
219 this.chargingStation
.getMayAuthorizeAtRemoteStart() &&
220 !this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
221 !this.chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
224 this.chargingStation
.logPrefix() +
225 ' Trying to start a transaction with a not authorized idTag ' +
226 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
227 ' on connector Id ' +
228 connectorId
.toString()
230 await this.resetConnectorOnStartTransactionError(connectorId
);
234 this.chargingStation
.getConnectorStatus(connectorId
).idTagAuthorized
&&
235 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
!== requestPayload
.idTag
238 this.chargingStation
.logPrefix() +
239 ' Trying to start a transaction with an idTag ' +
240 requestPayload
.idTag
+
241 ' different from the authorize request one ' +
242 this.chargingStation
.getConnectorStatus(connectorId
).authorizeIdTag
+
243 ' on connector Id ' +
244 connectorId
.toString()
246 await this.resetConnectorOnStartTransactionError(connectorId
);
250 this.chargingStation
.getConnectorStatus(connectorId
).idTagLocalAuthorized
&&
251 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
!==
255 this.chargingStation
.logPrefix() +
256 ' Trying to start a transaction with an idTag ' +
257 requestPayload
.idTag
+
258 ' different from the local authorized one ' +
259 this.chargingStation
.getConnectorStatus(connectorId
).localAuthorizeIdTag
+
260 ' on connector Id ' +
261 connectorId
.toString()
263 await this.resetConnectorOnStartTransactionError(connectorId
);
266 if (this.chargingStation
.getConnectorStatus(connectorId
)?.transactionStarted
) {
268 this.chargingStation
.logPrefix() +
269 ' Trying to start a transaction on an already used connector ' +
270 connectorId
.toString() +
272 this.chargingStation
.getConnectorStatus(connectorId
)
277 this.chargingStation
.getConnectorStatus(connectorId
)?.status !==
278 OCPP16ChargePointStatus
.AVAILABLE
&&
279 this.chargingStation
.getConnectorStatus(connectorId
)?.status !==
280 OCPP16ChargePointStatus
.PREPARING
283 `${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${
284 this.chargingStation.getConnectorStatus(connectorId)?.status
289 if (!Number.isInteger(payload
.transactionId
)) {
291 `${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${
292 payload.transactionId
293 }, converting to integer`
295 payload
.transactionId
= Utils
.convertToInt(payload
.transactionId
);
298 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
299 this.chargingStation
.getConnectorStatus(connectorId
).transactionStarted
= true;
300 this.chargingStation
.getConnectorStatus(connectorId
).transactionId
= payload
.transactionId
;
301 this.chargingStation
.getConnectorStatus(connectorId
).transactionIdTag
= requestPayload
.idTag
;
302 this.chargingStation
.getConnectorStatus(
304 ).transactionEnergyActiveImportRegisterValue
= 0;
305 this.chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
=
306 OCPP16ServiceUtils
.buildTransactionBeginMeterValue(
307 this.chargingStation
,
309 requestPayload
.meterStart
311 this.chargingStation
.getBeginEndMeterValues() &&
312 (await this.chargingStation
.ocppRequestService
.sendTransactionBeginMeterValues(
314 payload
.transactionId
,
315 this.chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
317 await this.chargingStation
.ocppRequestService
.sendStatusNotification(
319 OCPP16ChargePointStatus
.CHARGING
321 this.chargingStation
.getConnectorStatus(connectorId
).status =
322 OCPP16ChargePointStatus
.CHARGING
;
324 this.chargingStation
.logPrefix() +
326 payload
.transactionId
.toString() +
328 this.chargingStation
.stationInfo
.chargingStationId
+
330 connectorId
.toString() +
334 if (this.chargingStation
.stationInfo
.powerSharedByConnectors
) {
335 this.chargingStation
.stationInfo
.powerDivider
++;
337 const configuredMeterValueSampleInterval
= this.chargingStation
.getConfigurationKey(
338 OCPP16StandardParametersKey
.MeterValueSampleInterval
340 this.chargingStation
.startMeterValues(
342 configuredMeterValueSampleInterval
343 ? Utils
.convertToInt(configuredMeterValueSampleInterval
.value
) * 1000
348 this.chargingStation
.logPrefix() +
349 ' Starting transaction id ' +
350 payload
.transactionId
.toString() +
351 ' REJECTED with status ' +
352 payload
?.idTagInfo
?.status +
356 await this.resetConnectorOnStartTransactionError(connectorId
);
360 private async resetConnectorOnStartTransactionError(connectorId
: number): Promise
<void> {
361 this.chargingStation
.resetConnectorStatus(connectorId
);
363 this.chargingStation
.getConnectorStatus(connectorId
).status !==
364 OCPP16ChargePointStatus
.AVAILABLE
366 await this.chargingStation
.ocppRequestService
.sendStatusNotification(
368 OCPP16ChargePointStatus
.AVAILABLE
370 this.chargingStation
.getConnectorStatus(connectorId
).status =
371 OCPP16ChargePointStatus
.AVAILABLE
;
375 private async handleResponseStopTransaction(
376 payload
: OCPP16StopTransactionResponse
,
377 requestPayload
: StopTransactionRequest
379 let transactionConnectorId
: number;
380 for (const connectorId
of this.chargingStation
.connectors
.keys()) {
383 this.chargingStation
.getConnectorStatus(connectorId
)?.transactionId
===
384 requestPayload
.transactionId
386 transactionConnectorId
= connectorId
;
390 if (!transactionConnectorId
) {
392 this.chargingStation
.logPrefix() +
393 ' Trying to stop a non existing transaction ' +
394 requestPayload
.transactionId
.toString()
398 if (payload
.idTagInfo
?.status === OCPP16AuthorizationStatus
.ACCEPTED
) {
399 this.chargingStation
.getBeginEndMeterValues() &&
400 !this.chargingStation
.getOcppStrictCompliance() &&
401 this.chargingStation
.getOutOfOrderEndMeterValues() &&
402 (await this.chargingStation
.ocppRequestService
.sendTransactionEndMeterValues(
403 transactionConnectorId
,
404 requestPayload
.transactionId
,
405 OCPP16ServiceUtils
.buildTransactionEndMeterValue(
406 this.chargingStation
,
407 transactionConnectorId
,
408 requestPayload
.meterStop
412 !this.chargingStation
.isChargingStationAvailable() ||
413 !this.chargingStation
.isConnectorAvailable(transactionConnectorId
)
415 await this.chargingStation
.ocppRequestService
.sendStatusNotification(
416 transactionConnectorId
,
417 OCPP16ChargePointStatus
.UNAVAILABLE
419 this.chargingStation
.getConnectorStatus(transactionConnectorId
).status =
420 OCPP16ChargePointStatus
.UNAVAILABLE
;
422 await this.chargingStation
.ocppRequestService
.sendStatusNotification(
423 transactionConnectorId
,
424 OCPP16ChargePointStatus
.AVAILABLE
426 this.chargingStation
.getConnectorStatus(transactionConnectorId
).status =
427 OCPP16ChargePointStatus
.AVAILABLE
;
429 if (this.chargingStation
.stationInfo
.powerSharedByConnectors
) {
430 this.chargingStation
.stationInfo
.powerDivider
--;
433 this.chargingStation
.logPrefix() +
435 requestPayload
.transactionId
.toString() +
437 this.chargingStation
.stationInfo
.chargingStationId
+
439 transactionConnectorId
.toString()
441 this.chargingStation
.resetConnectorStatus(transactionConnectorId
);
444 this.chargingStation
.logPrefix() +
445 ' Stopping transaction id ' +
446 requestPayload
.transactionId
.toString() +
447 ' REJECTED with status ' +
448 payload
.idTagInfo
?.status
453 private handleResponseStatusNotification(
454 payload
: StatusNotificationRequest
,
455 requestPayload
: StatusNotificationResponse
458 this.chargingStation
.logPrefix() +
459 ' Status notification response received: %j to StatusNotification request: %j',
465 private handleResponseMeterValues(
466 payload
: MeterValuesRequest
,
467 requestPayload
: MeterValuesResponse
470 this.chargingStation
.logPrefix() +
471 ' MeterValues response received: %j to MeterValues request: %j',