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 { OCPP16ServiceUtils
} from
'./OCPP16ServiceUtils';
10 import OCPPError from
'../../../exception/OCPPError';
11 import type { JsonObject
, JsonType
} from
'../../../types/JsonType';
12 import type { OCPP16MeterValuesRequest
} from
'../../../types/ocpp/1.6/MeterValues';
14 type OCPP16BootNotificationRequest
,
15 type OCPP16DataTransferRequest
,
16 type OCPP16DiagnosticsStatusNotificationRequest
,
17 type OCPP16FirmwareStatusNotificationRequest
,
18 type OCPP16HeartbeatRequest
,
20 type OCPP16StatusNotificationRequest
,
21 } from
'../../../types/ocpp/1.6/Requests';
23 OCPP16AuthorizeRequest
,
24 OCPP16StartTransactionRequest
,
25 OCPP16StopTransactionRequest
,
26 } from
'../../../types/ocpp/1.6/Transaction';
27 import { ErrorType
} from
'../../../types/ocpp/ErrorType';
28 import { OCPPVersion
} from
'../../../types/ocpp/OCPPVersion';
29 import type { RequestParams
} from
'../../../types/ocpp/Requests';
30 import Constants from
'../../../utils/Constants';
31 import Utils from
'../../../utils/Utils';
32 import type ChargingStation from
'../../ChargingStation';
33 import OCPPRequestService from
'../OCPPRequestService';
34 import type OCPPResponseService from
'../OCPPResponseService';
36 const moduleName
= 'OCPP16RequestService';
38 export default class OCPP16RequestService
extends OCPPRequestService
{
39 protected 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
,
49 this.parseJsonSchemaFile
<OCPP16AuthorizeRequest
>(
50 '../../../assets/json-schemas/ocpp/1.6/Authorize.json'
54 OCPP16RequestCommand
.BOOT_NOTIFICATION
,
55 this.parseJsonSchemaFile
<OCPP16BootNotificationRequest
>(
56 '../../../assets/json-schemas/ocpp/1.6/BootNotification.json'
60 OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
,
61 this.parseJsonSchemaFile
<OCPP16DiagnosticsStatusNotificationRequest
>(
62 '../../../assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotification.json'
66 OCPP16RequestCommand
.HEARTBEAT
,
67 this.parseJsonSchemaFile
<OCPP16HeartbeatRequest
>(
68 '../../../assets/json-schemas/ocpp/1.6/Heartbeat.json'
72 OCPP16RequestCommand
.METER_VALUES
,
73 this.parseJsonSchemaFile
<OCPP16MeterValuesRequest
>(
74 '../../../assets/json-schemas/ocpp/1.6/MeterValues.json'
78 OCPP16RequestCommand
.STATUS_NOTIFICATION
,
79 this.parseJsonSchemaFile
<OCPP16StatusNotificationRequest
>(
80 '../../../assets/json-schemas/ocpp/1.6/StatusNotification.json'
84 OCPP16RequestCommand
.START_TRANSACTION
,
85 this.parseJsonSchemaFile
<OCPP16StartTransactionRequest
>(
86 '../../../assets/json-schemas/ocpp/1.6/StartTransaction.json'
90 OCPP16RequestCommand
.STOP_TRANSACTION
,
91 this.parseJsonSchemaFile
<OCPP16StopTransactionRequest
>(
92 '../../../assets/json-schemas/ocpp/1.6/StopTransaction.json'
96 OCPP16RequestCommand
.DATA_TRANSFER
,
97 this.parseJsonSchemaFile
<OCPP16DataTransferRequest
>(
98 '../../../assets/json-schemas/ocpp/1.6/DataTransfer.json'
102 OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
,
103 this.parseJsonSchemaFile
<OCPP16FirmwareStatusNotificationRequest
>(
104 '../../../assets/json-schemas/ocpp/1.6/FirmwareStatusNotification.json'
108 this.buildRequestPayload
.bind(this);
111 public async requestHandler
<RequestType
extends JsonType
, ResponseType
extends JsonType
>(
112 chargingStation
: ChargingStation
,
113 commandName
: OCPP16RequestCommand
,
114 commandParams
?: JsonType
,
115 params
?: RequestParams
116 ): Promise
<ResponseType
> {
117 if (OCPP16ServiceUtils
.isRequestCommandSupported(chargingStation
, commandName
) === true) {
118 const requestPayload
= this.buildRequestPayload
<RequestType
>(
123 return (await this.sendMessage(
125 Utils
.generateUUID(),
129 )) as unknown
as ResponseType
;
131 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
133 ErrorType
.NOT_SUPPORTED
,
134 `Unsupported OCPP command '${commandName}'`,
140 private buildRequestPayload
<Request
extends JsonType
>(
141 chargingStation
: ChargingStation
,
142 commandName
: OCPP16RequestCommand
,
143 commandParams
?: JsonType
145 let connectorId
: number;
146 let energyActiveImportRegister
: number;
147 commandParams
= commandParams
as JsonObject
;
148 switch (commandName
) {
149 case OCPP16RequestCommand
.AUTHORIZE
:
151 ...(!Utils
.isUndefined(commandParams
?.idTag
)
152 ? { idTag
: commandParams
.idTag
}
153 : { idTag
: Constants
.DEFAULT_IDTAG
}),
154 } as unknown
as Request
;
155 case OCPP16RequestCommand
.BOOT_NOTIFICATION
:
157 chargePointModel
: commandParams
?.chargePointModel
,
158 chargePointVendor
: commandParams
?.chargePointVendor
,
159 ...(!Utils
.isUndefined(commandParams
?.chargeBoxSerialNumber
) && {
160 chargeBoxSerialNumber
: commandParams
.chargeBoxSerialNumber
,
162 ...(!Utils
.isUndefined(commandParams
?.chargePointSerialNumber
) && {
163 chargePointSerialNumber
: commandParams
.chargePointSerialNumber
,
165 ...(!Utils
.isUndefined(commandParams
?.firmwareVersion
) && {
166 firmwareVersion
: commandParams
.firmwareVersion
,
168 ...(!Utils
.isUndefined(commandParams
?.iccid
) && { iccid
: commandParams
.iccid
}),
169 ...(!Utils
.isUndefined(commandParams
?.imsi
) && { imsi
: commandParams
.imsi
}),
170 ...(!Utils
.isUndefined(commandParams
?.meterSerialNumber
) && {
171 meterSerialNumber
: commandParams
.meterSerialNumber
,
173 ...(!Utils
.isUndefined(commandParams
?.meterType
) && {
174 meterType
: commandParams
.meterType
,
176 } as unknown
as Request
;
177 case OCPP16RequestCommand
.DIAGNOSTICS_STATUS_NOTIFICATION
:
178 case OCPP16RequestCommand
.FIRMWARE_STATUS_NOTIFICATION
:
180 status: commandParams
?.status,
181 } as unknown
as Request
;
182 case OCPP16RequestCommand
.HEARTBEAT
:
183 return {} as unknown
as Request
;
184 case OCPP16RequestCommand
.METER_VALUES
:
186 connectorId
: commandParams
?.connectorId
,
187 transactionId
: commandParams
?.transactionId
,
188 meterValue
: commandParams
?.meterValue
,
189 } as unknown
as Request
;
190 case OCPP16RequestCommand
.STATUS_NOTIFICATION
:
192 connectorId
: commandParams
?.connectorId
,
193 status: commandParams
?.status,
194 errorCode
: commandParams
?.errorCode
,
195 ...(!Utils
.isUndefined(commandParams
?.info
) && {
196 info
: commandParams
?.info
,
198 ...(!Utils
.isUndefined(commandParams
?.timestamp
) && {
199 timestamp
: commandParams
?.timestamp
,
201 ...(!Utils
.isUndefined(commandParams
?.vendorId
) && {
202 vendorId
: commandParams
?.vendorId
,
204 ...(!Utils
.isUndefined(commandParams
?.vendorErrorCode
) && {
205 vendorErrorCode
: commandParams
?.vendorErrorCode
,
207 } as unknown
as Request
;
208 case OCPP16RequestCommand
.START_TRANSACTION
:
210 connectorId
: commandParams
?.connectorId
,
211 ...(!Utils
.isUndefined(commandParams
?.idTag
)
212 ? { idTag
: commandParams
?.idTag
}
213 : { idTag
: Constants
.DEFAULT_IDTAG
}),
215 commandParams
?.meterStart
??
216 chargingStation
.getEnergyActiveImportRegisterByConnectorId(
217 commandParams
?.connectorId
as number
219 timestamp
: commandParams
?.timestamp
?? new Date(),
220 ...(!Utils
.isUndefined(commandParams
?.reservationId
) && {
221 reservationId
: commandParams
?.reservationId
,
223 } as unknown
as Request
;
224 case OCPP16RequestCommand
.STOP_TRANSACTION
:
225 chargingStation
.getTransactionDataMeterValues() &&
226 Utils
.isUndefined(commandParams
?.transactionData
) &&
227 (connectorId
= chargingStation
.getConnectorIdByTransactionId(
228 commandParams
?.transactionId
as number
230 !commandParams
?.meterStop
&&
231 (energyActiveImportRegister
=
232 chargingStation
.getEnergyActiveImportRegisterByTransactionId(
233 commandParams
?.transactionId
as number,
237 transactionId
: commandParams
?.transactionId
,
239 commandParams
?.idTag
??
240 chargingStation
.getTransactionIdTag(commandParams
?.transactionId
as number),
241 meterStop
: commandParams
?.meterStop
?? energyActiveImportRegister
,
242 timestamp
: commandParams
?.timestamp
?? new Date(),
243 ...(!Utils
.isUndefined(commandParams
?.reason
) && {
244 reason
: commandParams
?.reason
,
246 ...(!Utils
.isUndefined(commandParams
?.transactionData
)
247 ? { transactionData
: commandParams
?.transactionData
}
248 : chargingStation
.getTransactionDataMeterValues() && {
249 transactionData
: OCPP16ServiceUtils
.buildTransactionDataMeterValues(
250 chargingStation
.getConnectorStatus(connectorId
).transactionBeginMeterValue
,
251 OCPP16ServiceUtils
.buildTransactionEndMeterValue(
254 (commandParams
?.meterStop
as number) ?? energyActiveImportRegister
258 } as unknown
as Request
;
259 case OCPP16RequestCommand
.DATA_TRANSFER
:
260 return commandParams
as unknown
as Request
;
262 // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
264 ErrorType
.NOT_SUPPORTED
,
265 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
266 `Unsupported OCPP command '${commandName}'`,
273 private parseJsonSchemaFile
<T
extends JsonType
>(relativePath
: string): JSONSchemaType
<T
> {
276 path
.resolve(path
.dirname(fileURLToPath(import.meta
.url
)), relativePath
),
279 ) as JSONSchemaType
<T
>;