Save Asset consumption to DB
MODIFY
src/integration/asset/AssetIntegration.ts
src/integration/asset/AssetIntegration.ts
@@ -6,13 +6,15 @@ export default abstract class AssetIntegration<T extends AssetSetting> {
protected readonly tenantID: string;
protected settings: T;
protected connection: AssetConnectionSetting;
+ protected token: string;
protected constructor(tenantID: string, settings: T, connection: AssetConnectionSetting) {
this.tenantID = tenantID;
this.settings = settings;
this.connection = connection;
+ this.token = null;
}
async abstract checkConnection();
- async abstract retrieveMeterValuesByID(asset: Asset, meterID: string): Promise<AbstractConsumption>;
+ async abstract retrieveMeterValues(asset: Asset): Promise<AbstractConsumption>;
}

MODIFY
src/integration/asset/schneider/SchneiderAssetIntegration.ts
src/integration/asset/schneider/SchneiderAssetIntegration.ts
@@ -19,11 +19,8 @@ export default class SchneiderAssetIntegration extends AssetIntegration<AssetSet
public async checkConnection() {
// Check if connection is initialized
this.isAssetConnectionInitialized();
- // Set credential params
- const params = new URLSearchParams();
- params.append('grant_type', 'password');
- params.append('username', this.connection.connection.user);
- params.append('password', Cypher.decrypt(this.connection.connection.password));
+ // Get credential params
+ const params = this.getCredentialParams();
// Send credentials to get the token
await Utils.executePromiseWithTimeout(5000,
axios.post(`${this.connection.url}/GetToken`, params, {
@@ -33,8 +30,69 @@ export default class SchneiderAssetIntegration extends AssetIntegration<AssetSet
);
}
- public async retrieveMeterValuesByID(asset: Asset, meterID: string): Promise<AbstractConsumption> {
- return;
+ public async retrieveMeterValues(asset: Asset): Promise<AbstractConsumption> {
+ // Set new Token
+ await this.setToken();
+ // Get consumption
+ const { data } = await axios.get(
+ `${this.connection.url}/Containers/${Constants.DEFAULT_ASSET_SCHNEIDER_BASE_ID}${asset.meterID}/Children`,
+ {
+ headers: this.buildAuthHeader()
+ }
+ );
+ return this.filterEnergyConsumptionRequest(asset, data);
+ }
+
+ private filterEnergyConsumptionRequest(asset: Asset, data: any): AbstractConsumption {
+ let consumption = {} as AbstractConsumption;
+ consumption.consumptionWh = this.getLastConsumptionWh(asset, +data[0].Value);
+ consumption.instantAmpsL1 = +data[6].Value;
+ consumption.instantAmpsL2 = +data[7].Value;
+ consumption.instantAmpsL3 = +data[8].Value;
+ consumption.instantAmps = +data[6].Value + +data[7].Value + +data[8].Value;
+ consumption.instantVoltsL1 = +data[11].Value;
+ consumption.instantVoltsL2 = +data[13].Value;
+ consumption.instantVoltsL3 = +data[15].Value;
+ consumption.instantVolts = +data[11].Value + +data[13].Value + +data[15].Value;
+ consumption.instantWattsL1 = +data[17].Value * 1000;
+ consumption.instantWattsL2 = +data[18].Value * 1000;
+ consumption.instantWattsL3 = +data[19].Value * 1000;
+ consumption.instantWatts = (+data[17].Value + +data[18].Value + +data[19].Value) * 1000;
+ return consumption;
+ }
+
+ private async setToken() {
+ // Check if connection is initialized
+ this.isAssetConnectionInitialized();
+ // Get credential params
+ const params = this.getCredentialParams();
+ // Send credentials to get the token
+ const { data } = await Utils.executePromiseWithTimeout(5000,
+ axios.post(`${this.connection.url}/GetToken`, params, {
+ headers: this.buildFormHeaders()
+ }),
+ `Time out error (5s) when trying to test the connection URL '${this.connection.url}/GetToken'`
+ );
+ // Set Token
+ if (data.access_token) {
+ this.token = data.access_token;
+ }
+ }
+
+ private getCredentialParams(): URLSearchParams {
+ const params = new URLSearchParams();
+ params.append('grant_type', 'password');
+ params.append('username', this.connection.connection.user);
+ params.append('password', Cypher.decrypt(this.connection.connection.password));
+ return params;
+ }
+
+ private getLastConsumptionWh(asset: Asset, consumptionkWh: number): number {
+ const consumptionWh = consumptionkWh * 1000;
+ if (asset.consumption.consumptionWh > consumptionWh) {
+ return consumptionWh - asset.consumption.consumptionWh;
+ }
+ return asset.consumption.consumptionWh;
}
private isAssetConnectionInitialized(): void {
@@ -54,4 +112,10 @@ export default class SchneiderAssetIntegration extends AssetIntegration<AssetSet
'Content-Type': 'application/x-www-form-urlencoded'
};
}
+
+ private buildAuthHeader(): any {
+ return {
+ 'Authorization': 'Bearer ' + this.token
+ };
+ }
}

MODIFY
src/server/rest/service/AssetService.ts
src/server/rest/service/AssetService.ts
@@ -110,34 +110,7 @@ export default class AssetService {
user: req.user
});
}
- // Get asset connection type
- const assetImpl = await AssetFactory.getAssetImpl(req.user.tenantID, asset.connectionID);
- // Asset has unknown connection type
- if (!assetImpl) {
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.GENERAL_ERROR,
- message: 'Asset service is not configured',
- module: MODULE_NAME, method: 'handleRefreshMetrics',
- action: action,
- user: req.user
- });
- }
- try {
- // Check connection
- await assetImpl.checkConnection();
- } catch (error) {
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.GENERAL_ERROR,
- message: 'Connection to the asset failed',
- module: MODULE_NAME, method: 'handleRefreshMetrics',
- action: action,
- user: req.user,
- detailedMessages: { error: error.message, stack: error.stack }
- });
- }
- Utils.retrieveAssetMetrics(req.user.tenantID, asset);
+ await Utils.retrieveAssetMetrics(action, req, asset);
res.json(Constants.REST_RESPONSE_SUCCESS);
next();
}

