1 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
4 import path from
'path';
5 import { fileURLToPath
} from
'url';
7 import type { JSONSchemaType
} from
'ajv';
9 import OCPPError from
'../../../exception/OCPPError';
10 import type { JsonObject
, JsonType
} from
'../../../types/JsonType';
11 import type { OCPP16MeterValuesRequest
} from
'../../../types/ocpp/1.6/MeterValues';
13 type DiagnosticsStatusNotificationRequest
,
14 type OCPP16BootNotificationRequest
,
15 type OCPP16DataTransferRequest
,
16 type OCPP16HeartbeatRequest
,
18 type OCPP16StatusNotificationRequest
,
19 } from
'../../../types/ocpp/1.6/Requests';
21 OCPP16AuthorizeRequest
,
22 OCPP16StartTransactionRequest
,
23 OCPP16StopTransactionRequest
,
24 } from
'../../../types/ocpp/1.6/Transaction';
25 import { ErrorType
} from
'../../../types/ocpp/ErrorType';
26 import { OCPPVersion
} from
'../../../types/ocpp/OCPPVersion';
27 import type { RequestParams
} from
'../../../types/ocpp/Requests';
28 import Constants from
'../../../utils/Constants';
29 import logger from
'../../../utils/Logger';
30 import Utils from
'../../../utils/Utils';
31 import type ChargingStation from
'../../ChargingStation';
32 import OCPPRequestService from
'../OCPPRequestService';
33 import type OCPPResponseService from
'../OCPPResponseService';
34 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
36 const moduleName
= 'OCPP16RequestService';
38 export default class OCPP16RequestService
extends OCPPRequestService
{
39 private jsonSchemas
: Map
<OCPP16RequestCommand
, JSONSchemaType
<JsonObject
>>;
41 public constructor(ocppResponseService
: OCPPResponseService
) {
42 if (new.target
?.name
=== moduleName
) {
43 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
45 super(OCPPVersion
.VERSION_16
, ocppResponseService
);
46 this.jsonSchemas
= new Map
<OCPP16RequestCommand
, JSONSchemaType
<JsonObject
>>([
48 OCPP16RequestCommand
.AUTHORIZE
,
52 path
.dirname(fileURLToPath(import.meta
.url
)),
53 '../../../assets/json-schemas/ocpp/1.6/Authorize.json'
57 ) as JSONSchemaType
<OCPP16AuthorizeRequest
>,
60 OCPP16RequestCommand
.BOOT_NOTIFICATION
,
64 path
.dirname(fileURLToPath(import.meta
.url
)),
65 '../../../assets/json-schemas/ocpp/1.6/BootNotification.json'
69 ) as JSONSchemaType
<OCPP16BootNotificationRequest
>,
72 OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
,
76 path
.dirname(fileURLToPath(import.meta
.url
)),
77 '../../../assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotification.json'
81 ) as JSONSchemaType
<DiagnosticsStatusNotificationRequest
>,
84 OCPP16RequestCommand
.HEARTBEAT
,
88 path
.dirname(fileURLToPath(import.meta
.url
)),
89 '../../../assets/json-schemas/ocpp/1.6/Heartbeat.json'
93 ) as JSONSchemaType
<OCPP16HeartbeatRequest
>,
96 OCPP16RequestCommand
.METER_VALUES
,
100 path
.dirname(fileURLToPath(import.meta
.url
)),
101 '../../../assets/json-schemas/ocpp/1.6/MeterValues.json'
105 ) as JSONSchemaType
<OCPP16MeterValuesRequest
>,
108 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
112 path
.dirname(fileURLToPath(import.meta
.url
)),
113 '../../../assets/json-schemas/ocpp/1.6/StatusNotification.json'
117 ) as JSONSchemaType
<OCPP16StatusNotificationRequest
>,
120 OCPP16RequestCommand
.START_TRANSACTION
,
124 path
.dirname(fileURLToPath(import.meta
.url
)),
125 '../../../assets/json-schemas/ocpp/1.6/StartTransaction.json'
129 ) as JSONSchemaType
<OCPP16StartTransactionRequest
>,
132 OCPP16RequestCommand
.STOP_TRANSACTION
,
136 path
.dirname(fileURLToPath(import.meta
.url
)),
137 '../../../assets/json-schemas/ocpp/1.6/StopTransaction.json'
141 ) as JSONSchemaType
<OCPP16StopTransactionRequest
>,
144 OCPP16RequestCommand
.DATA_TRANSFER
,
148 path
.dirname(fileURLToPath(import.meta
.url
)),
149 '../../../assets/json-schemas/ocpp/1.6/DataTransfer.json'
153 ) as JSONSchemaType
<OCPP16DataTransferRequest
>,
156 this.buildRequestPayload
.bind(this);
157 this.validatePayload
.bind(this);
160 public async requestHandler
<RequestType
extends JsonType
, ResponseType
extends JsonType
>(
161 chargingStation
: ChargingStation
,
162 commandName
: OCPP16RequestCommand
,
163 commandParams
?: JsonType
,
164 params
?: RequestParams
165 ): Promise
<ResponseType
> {
166 if (OCPP16ServiceUtils
.isRequestCommandSupported(chargingStation
, commandName
) === true) {
167 const requestPayload
= this.buildRequestPayload
<RequestType
>(
172 this.validatePayload(chargingStation
, commandName
, requestPayload
);
173 return (await this.sendMessage(
175 Utils
.generateUUID(),
179 )) as unknown
as ResponseType
;
181 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
183 ErrorType
.NOT_SUPPORTED
,
184 `Unsupported OCPP command '${commandName}'`,
190 private buildRequestPayload
<Request
extends JsonType
>(
191 chargingStation
: ChargingStation
,
192 commandName
: OCPP16RequestCommand
,
193 commandParams
?: JsonType
195 let connectorId
: number;
196 let energyActiveImportRegister
: number;
197 commandParams
= commandParams
as JsonObject
;
198 switch (commandName
) {
199 case OCPP16RequestCommand
.AUTHORIZE
:
201 ...(!Utils
.isUndefined(commandParams
?.idTag
)
202 ? { idTag
: commandParams
.idTag
}
203 : { idTag
: Constants
.DEFAULT_IDTAG
}),
204 } as unknown
as Request
;
205 case OCPP16RequestCommand
.BOOT_NOTIFICATION
:
207 chargePointModel
: commandParams
?.chargePointModel
,
208 chargePointVendor
: commandParams
?.chargePointVendor
,
209 ...(!Utils
.isUndefined(commandParams
?.chargeBoxSerialNumber
) && {
210 chargeBoxSerialNumber
: commandParams
.chargeBoxSerialNumber
,
212 ...(!Utils
.isUndefined(commandParams
?.chargePointSerialNumber
) && {
213 chargePointSerialNumber
: commandParams
.chargePointSerialNumber
,
215 ...(!Utils
.isUndefined(commandParams
?.firmwareVersion
) && {
216 firmwareVersion
: commandParams
.firmwareVersion
,
218 ...(!Utils
.isUndefined(commandParams
?.iccid
) && { iccid
: commandParams
.iccid
}),
219 ...(!Utils
.isUndefined(commandParams
?.imsi
) && { imsi
: commandParams
.imsi
}),
220 ...(!Utils
.isUndefined(commandParams
?.meterSerialNumber
) && {
221 meterSerialNumber
: commandParams
.meterSerialNumber
,
223 ...(!Utils
.isUndefined(commandParams
?.meterType
) && {
224 meterType
: commandParams
.meterType
,
226 } as unknown
as Request
;
227 case OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
:
229 status: commandParams
?.diagnosticsStatus
,
230 } as unknown
as Request
;
231 case OCPP16RequestCommand
.HEARTBEAT
:
232 return {} as unknown
as Request
;
233 case OCPP16RequestCommand
.METER_VALUES
:
235 connectorId
: commandParams
?.connectorId
,
236 transactionId
: commandParams
?.transactionId
,
237 meterValue
: commandParams
?.meterValue
,
238 } as unknown
as Request
;
239 case OCPP16RequestCommand
.STATUS_NOTIFICATION
:
241 connectorId
: commandParams
?.connectorId
,
242 status: commandParams
?.status,
243 errorCode
: commandParams
?.errorCode
,
244 } as unknown
as Request
;
245 case OCPP16RequestCommand
.START_TRANSACTION
:
247 connectorId
: commandParams
?.connectorId
,
248 ...(!Utils
.isUndefined(commandParams
?.idTag
)
249 ? { idTag
: commandParams
?.idTag
}
250 : { idTag
: Constants
.DEFAULT_IDTAG
}),
251 meterStart
: chargingStation
.getEnergyActiveImportRegisterByConnectorId(
252 commandParams
?.connectorId
as number
254 timestamp
: new Date().toISOString(),
255 } as unknown
as Request
;
256 case OCPP16RequestCommand
.STOP_TRANSACTION
:
257 connectorId
= chargingStation
.getConnectorIdByTransactionId(
258 commandParams
?.transactionId
as number
260 commandParams
?.meterStop
&&
261 (energyActiveImportRegister
=
262 chargingStation
.getEnergyActiveImportRegisterByTransactionId(
263 commandParams
?.transactionId
as number,
267 transactionId
: commandParams
?.transactionId
,
269 commandParams
?.idTag
??
270 chargingStation
.getTransactionIdTag(commandParams
?.transactionId
as number),
271 meterStop
: commandParams
?.meterStop
?? energyActiveImportRegister
,
272 timestamp
: new Date().toISOString(),
273 reason
: commandParams
?.reason
,
274 ...(chargingStation
.getTransactionDataMeterValues() && {
275 transactionData
: OCPP16ServiceUtils
.buildTransactionDataMeterValues(
276 chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
,
277 OCPP16ServiceUtils
.buildTransactionEndMeterValue(
280 (commandParams
?.meterStop
as number) ?? energyActiveImportRegister
284 } as unknown
as Request
;
285 case OCPP16RequestCommand
.DATA_TRANSFER
:
286 return commandParams
as unknown
as Request
;
288 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
290 ErrorType
.NOT_SUPPORTED
,
291 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
292 `Unsupported OCPP command '${commandName}'`,
299 private validatePayload
<Request
extends JsonType
>(
300 chargingStation
: ChargingStation
,
301 commandName
: OCPP16RequestCommand
,
302 requestPayload
: Request
304 if (this.jsonSchemas
.has(commandName
)) {
305 return this.validateRequestPayload(
308 this.jsonSchemas
.get(commandName
),
313 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command ${commandName} PDU validation`