beginEndMeterValues | true/false | false | boolean | enable Transaction.{Begin,End} MeterValues
outOfOrderEndMeterValues | true/false | false | boolean | send Transaction.End MeterValues out of order
meteringPerTransaction | true/false | true | boolean | disable metering on a per transaction basis
+transactionDataMeterValues | true/false | false | boolean | enable transaction data MeterValues at stop transaction
Configuration | | | ChargingStationConfiguration | charging stations OCPP configuration parameters
AutomaticTransactionGenerator | | | AutomaticTransactionGenerator | charging stations ATG configuration
Connectors | | | Connectors | charging stations connectors configuration
return this.stationInfo.meteringPerTransaction ?? true;
}
+ public getTransactionDataMeterValues(): boolean {
+ return this.stationInfo.transactionDataMeterValues ?? false;
+ }
+
public getEnergyActiveImportRegisterByTransactionId(transactionId: number): number | undefined {
if (this.getMeteringPerTransaction()) {
for (const connector in this.connectors) {
delete this.getConnector(connectorId).transactionId;
delete this.getConnector(connectorId).idTag;
this.getConnector(connectorId).transactionEnergyActiveImportRegisterValue = 0;
+ delete this.getConnector(connectorId).transactionBeginMeterValue;
this.stopMeterValues(connectorId);
}
} else {
stationTemplateFromFile.power = stationTemplateFromFile.power as number;
stationInfo.maxPower = stationTemplateFromFile.powerUnit === PowerUnits.KILO_WATT
- ? (stationTemplateFromFile.power) * 1000
+ ? stationTemplateFromFile.power * 1000
: stationTemplateFromFile.power;
}
+ delete stationInfo.power;
+ delete stationInfo.powerUnit;
stationInfo.chargingStationId = this.getChargingStationId(stationTemplateFromFile);
stationInfo.resetTime = stationTemplateFromFile.resetTime ? stationTemplateFromFile.resetTime * 1000 : Constants.CHARGING_STATION_DEFAULT_RESET_TIME;
return stationInfo;
const authorizationFile = this.getAuthorizationFile();
if (authorizationFile) {
try {
- fs.watch(authorizationFile).on('change', (e) => {
+ fs.watch(authorizationFile).on('change', () => {
try {
logger.debug(this.logPrefix() + ' Authorization file ' + authorizationFile + ' have changed, reload');
// Initialize authorizedTags
private startStationTemplateFileMonitoring(): void {
try {
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
- fs.watch(this.stationTemplateFile).on('change', async (e): Promise<void> => {
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
+ fs.watch(this.stationTemplateFile).on('change', async (): Promise<void> => {
try {
logger.debug(this.logPrefix() + ' Template file ' + this.stationTemplateFile + ' have changed, reload');
// Initialize
import { ACElectricUtils, DCElectricUtils } from '../../../utils/ElectricUtils';
import { AuthorizeRequest, OCPP16AuthorizeResponse, OCPP16StartTransactionResponse, OCPP16StopTransactionReason, OCPP16StopTransactionResponse, StartTransactionRequest, StopTransactionRequest } from '../../../types/ocpp/1.6/Transaction';
import { HeartbeatRequest, OCPP16BootNotificationRequest, OCPP16IncomingRequestCommand, OCPP16RequestCommand, StatusNotificationRequest } from '../../../types/ocpp/1.6/Requests';
-import { MeterValue, MeterValueContext, MeterValuePhase, MeterValueUnit, MeterValuesRequest, OCPP16MeterValueMeasurand, OCPP16SampledValue } from '../../../types/ocpp/1.6/MeterValues';
+import { MeterValuePhase, MeterValueUnit, MeterValuesRequest, OCPP16MeterValue, OCPP16MeterValueMeasurand, OCPP16SampledValue } from '../../../types/ocpp/1.6/MeterValues';
import Constants from '../../../utils/Constants';
import { CurrentOutType } from '../../../types/ChargingStationTemplate';
}
}
- public async sendBootNotification(chargePointModel: string, chargePointVendor: string, chargeBoxSerialNumber?: string, firmwareVersion?: string, chargePointSerialNumber?: string, iccid?: string, imsi?: string, meterSerialNumber?: string, meterType?: string): Promise<OCPP16BootNotificationResponse> {
+ public async sendBootNotification(chargePointModel: string, chargePointVendor: string, chargeBoxSerialNumber?: string, firmwareVersion?: string,
+ chargePointSerialNumber?: string, iccid?: string, imsi?: string, meterSerialNumber?: string, meterType?: string): Promise<OCPP16BootNotificationResponse> {
try {
const payload: OCPP16BootNotificationRequest = {
chargePointModel,
public async sendStopTransaction(transactionId: number, meterStop: number, idTag?: string,
reason: OCPP16StopTransactionReason = OCPP16StopTransactionReason.NONE): Promise<OCPP16StopTransactionResponse> {
try {
- const payload: StopTransactionRequest = {
- transactionId,
- ...!Utils.isUndefined(idTag) && { idTag },
- meterStop: meterStop,
- timestamp: new Date().toISOString(),
- ...reason && { reason },
- };
let connectorId: number;
for (const connector in this.chargingStation.connectors) {
if (Utils.convertToInt(connector) > 0 && this.chargingStation.getConnector(Utils.convertToInt(connector))?.transactionId === transactionId) {
connectorId = Utils.convertToInt(connector);
}
}
+ const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(this.chargingStation, connectorId, meterStop);
+ // FIXME: should be a callback, each OCPP commands implementation must do only one job
(this.chargingStation.getBeginEndMeterValues() && !this.chargingStation.getOutOfOrderEndMeterValues())
- && await this.sendTransactionEndMeterValues(connectorId, transactionId, meterStop);
+ && await this.sendTransactionEndMeterValues(connectorId, transactionId, transactionEndMeterValue);
+ const payload: StopTransactionRequest = {
+ transactionId,
+ ...!Utils.isUndefined(idTag) && { idTag },
+ meterStop,
+ timestamp: new Date().toISOString(),
+ ...reason && { reason },
+ ...this.chargingStation.getTransactionDataMeterValues() && { transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues(this.chargingStation.getConnector(connectorId).transactionBeginMeterValue, transactionEndMeterValue) },
+ };
return await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.STOP_TRANSACTION) as OCPP16StartTransactionResponse;
} catch (error) {
this.handleRequestError(OCPP16RequestCommand.STOP_TRANSACTION, error);
// eslint-disable-next-line consistent-this
public async sendMeterValues(connectorId: number, transactionId: number, interval: number, self: OCPPRequestService, debug = false): Promise<void> {
try {
- const meterValue: MeterValue = {
+ const meterValue: OCPP16MeterValue = {
timestamp: new Date().toISOString(),
sampledValue: [],
};
}
}
- public async sendTransactionBeginMeterValues(connectorId: number, transactionId: number, meterBegin: number): Promise<void> {
- const meterValue: MeterValue = {
- timestamp: new Date().toISOString(),
- sampledValue: [],
- };
- const meterValuesTemplate: OCPP16SampledValue[] = this.chargingStation.getConnector(connectorId).MeterValues;
- for (let index = 0; index < meterValuesTemplate.length; index++) {
- // Energy.Active.Import.Register measurand (default)
- if (!meterValuesTemplate[index].measurand || meterValuesTemplate[index].measurand === OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) {
- const unitDivider = meterValuesTemplate[index]?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
- meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(meterValuesTemplate[index],
- Utils.roundTo(meterBegin / unitDivider, 4), MeterValueContext.TRANSACTION_BEGIN));
- }
- }
+ public async sendTransactionBeginMeterValues(connectorId: number, transactionId: number, beginMeterValue: OCPP16MeterValue): Promise<void> {
const payload: MeterValuesRequest = {
connectorId,
transactionId,
- meterValue,
+ meterValue: beginMeterValue,
};
await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES);
}
- public async sendTransactionEndMeterValues(connectorId: number, transactionId: number, meterEnd: number): Promise<void> {
- const meterValue: MeterValue = {
- timestamp: new Date().toISOString(),
- sampledValue: [],
- };
- const meterValuesTemplate: OCPP16SampledValue[] = this.chargingStation.getConnector(connectorId).MeterValues;
- for (let index = 0; index < meterValuesTemplate.length; index++) {
- // Energy.Active.Import.Register measurand (default)
- if (!meterValuesTemplate[index].measurand || meterValuesTemplate[index].measurand === OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) {
- const unitDivider = meterValuesTemplate[index]?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
- meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(meterValuesTemplate[index], Utils.roundTo(meterEnd / unitDivider, 4), MeterValueContext.TRANSACTION_END));
- }
- }
+ public async sendTransactionEndMeterValues(connectorId: number, transactionId: number, endMeterValue: OCPP16MeterValue): Promise<void> {
const payload: MeterValuesRequest = {
connectorId,
transactionId,
- meterValue,
+ meterValue: endMeterValue,
};
await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES);
}
import { MeterValuesRequest, MeterValuesResponse } from '../../../types/ocpp/1.6/MeterValues';
import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
+import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
import { OCPP16StandardParametersKey } from '../../../types/ocpp/1.6/Configuration';
import OCPPResponseService from '../OCPPResponseService';
import Utils from '../../../utils/Utils';
this.chargingStation.getConnector(connectorId).transactionId = payload.transactionId;
this.chargingStation.getConnector(connectorId).idTag = requestPayload.idTag;
this.chargingStation.getConnector(connectorId).transactionEnergyActiveImportRegisterValue = 0;
+ this.chargingStation.getConnector(connectorId).transactionBeginMeterValue = OCPP16ServiceUtils.buildTransactionBeginMeterValue(this.chargingStation, connectorId,
+ requestPayload.meterStart);
this.chargingStation.getBeginEndMeterValues() && await this.chargingStation.ocppRequestService.sendTransactionBeginMeterValues(connectorId, payload.transactionId,
- this.chargingStation.getEnergyActiveImportRegisterByTransactionId(payload.transactionId));
+ this.chargingStation.getConnector(connectorId).transactionBeginMeterValue);
await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.CHARGING);
this.chargingStation.getConnector(connectorId).status = OCPP16ChargePointStatus.CHARGING;
logger.info(this.chargingStation.logPrefix() + ' Transaction ' + payload.transactionId.toString() + ' STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + connectorId.toString() + ' for idTag ' + requestPayload.idTag);
}
if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
(this.chargingStation.getBeginEndMeterValues() && this.chargingStation.getOutOfOrderEndMeterValues())
- && await this.chargingStation.ocppRequestService.sendTransactionEndMeterValues(transactionConnectorId, requestPayload.transactionId, requestPayload.meterStop);
+ && await this.chargingStation.ocppRequestService.sendTransactionEndMeterValues(transactionConnectorId, requestPayload.transactionId,
+ OCPP16ServiceUtils.buildTransactionEndMeterValue(this.chargingStation, transactionConnectorId, requestPayload.meterStop));
if (!this.chargingStation.isChargingStationAvailable() || !this.chargingStation.isConnectorAvailable(transactionConnectorId)) {
await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorId, OCPP16ChargePointStatus.UNAVAILABLE);
this.chargingStation.getConnector(transactionConnectorId).status = OCPP16ChargePointStatus.UNAVAILABLE;
-import { MeterValueContext, MeterValueLocation, MeterValuePhase, MeterValueUnit, OCPP16MeterValueMeasurand, OCPP16SampledValue } from '../../../types/ocpp/1.6/MeterValues';
+import { MeterValueContext, MeterValueLocation, MeterValuePhase, MeterValueUnit, OCPP16MeterValue, OCPP16MeterValueMeasurand, OCPP16SampledValue } from '../../../types/ocpp/1.6/MeterValues';
import ChargingStation from '../../ChargingStation';
import Utils from '../../../utils/Utils';
const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`;
logger.error(errMsg);
throw Error(errMsg);
- } else if (chargingStation.stationInfo.powerDivider && chargingStation.stationInfo.powerDivider <= 0) {
+ } else if (chargingStation.stationInfo?.powerDivider <= 0) {
const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider have zero or below value ${chargingStation.stationInfo.powerDivider}`;
logger.error(errMsg);
throw Error(errMsg);
return MeterValueLocation.EV;
}
}
+
+ public static buildTransactionBeginMeterValue(chargingStation: ChargingStation, connectorId: number, meterBegin: number): OCPP16MeterValue {
+ const meterValue: OCPP16MeterValue = {
+ timestamp: new Date().toISOString(),
+ sampledValue: [],
+ };
+ const meterValuesTemplate: OCPP16SampledValue[] = chargingStation.getConnector(connectorId).MeterValues;
+ for (let index = 0; index < meterValuesTemplate.length; index++) {
+ // Energy.Active.Import.Register measurand (default)
+ if (!meterValuesTemplate[index].measurand || meterValuesTemplate[index].measurand === OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) {
+ const unitDivider = meterValuesTemplate[index]?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
+ meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(meterValuesTemplate[index],
+ Utils.roundTo(meterBegin / unitDivider, 4), MeterValueContext.TRANSACTION_BEGIN));
+ }
+ }
+ return meterValue;
+ }
+
+ public static buildTransactionEndMeterValue(chargingStation: ChargingStation, connectorId: number, meterEnd: number): OCPP16MeterValue {
+ const meterValue: OCPP16MeterValue = {
+ timestamp: new Date().toISOString(),
+ sampledValue: [],
+ };
+ const meterValuesTemplate: OCPP16SampledValue[] = chargingStation.getConnector(connectorId).MeterValues;
+ for (let index = 0; index < meterValuesTemplate.length; index++) {
+ // Energy.Active.Import.Register measurand (default)
+ if (!meterValuesTemplate[index].measurand || meterValuesTemplate[index].measurand === OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) {
+ const unitDivider = meterValuesTemplate[index]?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
+ meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(meterValuesTemplate[index], Utils.roundTo(meterEnd / unitDivider, 4), MeterValueContext.TRANSACTION_END));
+ }
+ }
+ return meterValue;
+ }
+
+ public static buildTransactionDataMeterValues(transactionBeginMeterValue: OCPP16MeterValue, transactionEndMeterValue: OCPP16MeterValue): OCPP16MeterValue[] {
+ const meterValues: OCPP16MeterValue[] = [];
+ meterValues.push(transactionBeginMeterValue);
+ meterValues.push(transactionEndMeterValue);
+ return meterValues;
+ }
}
import Constants from '../../utils/Constants';
import { ErrorType } from '../../types/ocpp/ErrorType';
import { MessageType } from '../../types/ocpp/MessageType';
+import { MeterValue } from '../../types/ocpp/MeterValues';
import OCPPError from '../OcppError';
import OCPPResponseService from './OCPPResponseService';
import logger from '../../utils/Logger';
public abstract sendStartTransaction(connectorId: number, idTag?: string): Promise<StartTransactionResponse>;
public abstract sendStopTransaction(transactionId: number, meterStop: number, idTag?: string, reason?: StopTransactionReason): Promise<StopTransactionResponse>;
public abstract sendMeterValues(connectorId: number, transactionId: number, interval: number, self: OCPPRequestService): Promise<void>;
- public abstract sendTransactionBeginMeterValues(connectorId: number, transactionId: number, meterBegin: number): Promise<void>;
- public abstract sendTransactionEndMeterValues(connectorId: number, transactionId: number, meterEnd: number): Promise<void>;
+ public abstract sendTransactionBeginMeterValues(connectorId: number, transactionId: number, beginMeterValue: MeterValue): Promise<void>;
+ public abstract sendTransactionEndMeterValues(connectorId: number, transactionId: number, endMeterValue: MeterValue): Promise<void>;
public abstract sendError(messageId: string, error: OCPPError, commandName: RequestCommand | IncomingRequestCommand): Promise<unknown>;
}
export enum VoltageOut {
VOLTAGE_110 = 110,
VOLTAGE_230 = 230,
- VOLTAGE_400 = 400
+ VOLTAGE_400 = 400,
+ VOLTAGE_800 = 800
}
export interface AutomaticTransactionGenerator {
beginEndMeterValues?: boolean;
outOfOrderEndMeterValues?: boolean;
meteringPerTransaction?: boolean;
+ transactionDataMeterValues?: boolean;
Configuration?: ChargingStationConfiguration;
AutomaticTransactionGenerator: AutomaticTransactionGenerator;
Connectors: Connectors;
+import { MeterValue, SampledValue } from './ocpp/MeterValues';
+
import { AvailabilityType } from './ocpp/Requests';
import { ChargePointStatus } from './ocpp/ChargePointStatus';
import { ChargingProfile } from './ocpp/ChargingProfile';
-import { SampledValue } from './ocpp/MeterValues';
export interface Connector {
availability: AvailabilityType;
idTag?: string;
energyActiveImportRegisterValue?: number; // In Wh
transactionEnergyActiveImportRegisterValue?: number; // In Wh
- chargingProfiles?: ChargingProfile[]
+ transactionBeginMeterValue?: MeterValue;
+ chargingProfiles?: ChargingProfile[];
}
export default interface Connectors {
format?: MeterValueFormat;
}
-export interface MeterValue {
+export interface OCPP16MeterValue {
timestamp: string;
sampledValue: OCPP16SampledValue[];
}
export interface MeterValuesRequest {
connectorId: number;
transactionId?: number;
- meterValue: MeterValue | MeterValue[];
+ meterValue: OCPP16MeterValue | OCPP16MeterValue[];
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
-import { MeterValue } from './MeterValues';
+import { OCPP16MeterValue } from './MeterValues';
export enum OCPP16StopTransactionReason {
NONE = '',
timestamp: string;
transactionId: number;
reason?: OCPP16StopTransactionReason;
- transactionData?: MeterValue[];
+ transactionData?: OCPP16MeterValue[];
}
export interface OCPP16StopTransactionResponse {
-import { OCPP16MeterValueMeasurand, OCPP16SampledValue } from './1.6/MeterValues';
+import { OCPP16MeterValue, OCPP16MeterValueMeasurand, OCPP16SampledValue } from './1.6/MeterValues';
export type MeterValueMeasurand = OCPP16MeterValueMeasurand;
};
export type SampledValue = OCPP16SampledValue;
+
+export type MeterValue = OCPP16MeterValue;
private static getConfigurationFileWatcher(): fs.FSWatcher {
try {
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
- return fs.watch(Configuration.configurationFilePath).on('change', async (e): Promise<void> => {
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
+ return fs.watch(Configuration.configurationFilePath).on('change', async (): Promise<void> => {
// Nullify to force configuration file reading
Configuration.configuration = null;
if (!Configuration.isUndefined(Configuration.configurationChangeCallback)) {