Commit | Line | Data |
---|---|---|
edd13439 | 1 | // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. |
c8eeb62b | 2 | |
b52c969d JB |
3 | import fs from 'fs'; |
4 | import path from 'path'; | |
5 | import { fileURLToPath } from 'url'; | |
6 | ||
6c1761d4 | 7 | import type { JSONSchemaType } from 'ajv'; |
b52c969d | 8 | |
78202038 | 9 | import { OCPP16ServiceUtils } from './OCPP16ServiceUtils'; |
8114d10e | 10 | import OCPPError from '../../../exception/OCPPError'; |
6c1761d4 JB |
11 | import type { JsonObject, JsonType } from '../../../types/JsonType'; |
12 | import type { OCPP16MeterValuesRequest } from '../../../types/ocpp/1.6/MeterValues'; | |
b52c969d | 13 | import { |
27782dbc JB |
14 | type OCPP16BootNotificationRequest, |
15 | type OCPP16DataTransferRequest, | |
c9a4f9ea | 16 | type OCPP16DiagnosticsStatusNotificationRequest, |
e9a4164c | 17 | type OCPP16FirmwareStatusNotificationRequest, |
27782dbc | 18 | type OCPP16HeartbeatRequest, |
b52c969d | 19 | OCPP16RequestCommand, |
27782dbc | 20 | type OCPP16StatusNotificationRequest, |
b52c969d | 21 | } from '../../../types/ocpp/1.6/Requests'; |
6c1761d4 | 22 | import type { |
b52c969d JB |
23 | OCPP16AuthorizeRequest, |
24 | OCPP16StartTransactionRequest, | |
25 | OCPP16StopTransactionRequest, | |
26 | } from '../../../types/ocpp/1.6/Transaction'; | |
8114d10e | 27 | import { ErrorType } from '../../../types/ocpp/ErrorType'; |
d270cc87 | 28 | import { OCPPVersion } from '../../../types/ocpp/OCPPVersion'; |
6c1761d4 | 29 | import type { RequestParams } from '../../../types/ocpp/Requests'; |
8114d10e | 30 | import Constants from '../../../utils/Constants'; |
c0560973 | 31 | import Utils from '../../../utils/Utils'; |
8114d10e | 32 | import type ChargingStation from '../../ChargingStation'; |
4d20f040 | 33 | import OCPPConstants from '../OCPPConstants'; |
8114d10e JB |
34 | import OCPPRequestService from '../OCPPRequestService'; |
35 | import type OCPPResponseService from '../OCPPResponseService'; | |
c0560973 | 36 | |
909dcf2d JB |
37 | const moduleName = 'OCPP16RequestService'; |
38 | ||
c0560973 | 39 | export default class OCPP16RequestService extends OCPPRequestService { |
b3fc3ff5 | 40 | protected jsonSchemas: Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>; |
b52c969d | 41 | |
08f130a0 | 42 | public constructor(ocppResponseService: OCPPResponseService) { |
909dcf2d | 43 | if (new.target?.name === moduleName) { |
06127450 | 44 | throw new TypeError(`Cannot construct ${new.target?.name} instances directly`); |
9f2e3130 | 45 | } |
d270cc87 | 46 | super(OCPPVersion.VERSION_16, ocppResponseService); |
b52c969d JB |
47 | this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>([ |
48 | [ | |
49 | OCPP16RequestCommand.AUTHORIZE, | |
e9a4164c JB |
50 | this.parseJsonSchemaFile<OCPP16AuthorizeRequest>( |
51 | '../../../assets/json-schemas/ocpp/1.6/Authorize.json' | |
52 | ), | |
b52c969d JB |
53 | ], |
54 | [ | |
55 | OCPP16RequestCommand.BOOT_NOTIFICATION, | |
e9a4164c JB |
56 | this.parseJsonSchemaFile<OCPP16BootNotificationRequest>( |
57 | '../../../assets/json-schemas/ocpp/1.6/BootNotification.json' | |
58 | ), | |
b52c969d JB |
59 | ], |
60 | [ | |
61 | OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, | |
e9a4164c JB |
62 | this.parseJsonSchemaFile<OCPP16DiagnosticsStatusNotificationRequest>( |
63 | '../../../assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotification.json' | |
64 | ), | |
b52c969d JB |
65 | ], |
66 | [ | |
67 | OCPP16RequestCommand.HEARTBEAT, | |
e9a4164c JB |
68 | this.parseJsonSchemaFile<OCPP16HeartbeatRequest>( |
69 | '../../../assets/json-schemas/ocpp/1.6/Heartbeat.json' | |
70 | ), | |
b52c969d JB |
71 | ], |
72 | [ | |
73 | OCPP16RequestCommand.METER_VALUES, | |
e9a4164c JB |
74 | this.parseJsonSchemaFile<OCPP16MeterValuesRequest>( |
75 | '../../../assets/json-schemas/ocpp/1.6/MeterValues.json' | |
76 | ), | |
b52c969d JB |
77 | ], |
78 | [ | |
79 | OCPP16RequestCommand.STATUS_NOTIFICATION, | |
e9a4164c JB |
80 | this.parseJsonSchemaFile<OCPP16StatusNotificationRequest>( |
81 | '../../../assets/json-schemas/ocpp/1.6/StatusNotification.json' | |
82 | ), | |
b52c969d JB |
83 | ], |
84 | [ | |
85 | OCPP16RequestCommand.START_TRANSACTION, | |
e9a4164c JB |
86 | this.parseJsonSchemaFile<OCPP16StartTransactionRequest>( |
87 | '../../../assets/json-schemas/ocpp/1.6/StartTransaction.json' | |
88 | ), | |
b52c969d JB |
89 | ], |
90 | [ | |
91 | OCPP16RequestCommand.STOP_TRANSACTION, | |
e9a4164c JB |
92 | this.parseJsonSchemaFile<OCPP16StopTransactionRequest>( |
93 | '../../../assets/json-schemas/ocpp/1.6/StopTransaction.json' | |
94 | ), | |
b52c969d | 95 | ], |
91a7d3ea JB |
96 | [ |
97 | OCPP16RequestCommand.DATA_TRANSFER, | |
e9a4164c JB |
98 | this.parseJsonSchemaFile<OCPP16DataTransferRequest>( |
99 | '../../../assets/json-schemas/ocpp/1.6/DataTransfer.json' | |
100 | ), | |
91a7d3ea | 101 | ], |
c9a4f9ea JB |
102 | [ |
103 | OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, | |
e9a4164c JB |
104 | this.parseJsonSchemaFile<OCPP16FirmwareStatusNotificationRequest>( |
105 | '../../../assets/json-schemas/ocpp/1.6/FirmwareStatusNotification.json' | |
106 | ), | |
c9a4f9ea | 107 | ], |
b52c969d | 108 | ]); |
9952c548 | 109 | this.buildRequestPayload.bind(this); |
9f2e3130 JB |
110 | } |
111 | ||
6c1761d4 | 112 | public async requestHandler<RequestType extends JsonType, ResponseType extends JsonType>( |
08f130a0 | 113 | chargingStation: ChargingStation, |
94a464f9 | 114 | commandName: OCPP16RequestCommand, |
5cc4b63b | 115 | commandParams?: JsonType, |
be9b0d50 | 116 | params?: RequestParams |
6c1761d4 | 117 | ): Promise<ResponseType> { |
ed6cfcff | 118 | if (OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true) { |
f22266fd | 119 | return (await this.sendMessage( |
08f130a0 | 120 | chargingStation, |
94a464f9 | 121 | Utils.generateUUID(), |
18bf8274 | 122 | this.buildRequestPayload<RequestType>(chargingStation, commandName, commandParams), |
94a464f9 JB |
123 | commandName, |
124 | params | |
6c1761d4 | 125 | )) as unknown as ResponseType; |
94a464f9 | 126 | } |
e909d2a7 | 127 | // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). |
94a464f9 JB |
128 | throw new OCPPError( |
129 | ErrorType.NOT_SUPPORTED, | |
6c8f5d90 | 130 | `Unsupported OCPP command '${commandName}'`, |
94a464f9 | 131 | commandName, |
7369e417 | 132 | commandParams |
94a464f9 | 133 | ); |
c0560973 JB |
134 | } |
135 | ||
5cc4b63b | 136 | private buildRequestPayload<Request extends JsonType>( |
08f130a0 | 137 | chargingStation: ChargingStation, |
78085c42 | 138 | commandName: OCPP16RequestCommand, |
5cc4b63b | 139 | commandParams?: JsonType |
f22266fd | 140 | ): Request { |
68c993d5 | 141 | let connectorId: number; |
cf058664 | 142 | let energyActiveImportRegister: number; |
5cc4b63b | 143 | commandParams = commandParams as JsonObject; |
78085c42 | 144 | switch (commandName) { |
78085c42 | 145 | case OCPP16RequestCommand.BOOT_NOTIFICATION: |
78085c42 | 146 | case OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION: |
22e0d48e | 147 | case OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION: |
78085c42 | 148 | case OCPP16RequestCommand.METER_VALUES: |
78085c42 | 149 | case OCPP16RequestCommand.STATUS_NOTIFICATION: |
36c462a4 JB |
150 | case OCPP16RequestCommand.DATA_TRANSFER: |
151 | return commandParams as unknown as Request; | |
152 | case OCPP16RequestCommand.AUTHORIZE: | |
78085c42 | 153 | return { |
36c462a4 JB |
154 | idTag: Constants.DEFAULT_IDTAG, |
155 | ...commandParams, | |
f22266fd | 156 | } as unknown as Request; |
36c462a4 JB |
157 | case OCPP16RequestCommand.HEARTBEAT: |
158 | return OCPPConstants.OCPP_REQUEST_EMPTY as unknown as Request; | |
78085c42 JB |
159 | case OCPP16RequestCommand.START_TRANSACTION: |
160 | return { | |
36c462a4 JB |
161 | idTag: Constants.DEFAULT_IDTAG, |
162 | meterStart: chargingStation.getEnergyActiveImportRegisterByConnectorId( | |
18bf8274 JB |
163 | commandParams?.connectorId as number, |
164 | true | |
36c462a4 JB |
165 | ), |
166 | timestamp: new Date(), | |
167 | ...commandParams, | |
f22266fd | 168 | } as unknown as Request; |
78085c42 | 169 | case OCPP16RequestCommand.STOP_TRANSACTION: |
f1e731bd | 170 | chargingStation.getTransactionDataMeterValues() && |
f1e731bd JB |
171 | (connectorId = chargingStation.getConnectorIdByTransactionId( |
172 | commandParams?.transactionId as number | |
173 | )); | |
36c462a4 JB |
174 | energyActiveImportRegister = chargingStation.getEnergyActiveImportRegisterByTransactionId( |
175 | commandParams?.transactionId as number, | |
176 | true | |
177 | ); | |
78085c42 | 178 | return { |
36c462a4 JB |
179 | idTag: chargingStation.getTransactionIdTag(commandParams?.transactionId as number), |
180 | meterStop: energyActiveImportRegister, | |
181 | timestamp: new Date(), | |
182 | ...(chargingStation.getTransactionDataMeterValues() && { | |
183 | transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues( | |
184 | chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue, | |
185 | OCPP16ServiceUtils.buildTransactionEndMeterValue( | |
186 | chargingStation, | |
187 | connectorId, | |
188 | energyActiveImportRegister | |
189 | ) | |
190 | ), | |
78085c42 | 191 | }), |
36c462a4 | 192 | ...commandParams, |
f22266fd | 193 | } as unknown as Request; |
78085c42 | 194 | default: |
e909d2a7 | 195 | // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). |
78085c42 JB |
196 | throw new OCPPError( |
197 | ErrorType.NOT_SUPPORTED, | |
198 | // eslint-disable-next-line @typescript-eslint/restrict-template-expressions | |
6c8f5d90 | 199 | `Unsupported OCPP command '${commandName}'`, |
78085c42 | 200 | commandName, |
7369e417 | 201 | commandParams |
78085c42 JB |
202 | ); |
203 | } | |
204 | } | |
e9a4164c JB |
205 | |
206 | private parseJsonSchemaFile<T extends JsonType>(relativePath: string): JSONSchemaType<T> { | |
207 | return JSON.parse( | |
208 | fs.readFileSync( | |
209 | path.resolve(path.dirname(fileURLToPath(import.meta.url)), relativePath), | |
210 | 'utf8' | |
211 | ) | |
212 | ) as JSONSchemaType<T>; | |
213 | } | |
c0560973 | 214 | } |