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
, MeterValueContext
, MeterValueLocation
, MeterValuePhase
, MeterValueUnit
, MeterValuesRequest
, OCPP16MeterValueMeasurand
, OCPP16SampledValue
} from
'../../../types/ocpp/1.6/MeterValues';
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';
19 export default class OCPP16RequestService
extends OCPPRequestService
{
20 public async sendHeartbeat(): Promise
<void> {
22 const payload
: HeartbeatRequest
= {};
23 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.HEARTBEAT
);
25 this.handleRequestError(OCPP16RequestCommand
.HEARTBEAT
, error
);
29 public async sendBootNotification(chargePointModel
: string, chargePointVendor
: string, chargeBoxSerialNumber
?: string, firmwareVersion
?: string, chargePointSerialNumber
?: string, iccid
?: string, imsi
?: string, meterSerialNumber
?: string, meterType
?: string): Promise
<OCPP16BootNotificationResponse
> {
31 const payload
: OCPP16BootNotificationRequest
= {
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
}
42 return await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.BOOT_NOTIFICATION
) as OCPP16BootNotificationResponse
;
44 this.handleRequestError(OCPP16RequestCommand
.BOOT_NOTIFICATION
, error
);
48 public async sendStatusNotification(connectorId
: number, status: OCPP16ChargePointStatus
,
49 errorCode
: OCPP16ChargePointErrorCode
= OCPP16ChargePointErrorCode
.NO_ERROR
): Promise
<void> {
51 const payload
: StatusNotificationRequest
= {
56 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.STATUS_NOTIFICATION
);
58 this.handleRequestError(OCPP16RequestCommand
.STATUS_NOTIFICATION
, error
);
62 public async sendAuthorize(idTag
?: string): Promise
<OCPP16AuthorizeResponse
> {
64 const payload
: AuthorizeRequest
= {
65 ...!Utils
.isUndefined(idTag
) ? { idTag
} : { idTag
: Constants
.TRANSACTION_DEFAULT_IDTAG
},
67 return await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.AUTHORIZE
) as OCPP16AuthorizeResponse
;
69 this.handleRequestError(OCPP16RequestCommand
.AUTHORIZE
, error
);
73 public async sendStartTransaction(connectorId
: number, idTag
?: string): Promise
<OCPP16StartTransactionResponse
> {
75 const payload
: StartTransactionRequest
= {
77 ...!Utils
.isUndefined(idTag
) ? { idTag
} : { idTag
: Constants
.TRANSACTION_DEFAULT_IDTAG
},
79 timestamp
: new Date().toISOString(),
81 const response
= await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.START_TRANSACTION
) as OCPP16StartTransactionResponse
;
82 await this.sendTransactionBeginMeterValues(connectorId
, response
.transactionId
, 0);
85 this.handleRequestError(OCPP16RequestCommand
.START_TRANSACTION
, error
);
89 public async sendStopTransaction(transactionId
: number, meterStop
: number, idTag
?: string,
90 reason
: OCPP16StopTransactionReason
= OCPP16StopTransactionReason
.NONE
): Promise
<OCPP16StopTransactionResponse
> {
92 const payload
: StopTransactionRequest
= {
94 ...!Utils
.isUndefined(idTag
) && { idTag
},
96 timestamp
: new Date().toISOString(),
97 ...reason
&& { reason
},
99 let connectorId
: number;
100 for (const connector
in this.chargingStation
.connectors
) {
101 if (Utils
.convertToInt(connector
) > 0 && this.chargingStation
.getConnector(Utils
.convertToInt(connector
))?.transactionId
=== transactionId
) {
102 connectorId
= Utils
.convertToInt(connector
);
105 await this.sendTransactionEndMeterValues(connectorId
, transactionId
, meterStop
);
106 return await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.STOP_TRANSACTION
) as OCPP16StartTransactionResponse
;
108 this.handleRequestError(OCPP16RequestCommand
.STOP_TRANSACTION
, error
);
112 // eslint-disable-next-line consistent-this
113 public async sendMeterValues(connectorId
: number, transactionId
: number, interval
: number, self: OCPPRequestService
, debug
= false): Promise
<void> {
115 const meterValue
: MeterValue
= {
116 timestamp
: new Date().toISOString(),
119 const meterValuesTemplate
: OCPP16SampledValue
[] = self.chargingStation
.getConnector(connectorId
).MeterValues
;
120 for (let index
= 0; index
< meterValuesTemplate
.length
; index
++) {
121 const connector
= self.chargingStation
.getConnector(connectorId
);
123 if (meterValuesTemplate
[index
].measurand
&& meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
&& self.chargingStation
.getConfigurationKey(OCPP16StandardParametersKey
.MeterValuesSampledData
).value
.includes(OCPP16MeterValueMeasurand
.STATE_OF_CHARGE
)) {
124 meterValue
.sampledValue
.push({
125 ...!Utils
.isUndefined(meterValuesTemplate
[index
].unit
) ? { unit
: meterValuesTemplate
[index
].unit
} : { unit
: MeterValueUnit
.PERCENT
},
126 ...!Utils
.isUndefined(meterValuesTemplate
[index
].context
) && { context
: meterValuesTemplate
[index
].context
},
127 measurand
: meterValuesTemplate
[index
].measurand
,
128 ...!Utils
.isUndefined(meterValuesTemplate
[index
].location
) ? { location
: meterValuesTemplate
[index
].location
} : { location
: MeterValueLocation
.EV
},
129 ...!Utils
.isUndefined(meterValuesTemplate
[index
].value
) ? { value
: meterValuesTemplate
[index
].value
} : { value
: Utils
.getRandomInt(100).toString() },
131 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
132 if (Utils
.convertToInt(meterValue
.sampledValue
[sampledValuesIndex
].value
) > 100 || debug
) {
133 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`);
136 } else if (meterValuesTemplate
[index
].measurand
&& meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.VOLTAGE
&& self.chargingStation
.getConfigurationKey(OCPP16StandardParametersKey
.MeterValuesSampledData
).value
.includes(OCPP16MeterValueMeasurand
.VOLTAGE
)) {
137 const voltageMeasurandValue
= Utils
.getRandomFloatRounded(self.chargingStation
.getVoltageOut() + self.chargingStation
.getVoltageOut() * 0.1, self.chargingStation
.getVoltageOut() - self.chargingStation
.getVoltageOut() * 0.1);
138 meterValue
.sampledValue
.push({
139 ...!Utils
.isUndefined(meterValuesTemplate
[index
].unit
) ? { unit
: meterValuesTemplate
[index
].unit
} : { unit
: MeterValueUnit
.VOLT
},
140 ...!Utils
.isUndefined(meterValuesTemplate
[index
].context
) && { context
: meterValuesTemplate
[index
].context
},
141 measurand
: meterValuesTemplate
[index
].measurand
,
142 ...!Utils
.isUndefined(meterValuesTemplate
[index
].location
) && { location
: meterValuesTemplate
[index
].location
},
143 ...!Utils
.isUndefined(meterValuesTemplate
[index
].value
) ? { value
: meterValuesTemplate
[index
].value
} : { value
: voltageMeasurandValue
.toString() },
145 for (let phase
= 1; self.chargingStation
.getNumberOfPhases() === 3 && phase
<= self.chargingStation
.getNumberOfPhases(); phase
++) {
146 let phaseValue
: string;
147 if (self.chargingStation
.getVoltageOut() >= 0 && self.chargingStation
.getVoltageOut() <= 250) {
148 phaseValue
= `L${phase}-N`;
149 } else if (self.chargingStation
.getVoltageOut() > 250) {
150 phaseValue
= `L${phase}-L${(phase + 1) % self.chargingStation.getNumberOfPhases() !== 0 ? (phase + 1) % self.chargingStation.getNumberOfPhases() : self.chargingStation.getNumberOfPhases()}`;
152 meterValue
.sampledValue
.push({
153 ...!Utils
.isUndefined(meterValuesTemplate
[index
].unit
) ? { unit
: meterValuesTemplate
[index
].unit
} : { unit
: MeterValueUnit
.VOLT
},
154 ...!Utils
.isUndefined(meterValuesTemplate
[index
].context
) && { context
: meterValuesTemplate
[index
].context
},
155 measurand
: meterValuesTemplate
[index
].measurand
,
156 ...!Utils
.isUndefined(meterValuesTemplate
[index
].location
) && { location
: meterValuesTemplate
[index
].location
},
157 ...!Utils
.isUndefined(meterValuesTemplate
[index
].value
) ? { value
: meterValuesTemplate
[index
].value
} : { value
: voltageMeasurandValue
.toString() },
158 phase
: phaseValue
as MeterValuePhase
,
161 // Power.Active.Import measurand
162 } else if (meterValuesTemplate
[index
].measurand
&& meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
&& self.chargingStation
.getConfigurationKey(OCPP16StandardParametersKey
.MeterValuesSampledData
).value
.includes(OCPP16MeterValueMeasurand
.POWER_ACTIVE_IMPORT
)) {
163 // FIXME: factor out powerDivider checks
164 if (Utils
.isUndefined(self.chargingStation
.stationInfo
.powerDivider
)) {
165 const errMsg
= `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`;
166 logger
.error(errMsg
);
168 } else if (self.chargingStation
.stationInfo
.powerDivider
&& self.chargingStation
.stationInfo
.powerDivider
<= 0) {
169 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}`;
170 logger
.error(errMsg
);
173 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`;
174 const powerMeasurandValues
= {} as MeasurandValues
;
175 const maxPower
= Math.round(self.chargingStation
.stationInfo
.maxPower
/ self.chargingStation
.stationInfo
.powerDivider
);
176 const maxPowerPerPhase
= Math.round((self.chargingStation
.stationInfo
.maxPower
/ self.chargingStation
.stationInfo
.powerDivider
) / self.chargingStation
.getNumberOfPhases());
177 switch (self.chargingStation
.getCurrentOutType()) {
178 case CurrentOutType
.AC
:
179 if (Utils
.isUndefined(meterValuesTemplate
[index
].value
)) {
180 powerMeasurandValues
.L1
= Utils
.getRandomFloatRounded(maxPowerPerPhase
);
181 powerMeasurandValues
.L2
= 0;
182 powerMeasurandValues
.L3
= 0;
183 if (self.chargingStation
.getNumberOfPhases() === 3) {
184 powerMeasurandValues
.L2
= Utils
.getRandomFloatRounded(maxPowerPerPhase
);
185 powerMeasurandValues
.L3
= Utils
.getRandomFloatRounded(maxPowerPerPhase
);
187 powerMeasurandValues
.allPhases
= Utils
.roundTo(powerMeasurandValues
.L1
+ powerMeasurandValues
.L2
+ powerMeasurandValues
.L3
, 2);
190 case CurrentOutType
.DC
:
191 if (Utils
.isUndefined(meterValuesTemplate
[index
].value
)) {
192 powerMeasurandValues
.allPhases
= Utils
.getRandomFloatRounded(maxPower
);
196 logger
.error(errMsg
);
199 meterValue
.sampledValue
.push({
200 ...!Utils
.isUndefined(meterValuesTemplate
[index
].unit
) ? { unit
: meterValuesTemplate
[index
].unit
} : { unit
: MeterValueUnit
.WATT
},
201 ...!Utils
.isUndefined(meterValuesTemplate
[index
].context
) && { context
: meterValuesTemplate
[index
].context
},
202 measurand
: meterValuesTemplate
[index
].measurand
,
203 ...!Utils
.isUndefined(meterValuesTemplate
[index
].location
) && { location
: meterValuesTemplate
[index
].location
},
204 ...!Utils
.isUndefined(meterValuesTemplate
[index
].value
) ? { value
: meterValuesTemplate
[index
].value
} : { value
: powerMeasurandValues
.allPhases
.toString() },
206 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
207 if (Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxPower
|| debug
) {
208 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}`);
210 for (let phase
= 1; self.chargingStation
.getNumberOfPhases() === 3 && phase
<= self.chargingStation
.getNumberOfPhases(); phase
++) {
211 const phaseValue
= `L${phase}-N`;
212 meterValue
.sampledValue
.push({
213 ...!Utils
.isUndefined(meterValuesTemplate
[index
].unit
) ? { unit
: meterValuesTemplate
[index
].unit
} : { unit
: MeterValueUnit
.WATT
},
214 ...!Utils
.isUndefined(meterValuesTemplate
[index
].context
) && { context
: meterValuesTemplate
[index
].context
},
215 ...!Utils
.isUndefined(meterValuesTemplate
[index
].measurand
) && { measurand
: meterValuesTemplate
[index
].measurand
},
216 ...!Utils
.isUndefined(meterValuesTemplate
[index
].location
) && { location
: meterValuesTemplate
[index
].location
},
217 ...!Utils
.isUndefined(meterValuesTemplate
[index
].value
) ? { value
: meterValuesTemplate
[index
].value
} : { value
: powerMeasurandValues
[`L${phase}`] as string },
218 phase
: phaseValue
as MeterValuePhase
,
221 // Current.Import measurand
222 } else if (meterValuesTemplate
[index
].measurand
&& meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.CURRENT_IMPORT
&& self.chargingStation
.getConfigurationKey(OCPP16StandardParametersKey
.MeterValuesSampledData
).value
.includes(OCPP16MeterValueMeasurand
.CURRENT_IMPORT
)) {
223 // FIXME: factor out powerDivider checks
224 if (Utils
.isUndefined(self.chargingStation
.stationInfo
.powerDivider
)) {
225 const errMsg
= `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`;
226 logger
.error(errMsg
);
228 } else if (self.chargingStation
.stationInfo
.powerDivider
&& self.chargingStation
.stationInfo
.powerDivider
<= 0) {
229 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}`;
230 logger
.error(errMsg
);
233 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`;
234 const currentMeasurandValues
: MeasurandValues
= {} as MeasurandValues
;
235 let maxAmperage
: number;
236 switch (self.chargingStation
.getCurrentOutType()) {
237 case CurrentOutType
.AC
:
238 maxAmperage
= ACElectricUtils
.amperagePerPhaseFromPower(self.chargingStation
.getNumberOfPhases(), self.chargingStation
.stationInfo
.maxPower
/ self.chargingStation
.stationInfo
.powerDivider
, self.chargingStation
.getVoltageOut());
239 if (Utils
.isUndefined(meterValuesTemplate
[index
].value
)) {
240 currentMeasurandValues
.L1
= Utils
.getRandomFloatRounded(maxAmperage
);
241 currentMeasurandValues
.L2
= 0;
242 currentMeasurandValues
.L3
= 0;
243 if (self.chargingStation
.getNumberOfPhases() === 3) {
244 currentMeasurandValues
.L2
= Utils
.getRandomFloatRounded(maxAmperage
);
245 currentMeasurandValues
.L3
= Utils
.getRandomFloatRounded(maxAmperage
);
247 currentMeasurandValues
.allPhases
= Utils
.roundTo((currentMeasurandValues
.L1
+ currentMeasurandValues
.L2
+ currentMeasurandValues
.L3
) / self.chargingStation
.getNumberOfPhases(), 2);
250 case CurrentOutType
.DC
:
251 maxAmperage
= ACElectricUtils
.amperageTotalFromPower(self.chargingStation
.stationInfo
.maxPower
/ self.chargingStation
.stationInfo
.powerDivider
, self.chargingStation
.getVoltageOut());
252 if (Utils
.isUndefined(meterValuesTemplate
[index
].value
)) {
253 currentMeasurandValues
.allPhases
= Utils
.getRandomFloatRounded(maxAmperage
);
257 logger
.error(errMsg
);
260 meterValue
.sampledValue
.push({
261 ...!Utils
.isUndefined(meterValuesTemplate
[index
].unit
) ? { unit
: meterValuesTemplate
[index
].unit
} : { unit
: MeterValueUnit
.AMP
},
262 ...!Utils
.isUndefined(meterValuesTemplate
[index
].context
) && { context
: meterValuesTemplate
[index
].context
},
263 measurand
: meterValuesTemplate
[index
].measurand
,
264 ...!Utils
.isUndefined(meterValuesTemplate
[index
].location
) && { location
: meterValuesTemplate
[index
].location
},
265 ...!Utils
.isUndefined(meterValuesTemplate
[index
].value
) ? { value
: meterValuesTemplate
[index
].value
} : { value
: currentMeasurandValues
.allPhases
.toString() },
267 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
268 if (Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxAmperage
|| debug
) {
269 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}`);
271 for (let phase
= 1; self.chargingStation
.getNumberOfPhases() === 3 && phase
<= self.chargingStation
.getNumberOfPhases(); phase
++) {
272 const phaseValue
= `L${phase}`;
273 meterValue
.sampledValue
.push({
274 ...!Utils
.isUndefined(meterValuesTemplate
[index
].unit
) ? { unit
: meterValuesTemplate
[index
].unit
} : { unit
: MeterValueUnit
.AMP
},
275 ...!Utils
.isUndefined(meterValuesTemplate
[index
].context
) && { context
: meterValuesTemplate
[index
].context
},
276 ...!Utils
.isUndefined(meterValuesTemplate
[index
].measurand
) && { measurand
: meterValuesTemplate
[index
].measurand
},
277 ...!Utils
.isUndefined(meterValuesTemplate
[index
].location
) && { location
: meterValuesTemplate
[index
].location
},
278 ...!Utils
.isUndefined(meterValuesTemplate
[index
].value
) ? { value
: meterValuesTemplate
[index
].value
} : { value
: currentMeasurandValues
[phaseValue
] as string },
279 phase
: phaseValue
as MeterValuePhase
,
282 // Energy.Active.Import.Register measurand (default)
283 } else if (!meterValuesTemplate
[index
].measurand
|| meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
) {
284 // FIXME: factor out powerDivider checks
285 if (Utils
.isUndefined(self.chargingStation
.stationInfo
.powerDivider
)) {
286 const errMsg
= `${self.chargingStation.logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`;
287 logger
.error(errMsg
);
289 } else if (self.chargingStation
.stationInfo
.powerDivider
&& self.chargingStation
.stationInfo
.powerDivider
<= 0) {
290 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}`;
291 logger
.error(errMsg
);
294 if (Utils
.isUndefined(meterValuesTemplate
[index
].value
)) {
295 const measurandValue
= Utils
.getRandomInt(self.chargingStation
.stationInfo
.maxPower
/ (self.chargingStation
.stationInfo
.powerDivider
* 3600000) * interval
);
296 // Persist previous value in connector
297 if (connector
&& !Utils
.isNullOrUndefined(connector
.lastEnergyActiveImportRegisterValue
) && connector
.lastEnergyActiveImportRegisterValue
>= 0) {
298 connector
.lastEnergyActiveImportRegisterValue
+= measurandValue
;
300 connector
.lastEnergyActiveImportRegisterValue
= 0;
303 meterValue
.sampledValue
.push({
304 ...!Utils
.isUndefined(meterValuesTemplate
[index
].unit
) ? { unit
: meterValuesTemplate
[index
].unit
} : { unit
: MeterValueUnit
.WATT_HOUR
},
305 ...!Utils
.isUndefined(meterValuesTemplate
[index
].context
) && { context
: meterValuesTemplate
[index
].context
},
306 ...!Utils
.isUndefined(meterValuesTemplate
[index
].measurand
) && { measurand
: meterValuesTemplate
[index
].measurand
},
307 ...!Utils
.isUndefined(meterValuesTemplate
[index
].location
) && { location
: meterValuesTemplate
[index
].location
},
308 ...!Utils
.isUndefined(meterValuesTemplate
[index
].value
) ? { value
: meterValuesTemplate
[index
].value
} :
309 { value
: connector
.lastEnergyActiveImportRegisterValue
.toString() },
311 const sampledValuesIndex
= meterValue
.sampledValue
.length
- 1;
312 const maxConsumption
= Math.round(self.chargingStation
.stationInfo
.maxPower
* 3600 / (self.chargingStation
.stationInfo
.powerDivider
* interval
));
313 if (Utils
.convertToFloat(meterValue
.sampledValue
[sampledValuesIndex
].value
) > maxConsumption
|| debug
) {
314 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}`);
316 // Unsupported measurand
318 logger
.info(`${self.chargingStation.logPrefix()} Unsupported MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} on connectorId ${connectorId}`);
321 const payload
: MeterValuesRequest
= {
326 await self.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.METER_VALUES
);
328 self.handleRequestError(OCPP16RequestCommand
.METER_VALUES
, error
);
332 public async sendError(messageId
: string, error
: OCPPError
, commandName
: OCPP16RequestCommand
| OCPP16IncomingRequestCommand
): Promise
<unknown
> {
334 return this.sendMessage(messageId
, error
, MessageType
.CALL_ERROR_MESSAGE
, commandName
);
337 private async sendTransactionBeginMeterValues(connectorId
: number, transactionId
: number, meterBegin
: number) {
338 const meterValue
: MeterValue
= {
339 timestamp
: new Date().toISOString(),
342 const meterValuesTemplate
: OCPP16SampledValue
[] = this.chargingStation
.getConnector(connectorId
).MeterValues
;
343 for (let index
= 0; index
< meterValuesTemplate
.length
; index
++) {
344 // Energy.Active.Import.Register measurand (default)
345 if (!meterValuesTemplate
[index
].measurand
|| meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
) {
346 meterValue
.sampledValue
.push({
347 ...!Utils
.isUndefined(meterValuesTemplate
[index
].unit
) ? { unit
: meterValuesTemplate
[index
].unit
} : { unit
: MeterValueUnit
.WATT_HOUR
},
348 context
: MeterValueContext
.TRANSACTION_BEGIN
,
349 ...!Utils
.isUndefined(meterValuesTemplate
[index
].measurand
) && { measurand
: meterValuesTemplate
[index
].measurand
},
350 ...!Utils
.isUndefined(meterValuesTemplate
[index
].location
) && { location
: meterValuesTemplate
[index
].location
},
351 ...!Utils
.isUndefined(meterValuesTemplate
[index
].value
) ? { value
: meterValuesTemplate
[index
].value
} :
352 { value
: meterBegin
.toString() },
356 const payload
: MeterValuesRequest
= {
361 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.METER_VALUES
);
364 private async sendTransactionEndMeterValues(connectorId
: number, transactionId
: number, meterEnd
: number) {
365 const meterValue
: MeterValue
= {
366 timestamp
: new Date().toISOString(),
369 const meterValuesTemplate
: OCPP16SampledValue
[] = this.chargingStation
.getConnector(connectorId
).MeterValues
;
370 for (let index
= 0; index
< meterValuesTemplate
.length
; index
++) {
371 // Energy.Active.Import.Register measurand (default)
372 if (!meterValuesTemplate
[index
].measurand
|| meterValuesTemplate
[index
].measurand
=== OCPP16MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
) {
373 meterValue
.sampledValue
.push({
374 ...!Utils
.isUndefined(meterValuesTemplate
[index
].unit
) ? { unit
: meterValuesTemplate
[index
].unit
} : { unit
: MeterValueUnit
.WATT_HOUR
},
375 context
: MeterValueContext
.TRANSACTION_END
,
376 ...!Utils
.isUndefined(meterValuesTemplate
[index
].measurand
) && { measurand
: meterValuesTemplate
[index
].measurand
},
377 ...!Utils
.isUndefined(meterValuesTemplate
[index
].location
) && { location
: meterValuesTemplate
[index
].location
},
378 ...!Utils
.isUndefined(meterValuesTemplate
[index
].value
) ? { value
: meterValuesTemplate
[index
].value
} :
379 { value
: meterEnd
.toString() },
383 const payload
: MeterValuesRequest
= {
388 await this.sendMessage(Utils
.generateUUID(), payload
, MessageType
.CALL_MESSAGE
, OCPP16RequestCommand
.METER_VALUES
);