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