README.md: add more documentation for cs template
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16RequestService.ts
1 import { AuthorizeRequest, OCPP16AuthorizeResponse, OCPP16StartTransactionResponse, OCPP16StopTransactionReason, OCPP16StopTransactionResponse, StartTransactionRequest, StopTransactionRequest } from '../../../types/ocpp/1.6/Transaction';
2 import { HeartbeatRequest, OCPP16BootNotificationRequest, OCPP16IncomingRequestCommand, OCPP16RequestCommand, StatusNotificationRequest } from '../../../types/ocpp/1.6/Requests';
3 import { MeterValue, MeterValueLocation, MeterValuePhase, MeterValueUnit, MeterValuesRequest, OCPP16MeterValueMeasurand, OCPP16SampledValue } from '../../../types/ocpp/1.6/MeterValues';
4
5 import { ACElectricUtils } from '../../../utils/ElectricUtils';
6 import Constants from '../../../utils/Constants';
7 import { CurrentOutType } from '../../../types/ChargingStationTemplate';
8 import MeasurandValues from '../../../types/MeasurandValues';
9 import { MessageType } from '../../../types/ocpp/MessageType';
10 import { OCPP16BootNotificationResponse } from '../../../types/ocpp/1.6/Responses';
11 import { OCPP16ChargePointErrorCode } from '../../../types/ocpp/1.6/ChargePointErrorCode';
12 import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
13 import { OCPP16StandardParametersKey } from '../../../types/ocpp/1.6/Configuration';
14 import OCPPError from '../../OcppError';
15 import OCPPRequestService from '../OCPPRequestService';
16 import Utils from '../../../utils/Utils';
17 import logger from '../../../utils/Logger';
18
19 export default class OCPP16RequestService extends OCPPRequestService {
20 public async sendHeartbeat(): Promise<void> {
21 try {
22 const payload: HeartbeatRequest = {};
23 await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.HEARTBEAT);
24 } catch (error) {
25 this.handleRequestError(OCPP16RequestCommand.HEARTBEAT, error);
26 }
27 }
28
29 public async sendBootNotification(chargePointModel: string, chargePointVendor: string, chargeBoxSerialNumber?: string, firmwareVersion?: string, chargePointSerialNumber?: string, iccid?: string, imsi?: string, meterSerialNumber?: string, meterType?: string): Promise<OCPP16BootNotificationResponse> {
30 try {
31 const payload: OCPP16BootNotificationRequest = {
32 chargePointModel,
33 chargePointVendor,
34 ...!Utils.isUndefined(chargeBoxSerialNumber) && { chargeBoxSerialNumber },
35 ...!Utils.isUndefined(chargePointSerialNumber) && { chargePointSerialNumber },
36 ...!Utils.isUndefined(firmwareVersion) && { firmwareVersion },
37 ...!Utils.isUndefined(iccid) && { iccid },
38 ...!Utils.isUndefined(imsi) && { imsi },
39 ...!Utils.isUndefined(meterSerialNumber) && { meterSerialNumber },
40 ...!Utils.isUndefined(meterType) && { meterType }
41 };
42 return await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.BOOT_NOTIFICATION) as OCPP16BootNotificationResponse;
43 } catch (error) {
44 this.handleRequestError(OCPP16RequestCommand.BOOT_NOTIFICATION, error);
45 }
46 }
47
48 public async sendStatusNotification(connectorId: number, status: OCPP16ChargePointStatus,
49 errorCode: OCPP16ChargePointErrorCode = OCPP16ChargePointErrorCode.NO_ERROR): Promise<void> {
50 try {
51 const payload: StatusNotificationRequest = {
52 connectorId,
53 errorCode,
54 status,
55 };
56 await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.STATUS_NOTIFICATION);
57 } catch (error) {
58 this.handleRequestError(OCPP16RequestCommand.STATUS_NOTIFICATION, error);
59 }
60 }
61
62 public async sendAuthorize(idTag?: string): Promise<OCPP16AuthorizeResponse> {
63 try {
64 const payload: AuthorizeRequest = {
65 ...!Utils.isUndefined(idTag) ? { idTag } : { idTag: Constants.TRANSACTION_DEFAULT_TAGID },
66 };
67 return await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.AUTHORIZE) as OCPP16AuthorizeResponse;
68 } catch (error) {
69 this.handleRequestError(OCPP16RequestCommand.AUTHORIZE, error);
70 }
71 }
72
73 public async sendStartTransaction(connectorId: number, idTag?: string): Promise<OCPP16StartTransactionResponse> {
74 try {
75 const payload: StartTransactionRequest = {
76 connectorId,
77 ...!Utils.isUndefined(idTag) ? { idTag } : { idTag: Constants.TRANSACTION_DEFAULT_TAGID },
78 meterStart: 0,
79 timestamp: new Date().toISOString(),
80 };
81 return await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.START_TRANSACTION) as OCPP16StartTransactionResponse;
82 } catch (error) {
83 this.handleRequestError(OCPP16RequestCommand.START_TRANSACTION, error);
84 }
85 }
86
87 public async sendStopTransaction(transactionId: number, meterStop: number, idTag?: string,
88 reason: OCPP16StopTransactionReason = OCPP16StopTransactionReason.NONE): Promise<OCPP16StopTransactionResponse> {
89 try {
90 const payload: StopTransactionRequest = {
91 transactionId,
92 ...!Utils.isUndefined(idTag) && { idTag },
93 meterStop: meterStop,
94 timestamp: new Date().toISOString(),
95 ...reason && { reason },
96 };
97 return await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.STOP_TRANSACTION) as OCPP16StartTransactionResponse;
98 } catch (error) {
99 this.handleRequestError(OCPP16RequestCommand.STOP_TRANSACTION, error);
100 }
101 }
102
103 // eslint-disable-next-line consistent-this
104 public async sendMeterValues(connectorId: number, transactionId: number, interval: number, self: OCPPRequestService, debug = false): Promise<void> {
105 try {
106 const meterValue: MeterValue = {
107 timestamp: new Date().toISOString(),
108 sampledValue: [],
109 };
110 const meterValuesTemplate: OCPP16SampledValue[] = self.chargingStation.getConnector(connectorId).MeterValues;
111 for (let index = 0; index < meterValuesTemplate.length; index++) {
112 const connector = self.chargingStation.getConnector(connectorId);
113 // SoC measurand
114 if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === OCPP16MeterValueMeasurand.STATE_OF_CHARGE && self.chargingStation.getConfigurationKey(OCPP16StandardParametersKey.MeterValuesSampledData).value.includes(OCPP16MeterValueMeasurand.STATE_OF_CHARGE)) {
115 meterValue.sampledValue.push({
116 ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.PERCENT },
117 ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
118 measurand: meterValuesTemplate[index].measurand,
119 ...!Utils.isUndefined(meterValuesTemplate[index].location) ? { location: meterValuesTemplate[index].location } : { location: MeterValueLocation.EV },
120 ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: Utils.getRandomInt(100).toString() },
121 });
122 const sampledValuesIndex = meterValue.sampledValue.length - 1;
123 if (Utils.convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > 100 || debug) {
124 logger.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ? meterValue.sampledValue[sampledValuesIndex].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/100`);
125 }
126 // Voltage measurand
127 } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === OCPP16MeterValueMeasurand.VOLTAGE && self.chargingStation.getConfigurationKey(OCPP16StandardParametersKey.MeterValuesSampledData).value.includes(OCPP16MeterValueMeasurand.VOLTAGE)) {
128 const voltageMeasurandValue = Utils.getRandomFloatRounded(self.chargingStation.getVoltageOut() + self.chargingStation.getVoltageOut() * 0.1, self.chargingStation.getVoltageOut() - self.chargingStation.getVoltageOut() * 0.1);
129 meterValue.sampledValue.push({
130 ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.VOLT },
131 ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
132 measurand: meterValuesTemplate[index].measurand,
133 ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
134 ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: voltageMeasurandValue.toString() },
135 });
136 for (let phase = 1; self.chargingStation.getNumberOfPhases() === 3 && phase <= self.chargingStation.getNumberOfPhases(); phase++) {
137 let phaseValue: string;
138 if (self.chargingStation.getVoltageOut() >= 0 && self.chargingStation.getVoltageOut() <= 250) {
139 phaseValue = `L${phase}-N`;
140 } else if (self.chargingStation.getVoltageOut() > 250) {
141 phaseValue = `L${phase}-L${(phase + 1) % self.chargingStation.getNumberOfPhases() !== 0 ? (phase + 1) % self.chargingStation.getNumberOfPhases() : self.chargingStation.getNumberOfPhases()}`;
142 }
143 meterValue.sampledValue.push({
144 ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.VOLT },
145 ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
146 measurand: meterValuesTemplate[index].measurand,
147 ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
148 ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: voltageMeasurandValue.toString() },
149 phase: phaseValue as MeterValuePhase,
150 });
151 }
152 // Power.Active.Import measurand
153 } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT && self.chargingStation.getConfigurationKey(OCPP16StandardParametersKey.MeterValuesSampledData).value.includes(OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT)) {
154 // FIXME: factor out powerDivider checks
155 if (Utils.isUndefined(self.chargingStation.stationInfo.powerDivider)) {
156 const errMsg = `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`;
157 logger.error(errMsg);
158 throw Error(errMsg);
159 } else if (self.chargingStation.stationInfo.powerDivider && self.chargingStation.stationInfo.powerDivider <= 0) {
160 const errMsg = `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider have zero or below value ${self.chargingStation.stationInfo.powerDivider}`;
161 logger.error(errMsg);
162 throw Error(errMsg);
163 }
164 const errMsg = `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${self.chargingStation.getCurrentOutType()} currentOutType in template file ${self.chargingStation.stationTemplateFile}, cannot calculate ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`;
165 const powerMeasurandValues = {} as MeasurandValues;
166 const maxPower = Math.round(self.chargingStation.stationInfo.maxPower / self.chargingStation.stationInfo.powerDivider);
167 const maxPowerPerPhase = Math.round((self.chargingStation.stationInfo.maxPower / self.chargingStation.stationInfo.powerDivider) / self.chargingStation.getNumberOfPhases());
168 switch (self.chargingStation.getCurrentOutType()) {
169 case CurrentOutType.AC:
170 if (Utils.isUndefined(meterValuesTemplate[index].value)) {
171 powerMeasurandValues.L1 = Utils.getRandomFloatRounded(maxPowerPerPhase);
172 powerMeasurandValues.L2 = 0;
173 powerMeasurandValues.L3 = 0;
174 if (self.chargingStation.getNumberOfPhases() === 3) {
175 powerMeasurandValues.L2 = Utils.getRandomFloatRounded(maxPowerPerPhase);
176 powerMeasurandValues.L3 = Utils.getRandomFloatRounded(maxPowerPerPhase);
177 }
178 powerMeasurandValues.allPhases = Utils.roundTo(powerMeasurandValues.L1 + powerMeasurandValues.L2 + powerMeasurandValues.L3, 2);
179 }
180 break;
181 case CurrentOutType.DC:
182 if (Utils.isUndefined(meterValuesTemplate[index].value)) {
183 powerMeasurandValues.allPhases = Utils.getRandomFloatRounded(maxPower);
184 }
185 break;
186 default:
187 logger.error(errMsg);
188 throw Error(errMsg);
189 }
190 meterValue.sampledValue.push({
191 ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.WATT },
192 ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
193 measurand: meterValuesTemplate[index].measurand,
194 ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
195 ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: powerMeasurandValues.allPhases.toString() },
196 });
197 const sampledValuesIndex = meterValue.sampledValue.length - 1;
198 if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxPower || debug) {
199 logger.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ? meterValue.sampledValue[sampledValuesIndex].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxPower}`);
200 }
201 for (let phase = 1; self.chargingStation.getNumberOfPhases() === 3 && phase <= self.chargingStation.getNumberOfPhases(); phase++) {
202 const phaseValue = `L${phase}-N`;
203 meterValue.sampledValue.push({
204 ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.WATT },
205 ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
206 ...!Utils.isUndefined(meterValuesTemplate[index].measurand) && { measurand: meterValuesTemplate[index].measurand },
207 ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
208 ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: powerMeasurandValues[`L${phase}`] as string },
209 phase: phaseValue as MeterValuePhase,
210 });
211 }
212 // Current.Import measurand
213 } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === OCPP16MeterValueMeasurand.CURRENT_IMPORT && self.chargingStation.getConfigurationKey(OCPP16StandardParametersKey.MeterValuesSampledData).value.includes(OCPP16MeterValueMeasurand.CURRENT_IMPORT)) {
214 // FIXME: factor out powerDivider checks
215 if (Utils.isUndefined(self.chargingStation.stationInfo.powerDivider)) {
216 const errMsg = `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`;
217 logger.error(errMsg);
218 throw Error(errMsg);
219 } else if (self.chargingStation.stationInfo.powerDivider && self.chargingStation.stationInfo.powerDivider <= 0) {
220 const errMsg = `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider have zero or below value ${self.chargingStation.stationInfo.powerDivider}`;
221 logger.error(errMsg);
222 throw Error(errMsg);
223 }
224 const errMsg = `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${self.chargingStation.getCurrentOutType()} currentOutType in template file ${self.chargingStation.stationTemplateFile}, cannot calculate ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`;
225 const currentMeasurandValues: MeasurandValues = {} as MeasurandValues;
226 let maxAmperage: number;
227 switch (self.chargingStation.getCurrentOutType()) {
228 case CurrentOutType.AC:
229 maxAmperage = ACElectricUtils.amperagePerPhaseFromPower(self.chargingStation.getNumberOfPhases(), self.chargingStation.stationInfo.maxPower / self.chargingStation.stationInfo.powerDivider, self.chargingStation.getVoltageOut());
230 if (Utils.isUndefined(meterValuesTemplate[index].value)) {
231 currentMeasurandValues.L1 = Utils.getRandomFloatRounded(maxAmperage);
232 currentMeasurandValues.L2 = 0;
233 currentMeasurandValues.L3 = 0;
234 if (self.chargingStation.getNumberOfPhases() === 3) {
235 currentMeasurandValues.L2 = Utils.getRandomFloatRounded(maxAmperage);
236 currentMeasurandValues.L3 = Utils.getRandomFloatRounded(maxAmperage);
237 }
238 currentMeasurandValues.allPhases = Utils.roundTo((currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) / self.chargingStation.getNumberOfPhases(), 2);
239 }
240 break;
241 case CurrentOutType.DC:
242 maxAmperage = ACElectricUtils.amperageTotalFromPower(self.chargingStation.stationInfo.maxPower / self.chargingStation.stationInfo.powerDivider, self.chargingStation.getVoltageOut());
243 if (Utils.isUndefined(meterValuesTemplate[index].value)) {
244 currentMeasurandValues.allPhases = Utils.getRandomFloatRounded(maxAmperage);
245 }
246 break;
247 default:
248 logger.error(errMsg);
249 throw Error(errMsg);
250 }
251 meterValue.sampledValue.push({
252 ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.AMP },
253 ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
254 measurand: meterValuesTemplate[index].measurand,
255 ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
256 ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: currentMeasurandValues.allPhases.toString() },
257 });
258 const sampledValuesIndex = meterValue.sampledValue.length - 1;
259 if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxAmperage || debug) {
260 logger.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ? meterValue.sampledValue[sampledValuesIndex].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxAmperage}`);
261 }
262 for (let phase = 1; self.chargingStation.getNumberOfPhases() === 3 && phase <= self.chargingStation.getNumberOfPhases(); phase++) {
263 const phaseValue = `L${phase}`;
264 meterValue.sampledValue.push({
265 ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.AMP },
266 ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
267 ...!Utils.isUndefined(meterValuesTemplate[index].measurand) && { measurand: meterValuesTemplate[index].measurand },
268 ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
269 ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: currentMeasurandValues[phaseValue] as string },
270 phase: phaseValue as MeterValuePhase,
271 });
272 }
273 // Energy.Active.Import.Register measurand (default)
274 } else if (!meterValuesTemplate[index].measurand || meterValuesTemplate[index].measurand === OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) {
275 // FIXME: factor out powerDivider checks
276 if (Utils.isUndefined(self.chargingStation.stationInfo.powerDivider)) {
277 const errMsg = `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`;
278 logger.error(errMsg);
279 throw Error(errMsg);
280 } else if (self.chargingStation.stationInfo.powerDivider && self.chargingStation.stationInfo.powerDivider <= 0) {
281 const errMsg = `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider have zero or below value ${self.chargingStation.stationInfo.powerDivider}`;
282 logger.error(errMsg);
283 throw Error(errMsg);
284 }
285 if (Utils.isUndefined(meterValuesTemplate[index].value)) {
286 const measurandValue = Utils.getRandomInt(self.chargingStation.stationInfo.maxPower / (self.chargingStation.stationInfo.powerDivider * 3600000) * interval);
287 // Persist previous value in connector
288 if (connector && !Utils.isNullOrUndefined(connector.lastEnergyActiveImportRegisterValue) && connector.lastEnergyActiveImportRegisterValue >= 0) {
289 connector.lastEnergyActiveImportRegisterValue += measurandValue;
290 } else {
291 connector.lastEnergyActiveImportRegisterValue = 0;
292 }
293 }
294 meterValue.sampledValue.push({
295 ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.WATT_HOUR },
296 ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
297 ...!Utils.isUndefined(meterValuesTemplate[index].measurand) && { measurand: meterValuesTemplate[index].measurand },
298 ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
299 ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } :
300 { value: connector.lastEnergyActiveImportRegisterValue.toString() },
301 });
302 const sampledValuesIndex = meterValue.sampledValue.length - 1;
303 const maxConsumption = Math.round(self.chargingStation.stationInfo.maxPower * 3600 / (self.chargingStation.stationInfo.powerDivider * interval));
304 if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxConsumption || debug) {
305 logger.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ? meterValue.sampledValue[sampledValuesIndex].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxConsumption}`);
306 }
307 // Unsupported measurand
308 } else {
309 logger.info(`${self.chargingStation.logPrefix()} Unsupported MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} on connectorId ${connectorId}`);
310 }
311 }
312 const payload: MeterValuesRequest = {
313 connectorId,
314 transactionId,
315 meterValue,
316 };
317 await self.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES);
318 } catch (error) {
319 self.handleRequestError(OCPP16RequestCommand.METER_VALUES, error);
320 }
321 }
322
323 public async sendError(messageId: string, error: OCPPError, commandName: OCPP16RequestCommand | OCPP16IncomingRequestCommand): Promise<unknown> {
324 // Send error
325 return this.sendMessage(messageId, error, MessageType.CALL_ERROR_MESSAGE, commandName);
326 }
327 }