MODIFY
src/storage/mongodb/AssetStorage.ts
src/storage/mongodb/AssetStorage.ts
@@ -72,6 +72,7 @@ export default class AssetStorage {
assetMDB.issuer = Utils.convertToBoolean(assetToSave.issuer);
assetMDB.connectionID = assetToSave.connectionID;
assetMDB.meterID = assetToSave.meterID;
+ assetMDB.consumption = assetToSave.consumption;
// Add Last Changed/Created props
DatabaseUtils.addLastChangedCreatedProps(assetMDB, assetToSave);
// Modify

MODIFY
src/types/Asset.ts
src/types/Asset.ts
import CreatedUpdatedProps from './CreatedUpdatedProps';
import SiteArea from './SiteArea';
+import { AbstractConsumption } from './Consumption';
export default interface Asset extends CreatedUpdatedProps {
id: string;
@@ -13,4 +14,5 @@ export default interface Asset extends CreatedUpdatedProps {
dynamicAsset: boolean;
connectionID?: string;
meterID?: string;
+ consumption?: AbstractConsumption;
}

MODIFY
src/types/Transaction.ts
src/types/Transaction.ts
@@ -25,7 +25,26 @@ export enum TransactionAction {
STOP = 'stop'
}
-export default interface Transaction {
+export interface AbstractTransaction {
+ currentInstantWatts: number;
+ currentInstantWattsL1?: number;
+ currentInstantWattsL2?: number;
+ currentInstantWattsL3?: number;
+ currentInstantWattsDC?: number;
+ currentInstantVoltage?: number;
+ currentInstantVoltageL1?: number;
+ currentInstantVoltageL2?: number;
+ currentInstantVoltageL3?: number;
+ currentInstantVoltageDC?: number;
+ currentInstantAmps?: number;
+ currentInstantAmpsL1?: number;
+ currentInstantAmpsL2?: number;
+ currentInstantAmpsL3?: number;
+ currentInstantAmpsDC?: number;
+ currentConsumptionWh?: number;
+}
+
+export default interface Transaction extends AbstractTransaction {
id?: number;
carID?: string;
siteID?: string;
@@ -63,22 +82,6 @@ export default interface Transaction {
currentStateOfCharge: number;
currentTotalDurationSecs?: number;
transactionEndReceived?: boolean;
- currentInstantWatts: number;
- currentInstantWattsL1?: number;
- currentInstantWattsL2?: number;
- currentInstantWattsL3?: number;
- currentInstantWattsDC?: number;
- currentInstantVoltage?: number;
- currentInstantVoltageL1?: number;
- currentInstantVoltageL2?: number;
- currentInstantVoltageL3?: number;
- currentInstantVoltageDC?: number;
- currentInstantAmps?: number;
- currentInstantAmpsL1?: number;
- currentInstantAmpsL2?: number;
- currentInstantAmpsL3?: number;
- currentInstantAmpsDC?: number;
- currentConsumptionWh?: number;
currentCumulatedPrice?: number;
currentTotalConsumptionWh: number;
currentSignedData?: string;

MODIFY
src/utils/Constants.ts
src/utils/Constants.ts
@@ -73,6 +73,8 @@ export default class Constants {
public static readonly MAX_DATE = new Date('9999-12-31Z23:59:59:999');
public static readonly MIN_DATE = new Date('1970-01-01Z00:00:00:000');
+ public static readonly DEFAULT_ASSET_SCHNEIDER_BASE_ID = "00%252FServer%25201%252FSAPInterface%252FCOMPTEURS%252F";
+
public static readonly MONGO_USER_MASK = {
'_id': 0,
'__v': 0,

MODIFY
src/utils/Utils.ts
src/utils/Utils.ts
@@ -15,6 +15,7 @@ import validator from 'validator';
import Authorizations from '../authorization/Authorizations';
import AppError from '../exception/AppError';
import BackendError from '../exception/BackendError';
+import AssetFactory from '../integration/asset/AssetFactory';
import TenantStorage from '../storage/mongodb/TenantStorage';
import UserStorage from '../storage/mongodb/UserStorage';
import Asset from '../types/Asset';
@@ -41,6 +42,7 @@ import Configuration from './Configuration';
import Constants from './Constants';
import Cypher from './Cypher';
import Logging from './Logging';
+import AssetStorage from '../storage/mongodb/AssetStorage';
const _centralSystemFrontEndConfig = Configuration.getCentralSystemFrontEndConfig();
@@ -1879,11 +1881,53 @@ export default class Utils {
}
}
- public static async retrieveAssetMetrics(tenantID: string, asset: Asset) {
- // Recup impl with factory
- // CAll to BMS backend
- // Assign to Asset the result
- // Save asset
+ public static async retrieveAssetMetrics(action: ServerAction, req: Request, asset: Asset) {
+ // Get asset connection type
+ const assetImpl = await AssetFactory.getAssetImpl(req.user.tenantID, asset.connectionID);
+ // Asset has unknown connection type
+ if (!assetImpl) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Asset service is not configured',
+ module: MODULE_NAME, method: 'handleRefreshMetrics',
+ action: action,
+ user: req.user
+ });
+ }
+ try {
+ // Check connection
+ await assetImpl.checkConnection();
+ } catch (error) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Connection to the asset failed',
+ module: MODULE_NAME, method: 'handleRefreshMetrics',
+ action: action,
+ user: req.user,
+ detailedMessages: { error: error.message, stack: error.stack }
+ });
+ }
+ // Call to BMS backend
+ try {
+ const consumption = await assetImpl.retrieveMeterValues(asset);
+ // Store consumption to Asset
+ asset.consumption = consumption;
+ } catch (error) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Error occurred while retrieving metrics',
+ module: MODULE_NAME,
+ method: 'retrieveAssetMetrics',
+ action: action,
+ user: req.user,
+ detailedMessages: { error: error.message, stack: error.stack }
+ });
+ }
+ // Save Asset
+ await AssetStorage.saveAsset(req.user.tenantID, asset);
}
private static _isUserEmailValid(email: string): boolean {

Save Asset consumption to DB