TODO: Retrieve Asset Metrics
MODIFY
src/authorization/Authorizations.ts
src/authorization/Authorizations.ts
@@ -589,10 +589,14 @@ export default class Authorizations {
return Authorizations.canPerformAction(loggedUser, Entity.INVOICES, Action.SYNCHRONIZE_INVOICES);
}
- public static canCheckConnectionAsset(loggedUser: UserToken): boolean {
+ public static canCheckAssetConnection(loggedUser: UserToken): boolean {
return Authorizations.canPerformAction(loggedUser, Entity.ASSET, Action.CHECK_CONNECTION);
}
+ public static canRefreshAssetConnection(loggedUser: UserToken): boolean {
+ return Authorizations.canPerformAction(loggedUser, Entity.ASSET, Action.REFRESH_CONNECTION);
+ }
+
public static isSuperAdmin(user: UserToken | User): boolean {
return user.role === UserRole.SUPER_ADMIN;
}

MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -63,7 +63,7 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
},
{ resource: Entity.TAXES, action: [Action.LIST], attributes: ['*'] },
{ resource: Entity.INVOICES, action: [Action.LIST, Action.SYNCHRONIZE_INVOICES], attributes: ['*'] },
- { resource: Entity.ASSET, action: [Action.CREATE, Action.READ, Action.UPDATE, Action.DELETE, Action.CHECK_CONNECTION], attributes: ['*'] },
+ { resource: Entity.ASSET, action: [Action.CREATE, Action.READ, Action.UPDATE, Action.DELETE, Action.CHECK_CONNECTION, Action.REFRESH_CONNECTION], attributes: ['*'] },
{ resource: Entity.ASSETS, action: Action.LIST, attributes: ['*'] },
{ resource: Entity.SETTINGS, action: Action.LIST, attributes: ['*'] },
{ resource: Entity.SETTING, action: [Action.CREATE, Action.READ, Action.UPDATE, Action.DELETE], attributes: ['*'] },

MODIFY
src/integration/asset/AssetIntegration.ts
src/integration/asset/AssetIntegration.ts
import { AssetConnectionSetting, AssetSetting } from '../../types/Setting';
+import Asset from '../../types/Asset';
+import { AbstractConsumption } from '../../types/Consumption';
export default abstract class AssetIntegration<T extends AssetSetting> {
protected readonly tenantID: string;
@@ -12,4 +14,5 @@ export default abstract class AssetIntegration<T extends AssetSetting> {
}
async abstract checkConnection();
+ async abstract retrieveMeterValuesByID(asset: Asset, meterID: string): Promise<AbstractConsumption>;
}

