1 // Partial Copyright Jerome Benoit. 2021. 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 DiagnosticsStatusNotificationRequest
,
14 OCPP16BootNotificationRequest
,
15 OCPP16HeartbeatRequest
,
17 OCPP16StatusNotificationRequest
,
18 } from
'../../../types/ocpp/1.6/Requests';
20 OCPP16AuthorizeRequest
,
21 OCPP16StartTransactionRequest
,
22 OCPP16StopTransactionRequest
,
23 } from
'../../../types/ocpp/1.6/Transaction';
24 import { ErrorType
} from
'../../../types/ocpp/ErrorType';
25 import type { RequestParams
} from
'../../../types/ocpp/Requests';
26 import Constants from
'../../../utils/Constants';
27 import logger from
'../../../utils/Logger';
28 import Utils from
'../../../utils/Utils';
29 import type ChargingStation from
'../../ChargingStation';
30 import { ChargingStationUtils
} from
'../../ChargingStationUtils';
31 import OCPPRequestService from
'../OCPPRequestService';
32 import type OCPPResponseService from
'../OCPPResponseService';
33 import { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
35 const moduleName
= 'OCPP16RequestService';
37 export default class OCPP16RequestService
extends OCPPRequestService
{
38 private jsonSchemas
: Map
<OCPP16RequestCommand
, JSONSchemaType
<JsonObject
>>;
40 public constructor(ocppResponseService
: OCPPResponseService
) {
41 if (new.target
?.name
=== moduleName
) {
42 throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
44 super(ocppResponseService
);
45 this.jsonSchemas
= new Map
<OCPP16RequestCommand
, JSONSchemaType
<JsonObject
>>([
47 OCPP16RequestCommand
.AUTHORIZE
,
51 path
.dirname(fileURLToPath(import.meta
.url
)),
52 '../../../assets/json-schemas/ocpp/1.6/Authorize.json'
56 ) as JSONSchemaType
<OCPP16AuthorizeRequest
>,
59 OCPP16RequestCommand
.BOOT_NOTIFICATION
,
63 path
.dirname(fileURLToPath(import.meta
.url
)),
64 '../../../assets/json-schemas/ocpp/1.6/BootNotification.json'
68 ) as JSONSchemaType
<OCPP16BootNotificationRequest
>,
71 OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
,
75 path
.dirname(fileURLToPath(import.meta
.url
)),
76 '../../../assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotification.json'
80 ) as JSONSchemaType
<DiagnosticsStatusNotificationRequest
>,
83 OCPP16RequestCommand
.HEARTBEAT
,
87 path
.dirname(fileURLToPath(import.meta
.url
)),
88 '../../../assets/json-schemas/ocpp/1.6/Heartbeat.json'
92 ) as JSONSchemaType
<OCPP16HeartbeatRequest
>,
95 OCPP16RequestCommand
.METER_VALUES
,
99 path
.dirname(fileURLToPath(import.meta
.url
)),
100 '../../../assets/json-schemas/ocpp/1.6/MeterValues.json'
104 ) as JSONSchemaType
<OCPP16MeterValuesRequest
>,
107 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
111 path
.dirname(fileURLToPath(import.meta
.url
)),
112 '../../../assets/json-schemas/ocpp/1.6/StatusNotification.json'
116 ) as JSONSchemaType
<OCPP16StatusNotificationRequest
>,
119 OCPP16RequestCommand
.START_TRANSACTION
,
123 path
.dirname(fileURLToPath(import.meta
.url
)),
124 '../../../assets/json-schemas/ocpp/1.6/StartTransaction.json'
128 ) as JSONSchemaType
<OCPP16StartTransactionRequest
>,
131 OCPP16RequestCommand
.STOP_TRANSACTION
,
135 path
.dirname(fileURLToPath(import.meta
.url
)),
136 '../../../assets/json-schemas/ocpp/1.6/StopTransaction.json'
140 ) as JSONSchemaType
<OCPP16StopTransactionRequest
>,
143 this.buildRequestPayload
.bind(this);
144 this.validatePayload
.bind(this);
147 public async requestHandler
<RequestType
extends JsonType
, ResponseType
extends JsonType
>(
148 chargingStation
: ChargingStation
,
149 commandName
: OCPP16RequestCommand
,
150 commandParams
?: JsonType
,
151 params
?: RequestParams
152 ): Promise
<ResponseType
> {
153 if (OCPP16ServiceUtils
.isRequestCommandSupported(commandName
, chargingStation
)) {
154 const requestPayload
= this.buildRequestPayload
<RequestType
>(
159 this.validatePayload(chargingStation
, commandName
, requestPayload
);
160 return (await this.sendMessage(
162 Utils
.generateUUID(),
166 )) as unknown
as ResponseType
;
168 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
170 ErrorType
.NOT_SUPPORTED
,
171 `Unsupported OCPP command '${commandName}'`,
177 private buildRequestPayload
<Request
extends JsonType
>(
178 chargingStation
: ChargingStation
,
179 commandName
: OCPP16RequestCommand
,
180 commandParams
?: JsonType
182 let connectorId
: number;
183 let energyActiveImportRegister
: number;
184 commandParams
= commandParams
as JsonObject
;
185 switch (commandName
) {
186 case OCPP16RequestCommand
.AUTHORIZE
:
188 ...(!Utils
.isUndefined(commandParams
?.idTag
)
189 ? { idTag
: commandParams
.idTag
}
190 : { idTag
: Constants
.DEFAULT_IDTAG
}),
191 } as unknown
as Request
;
192 case OCPP16RequestCommand
.BOOT_NOTIFICATION
:
194 chargePointModel
: commandParams
?.chargePointModel
,
195 chargePointVendor
: commandParams
?.chargePointVendor
,
196 ...(!Utils
.isUndefined(commandParams
?.chargeBoxSerialNumber
) && {
197 chargeBoxSerialNumber
: commandParams
.chargeBoxSerialNumber
,
199 ...(!Utils
.isUndefined(commandParams
?.chargePointSerialNumber
) && {
200 chargePointSerialNumber
: commandParams
.chargePointSerialNumber
,
202 ...(!Utils
.isUndefined(commandParams
?.firmwareVersion
) && {
203 firmwareVersion
: commandParams
.firmwareVersion
,
205 ...(!Utils
.isUndefined(commandParams
?.iccid
) && { iccid
: commandParams
.iccid
}),
206 ...(!Utils
.isUndefined(commandParams
?.imsi
) && { imsi
: commandParams
.imsi
}),
207 ...(!Utils
.isUndefined(commandParams
?.meterSerialNumber
) && {
208 meterSerialNumber
: commandParams
.meterSerialNumber
,
210 ...(!Utils
.isUndefined(commandParams
?.meterType
) && {
211 meterType
: commandParams
.meterType
,
213 } as unknown
as Request
;
214 case OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
:
216 status: commandParams
?.diagnosticsStatus
,
217 } as unknown
as Request
;
218 case OCPP16RequestCommand
.HEARTBEAT
:
219 return {} as unknown
as Request
;
220 case OCPP16RequestCommand
.METER_VALUES
:
222 connectorId
: commandParams
?.connectorId
,
223 transactionId
: commandParams
?.transactionId
,
224 meterValue
: commandParams
?.meterValue
,
225 } as unknown
as Request
;
226 case OCPP16RequestCommand
.STATUS_NOTIFICATION
:
228 connectorId
: commandParams
?.connectorId
,
229 status: commandParams
?.status,
230 errorCode
: commandParams
?.errorCode
,
231 } as unknown
as Request
;
232 case OCPP16RequestCommand
.START_TRANSACTION
:
234 connectorId
: commandParams
?.connectorId
,
235 ...(!Utils
.isUndefined(commandParams
?.idTag
)
236 ? { idTag
: commandParams
?.idTag
}
237 : { idTag
: Constants
.DEFAULT_IDTAG
}),
238 meterStart
: chargingStation
.getEnergyActiveImportRegisterByConnectorId(
239 commandParams
?.connectorId
as number
241 timestamp
: new Date().toISOString(),
242 } as unknown
as Request
;
243 case OCPP16RequestCommand
.STOP_TRANSACTION
:
244 connectorId
= chargingStation
.getConnectorIdByTransactionId(
245 commandParams
?.transactionId
as number
247 commandParams
?.meterStop
&&
248 (energyActiveImportRegister
=
249 chargingStation
.getEnergyActiveImportRegisterByTransactionId(
250 commandParams
?.transactionId
as number,
254 transactionId
: commandParams
?.transactionId
,
256 commandParams
?.idTag
??
257 chargingStation
.getTransactionIdTag(commandParams
?.transactionId
as number),
258 meterStop
: commandParams
?.meterStop
?? energyActiveImportRegister
,
259 timestamp
: new Date().toISOString(),
260 reason
: commandParams
?.reason
,
261 ...(chargingStation
.getTransactionDataMeterValues() && {
262 transactionData
: OCPP16ServiceUtils
.buildTransactionDataMeterValues(
263 chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
,
264 OCPP16ServiceUtils
.buildTransactionEndMeterValue(
267 (commandParams
?.meterStop
as number) ?? energyActiveImportRegister
271 } as unknown
as Request
;
273 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
275 ErrorType
.NOT_SUPPORTED
,
276 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
277 `Unsupported OCPP command '${commandName}'`,
284 private validatePayload
<Request
extends JsonType
>(
285 chargingStation
: ChargingStation
,
286 commandName
: OCPP16RequestCommand
,
287 requestPayload
: Request
289 if (this.jsonSchemas
.has(commandName
)) {
290 return this.validateRequestPayload(
293 this.jsonSchemas
.get(commandName
),
298 `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command ${commandName} PDU validation`