MODIFY
src/integration/asset/schneider/SchneiderAssetIntegration.ts
src/integration/asset/schneider/SchneiderAssetIntegration.ts
-import { AssetConnectionSetting, AssetSetting } from '../../../types/Setting';
-
-import AssetIntegration from '../AssetIntegration';
+import axios from 'axios';
import BackendError from '../../../exception/BackendError';
+import Asset from '../../../types/Asset';
+import { AbstractConsumption } from '../../../types/Consumption';
+import { ServerAction } from '../../../types/Server';
+import { AssetConnectionSetting, AssetSetting } from '../../../types/Setting';
import Constants from '../../../utils/Constants';
import Cypher from '../../../utils/Cypher';
-import { ServerAction } from '../../../types/Server';
import Utils from '../../../utils/Utils';
-import axios from 'axios';
+import AssetIntegration from '../AssetIntegration';
const MODULE_NAME = 'SchneiderAssetIntegration';
@@ -32,11 +33,16 @@ export default class SchneiderAssetIntegration extends AssetIntegration<AssetSet
);
}
+ public async retrieveMeterValuesByID(asset: Asset, meterID: string): Promise<AbstractConsumption> {
+ return;
+ }
+
private isAssetConnectionInitialized(): void {
if (!this.connection) {
throw new BackendError({
source: Constants.CENTRAL_SERVER,
- module: MODULE_NAME, method: 'isAssetConnectionInitialized',
+ module: MODULE_NAME,
+ method: 'isAssetConnectionInitialized',
action: ServerAction.CHECK_CONNECTION,
message: 'No connection provided'
});

MODIFY
src/server/rest/CentralRestServerService.ts
src/server/rest/CentralRestServerService.ts
@@ -132,6 +132,7 @@ class RequestMapper {
[ServerAction.ASSET_IMAGE]: AssetService.handleGetAssetImage.bind(this),
[ServerAction.ASSETS_IN_ERROR]: AssetService.handleGetAssetsInError.bind(this),
[ServerAction.CHECK_ASSET_CONNECTION]: AssetService.handleCheckAssetConnection.bind(this),
+ [ServerAction.REFRESH_ASSET_CONNECTION]: AssetService.handleRefreshMetrics.bind(this),
[ServerAction.SITES]: SiteService.handleGetSites.bind(this),
[ServerAction.SITE]: SiteService.handleGetSite.bind(this),
[ServerAction.SITE_IMAGE]: SiteService.handleGetSiteImage.bind(this),

MODIFY
src/server/rest/service/AssetService.ts
src/server/rest/service/AssetService.ts
@@ -42,14 +42,14 @@ export default class AssetService {
});
}
// Is authorized to check connection ?
- if (!Authorizations.canCheckConnectionAsset(req.user)) {
- throw new AppError({
- source: Constants.CENTRAL_SERVER,
- errorCode: HTTPError.GENERAL_ERROR,
- message: 'Not authorized to check asset connections',
- module: MODULE_NAME, method: 'handleCheckAssetConnection',
- action: action,
- user: req.user
+ if (!Authorizations.canCheckAssetConnection(req.user)) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.ERROR,
+ user: req.user,
+ action: Action.CHECK_CONNECTION,
+ entity: Entity.ASSET,
+ module: MODULE_NAME,
+ method: 'handleCheckAssetConnection'
});
}
try {
@@ -73,6 +73,75 @@ export default class AssetService {
next();
}
+ public static async handleRefreshMetrics(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check if component is active
+ UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.ASSET,
+ Action.REFRESH_CONNECTION, Entity.ASSET, MODULE_NAME, 'handleRefreshMetrics');
+ // Is authorized to check connection ?
+ if (!Authorizations.canRefreshAssetConnection(req.user)) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.ERROR,
+ user: req.user,
+ action: Action.REFRESH_CONNECTION,
+ entity: Entity.ASSET,
+ module: MODULE_NAME,
+ method: 'handleRefreshMetrics'
+ });
+ }
+ // Filter request
+ const filteredRequestID = AssetSecurity.filterAssetRequestByID(req.query);
+ // Check Mandatory fields
+ UtilsService.assertIdIsProvided(action, filteredRequestID, MODULE_NAME, 'handleRefreshMetrics', req.user);
+ // Get
+ const asset = await AssetStorage.getAsset(req.user.tenantID, filteredRequestID);
+ // Filter
+ AssetSecurity.filterAssetResponse(asset, req.user);
+ // Found?
+ UtilsService.assertObjectExists(action, asset, `Asset with ID '${filteredRequestID}' does not exist`,
+ MODULE_NAME, 'handleRefreshMetrics', req.user);
+ // Dynamic asset ?
+ if (!asset.dynamicAsset) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'Not a dynamic asset',
+ module: MODULE_NAME, method: 'handleRefreshMetrics',
+ action: action,
+ 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);
+ res.json(Constants.REST_RESPONSE_SUCCESS);
+ next();
+ }
+
public static async handleGetAssetsInError(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.ASSET,

MODIFY
src/server/rest/service/security/AssetSecurity.ts
src/server/rest/service/security/AssetSecurity.ts
-import { HttpAssetRequest, HttpAssetsRequest } from '../../../../types/requests/HttpAssetRequest';
-<<<<<<< HEAD
-import UserToken from '../../../../types/UserToken';
-=======
-
-import Asset from '../../../../types/Asset';
-import { AssetConnectionSetting } from '../../../../types/Setting';
+import sanitize from 'mongo-sanitize';
import Authorizations from '../../../../authorization/Authorizations';
+import Asset from '../../../../types/Asset';
import { DataResult } from '../../../../types/DataResult';
->>>>>>> 44db7d61c79a40c0d7fec7aaae387ce2bead207b
-import SiteAreaSecurity from './SiteAreaSecurity';
+import { HttpAssetRequest, HttpAssetsRequest } from '../../../../types/requests/HttpAssetRequest';
import UserToken from '../../../../types/UserToken';
+import SiteAreaSecurity from './SiteAreaSecurity';
import UtilsSecurity from './UtilsSecurity';
-import sanitize from 'mongo-sanitize';
export default class AssetSecurity {

MODIFY
src/types/Authorization.ts
src/types/Authorization.ts
@@ -110,6 +110,7 @@ export enum Action {
SYNCHRONIZE_USER = 'SynchronizeUser',
SYNCHRONIZE_INVOICES = 'SynchronizeInvoices',
CHECK_CONNECTION = 'CheckConnection',
+ REFRESH_CONNECTION = 'RefreshConnection',
PING = 'Ping',
GENERATE_LOCAL_TOKEN = 'GenerateLocalToken',
REGISTER = 'Register',

MODIFY
src/types/Consumption.ts
src/types/Consumption.ts
import { ConnectorCurrentLimitSource, SiteAreaLimitSource } from './ChargingStation';
-export default interface Consumption {
- id: string;
- startedAt: Date;
- endedAt: Date;
- transactionId: number;
- chargeBoxID: string;
- connectorId: number;
- siteAreaID: string;
- siteID: string;
- consumptionWh: number;
- consumptionAmps: number;
- cumulatedAmount: number;
- cumulatedConsumptionWh: number;
- cumulatedConsumptionAmps: number;
- pricingSource: string;
- amount: number;
- roundedAmount: number;
- currencyCode: string;
+export interface AbstractConsumption {
instantWatts: number;
instantWattsL1: number;
instantWattsL2: number;
@@ -33,6 +16,26 @@ export default interface Consumption {
instantVoltsL2: number;
instantVoltsL3: number;
instantVoltsDC: number;
+ consumptionWh: number;
+ consumptionAmps: number;
+}
+
+export default interface Consumption extends AbstractConsumption {
+ id: string;
+ startedAt: Date;
+ endedAt: Date;
+ transactionId: number;
+ chargeBoxID: string;
+ connectorId: number;
+ siteAreaID: string;
+ siteID: string;
+ cumulatedAmount: number;
+ cumulatedConsumptionWh: number;
+ cumulatedConsumptionAmps: number;
+ pricingSource: string;
+ amount: number;
+ roundedAmount: number;
+ currencyCode: string;
totalInactivitySecs: number;
totalDurationSecs: number;
stateOfCharge: number;

MODIFY
src/types/Server.ts
src/types/Server.ts
@@ -264,6 +264,7 @@ export enum ServerAction {
ASSET_UPDATE = 'AssetUpdate',
ASSET_DELETE = 'AssetDelete',
CHECK_ASSET_CONNECTION = 'CheckAssetConnection',
+ REFRESH_ASSET_CONNECTION = 'RefreshAssetConnection',
TENANT_CREATE = 'TenantCreate',
TENANTS = 'Tenants',

MODIFY
src/utils/Utils.ts
src/utils/Utils.ts
-import { AnalyticsSettingsType, AssetSettingsType, BillingSettingsType, PricingSettingsType, RefundSettingsType, RoamingSettingsType, SettingDBContent, SmartChargingContentType } from '../types/Setting';
-import { Car, CarCatalog, CarType } from '../types/Car';
-import { ChargePointStatus, OCPPProtocol, OCPPVersion } from '../types/ocpp/OCPPServer';
-import ChargingStation, { ChargePoint, Connector, ConnectorCurrentLimitSource, CurrentType, StaticLimitAmps } from '../types/ChargingStation';
-import User, { UserRole, UserStatus } from '../types/User';
-
-import { ActionsResponse } from '../types/GlobalType';
-import AppError from '../exception/AppError';
-import Asset from '../types/Asset';
+import bcrypt from 'bcryptjs';
+import crypto from 'crypto';
+import { Request } from 'express';
+import fs from 'fs';
+import http from 'http';
+import _ from 'lodash';
+import moment from 'moment';
+import { ObjectID } from 'mongodb';
+import passwordGenerator from 'password-generator';
+import path from 'path';
+import tzlookup from 'tz-lookup';
+import url from 'url';
+import { v4 as uuid } from 'uuid';
+import validator from 'validator';
import Authorizations from '../authorization/Authorizations';
+import AppError from '../exception/AppError';
import BackendError from '../exception/BackendError';
+import TenantStorage from '../storage/mongodb/TenantStorage';
+import UserStorage from '../storage/mongodb/UserStorage';
+import Asset from '../types/Asset';
+import { Car, CarCatalog, CarType } from '../types/Car';
import { ChargingProfile } from '../types/ChargingProfile';
+import ChargingStation, { ChargePoint, Connector, ConnectorCurrentLimitSource, CurrentType } from '../types/ChargingStation';
import Company from '../types/Company';
-import Configuration from './Configuration';
import ConnectorStats from '../types/ConnectorStats';
-import Constants from './Constants';
-import Cypher from './Cypher';
+import { ActionsResponse } from '../types/GlobalType';
import { HTTPError } from '../types/HTTPError';
-import Transaction, { InactivityStatus } from '../types/Transaction';
-import Logging from './Logging';
import OCPIEndpoint from '../types/ocpi/OCPIEndpoint';
-import { ObjectID } from 'mongodb';
-import { Request } from 'express';
+import { ChargePointStatus, OCPPProtocol, OCPPVersion } from '../types/ocpp/OCPPServer';
import { ServerAction } from '../types/Server';
+import { AnalyticsSettingsType, AssetSettingsType, BillingSettingsType, PricingSettingsType, RefundSettingsType, RoamingSettingsType, SettingDBContent, SmartChargingContentType } from '../types/Setting';
import Site from '../types/Site';
import SiteArea from '../types/SiteArea';
import Tag from '../types/Tag';
import Tenant from '../types/Tenant';
import TenantComponents from '../types/TenantComponents';
-import TenantStorage from '../storage/mongodb/TenantStorage';
-import UserStorage from '../storage/mongodb/UserStorage';
+import Transaction, { InactivityStatus } from '../types/Transaction';
+import User, { UserRole, UserStatus } from '../types/User';
import UserToken from '../types/UserToken';
-import _ from 'lodash';
-import bcrypt from 'bcryptjs';
-import crypto from 'crypto';
-import fs from 'fs';
-import http from 'http';
-import moment from 'moment';
-import passwordGenerator from 'password-generator';
-import path from 'path';
-import tzlookup from 'tz-lookup';
-import url from 'url';
-import { v4 as uuid } from 'uuid';
-import validator from 'validator';
+import Configuration from './Configuration';
+import Constants from './Constants';
+import Cypher from './Cypher';
+import Logging from './Logging';
+
const _centralSystemFrontEndConfig = Configuration.getCentralSystemFrontEndConfig();
const _tenants = [];
@@ -1883,6 +1883,13 @@ 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
+ }
+
private static _isUserEmailValid(email: string): boolean {
return validator.isEmail(email);
}

TODO: Retrieve Asset Metrics