Asset - Retrieve missed consumptions  caused by possible downtime
MODIFY
src/integration/asset/AssetIntegration.ts
src/integration/asset/AssetIntegration.ts
@@ -16,6 +16,6 @@ export default abstract class AssetIntegration<T extends AssetSetting> {
abstract checkConnection(): Promise<void>;
- abstract retrieveConsumption(asset: Asset, manualCall?: boolean): Promise<AbstractCurrentConsumption>;
+ abstract retrieveConsumption(asset: Asset, manualCall?: boolean): Promise<AbstractCurrentConsumption[]>;
}

MODIFY
src/integration/asset/greencom/GreencomAssetIntegration.ts
src/integration/asset/greencom/GreencomAssetIntegration.ts
import Asset, { AssetType } from '../../../types/Asset';
import { AssetConnectionSetting, AssetSetting } from '../../../types/Setting';
-import Consumption, { AbstractCurrentConsumption } from '../../../types/Consumption';
+import { AbstractCurrentConsumption } from '../../../types/Consumption';
import AssetIntegration from '../AssetIntegration';
import AxiosFactory from '../../../utils/AxiosFactory';
import { AxiosInstance } from 'axios';
@@ -27,7 +27,7 @@ export default class GreencomAssetIntegration extends AssetIntegration<AssetSett
await this.connect();
}
- public async retrieveConsumption(asset: Asset, manualCall?: boolean): Promise<AbstractCurrentConsumption> {
+ public async retrieveConsumption(asset: Asset, manualCall?: boolean): Promise<AbstractCurrentConsumption[]> {
// Set new Token
const token = await this.connect();
const request = manualCall ?
@@ -62,10 +62,64 @@ export default class GreencomAssetIntegration extends AssetIntegration<AssetSett
}
}
- private filterConsumptionRequest(asset: Asset, data: any): AbstractCurrentConsumption {
+ private filterConsumptionRequest(asset: Asset, data: any): AbstractCurrentConsumption[] {
+ const consumptions: AbstractCurrentConsumption[] = [];
+ // Check if result is array of consumptions (happens when no consumption was received over some time)
+ if (Array.isArray(data.power.data) || Array.isArray(data.power.charge.data)) {
+ switch (asset.assetType) {
+ case AssetType.CONSUMPTION:
+ for (let i = 0; i < data.energy.data.length; i++) {
+ const consumption = {
+ currentConsumptionWh: data.energy.data[i].value,
+ lastConsumption: {
+ // Timestamp in array is always start date of the one minute interval
+ timestamp: moment(data.energy.data[i].timestamp).add(1, 'minutes').toDate(),
+ value: data.energy.data[i].value
+ },
+ currentInstantWatts: data.power.data[i].value,
+ } as AbstractCurrentConsumption;
+ if (asset.siteArea?.voltage) {
+ consumption.currentInstantAmps = consumption.currentInstantWatts / asset.siteArea.voltage;
+ }
+ consumptions.push(consumption);
+ }
+ break;
+ case AssetType.PRODUCTION:
+ for (let i = 0; i < data.energy.data.length; i++) {
+ const consumption = {
+ currentConsumptionWh: data.energy.data[i].value * -1,
+ lastConsumption: {
+ timestamp: moment(data.energy.data[i].timestamp).add(1, 'minutes').toDate(),
+ value: data.energy.data[i].value
+ },
+ currentInstantWatts: data.power.data[i].value * -1,
+ } as AbstractCurrentConsumption;
+ if (asset.siteArea?.voltage) {
+ consumption.currentInstantAmps = consumption.currentInstantWatts / asset.siteArea.voltage;
+ }
+ consumptions.push(consumption);
+ }
+ break;
+ case AssetType.CONSUMPTION_AND_PRODUCTION:
+ for (let i = 0; i < data.energy.charge.data.length; i++) {
+ const consumption = {
+ currentConsumptionWh: data.energy.charge.data[i].value - data.energy.discharge.data[i].value,
+ lastConsumption:{
+ timestamp: moment(data.energy.charge.data[i].timestamp).add(1, 'minutes').toDate(),
+ value: data.energy.charge.data[i].value - data.energy.discharge.data[i].value,
+ },
+ currentInstantWatts: data.power.charge.data[i].value - data.power.discharge.data[i].value,
+ currentStateOfCharge: data.percent?.soc?.data[i].value,
+ } as AbstractCurrentConsumption;
+ if (asset.siteArea?.voltage) {
+ consumption.currentInstantAmps = consumption.currentInstantWatts / asset.siteArea.voltage;
+ }
+ consumptions.push(consumption);
+ }
+ break;
+ }
+ } else if (data.power && data.energy) {
const consumption = {} as AbstractCurrentConsumption;
- // Convert data
- if (data.power && data.energy) {
switch (asset.assetType) {
case AssetType.CONSUMPTION:
consumption.currentInstantWatts = data.power.average;
@@ -81,19 +135,18 @@ export default class GreencomAssetIntegration extends AssetIntegration<AssetSett
consumption.currentStateOfCharge = data.percent?.soc?.current;
break;
}
- }
// Check if site area provided and set amp value
if (asset.siteArea?.voltage) {
consumption.currentInstantAmps = consumption.currentInstantWatts / asset.siteArea.voltage;
}
-
// Set last consumption
consumption.lastConsumption = {
value: consumption.currentConsumptionWh,
timestamp: new Date()
};
-
- return consumption;
+ consumptions.push(consumption);
+ }
+ return consumptions;
}
private async connect(): Promise<string> {

MODIFY
src/integration/asset/schneider/SchneiderAssetIntegration.ts
src/integration/asset/schneider/SchneiderAssetIntegration.ts
@@ -26,7 +26,7 @@ export default class SchneiderAssetIntegration extends AssetIntegration<AssetSet
await this.connect();
}
- public async retrieveConsumption(asset: Asset): Promise<AbstractCurrentConsumption> {
+ public async retrieveConsumption(asset: Asset): Promise<AbstractCurrentConsumption[]> {
// Set new Token
const token = await this.connect();
const request = `${this.connection.url}/${asset.meterID}`;
@@ -60,7 +60,7 @@ export default class SchneiderAssetIntegration extends AssetIntegration<AssetSet
}
- private filterConsumptionRequest(asset: Asset, data: any[]): AbstractCurrentConsumption {
+ private filterConsumptionRequest(asset: Asset, data: any[]): AbstractCurrentConsumption[] {
const consumption = {} as AbstractCurrentConsumption;
// Convert data value to number and get consumption
const newConsumptionWh = this.getPropertyValue(data, SchneiderProperty.ENERGY_ACTIVE) * 1000;
@@ -87,7 +87,7 @@ export default class SchneiderAssetIntegration extends AssetIntegration<AssetSet
consumption.currentInstantWattsL1 = this.getPropertyValue(data, SchneiderProperty.POWER_ACTIVE_L1) * 1000 * energyDirection;
consumption.currentInstantWattsL2 = this.getPropertyValue(data, SchneiderProperty.POWER_ACTIVE_L2) * 1000 * energyDirection;
consumption.currentInstantWattsL3 = this.getPropertyValue(data, SchneiderProperty.POWER_ACTIVE_L3) * 1000 * energyDirection;
- return consumption;
+ return [consumption];
}
private getPropertyValue(data: any[], propertyName: string): number {

MODIFY
src/scheduler/tasks/AssetGetConsumptionTask.ts
src/scheduler/tasks/AssetGetConsumptionTask.ts
@@ -39,26 +39,38 @@ export default class AssetGetConsumptionTask extends SchedulerTask {
const assetImpl = await AssetFactory.getAssetImpl(tenant.id, asset.connectionID);
if (assetImpl) {
// Retrieve Consumption
- const assetConsumption = await assetImpl.retrieveConsumption(asset);
+ const assetConsumptions = await assetImpl.retrieveConsumption(asset);
+ // Create helper for site area limit
+ const siteAreaLimitConsumption: Consumption = {
+ startedAt: assetConsumptions[0].lastConsumption.timestamp,
+ cumulatedConsumptionWh: assetConsumptions[0].currentConsumptionWh,
+ cumulatedConsumptionAmps: Math.floor(assetConsumptions[0].currentConsumptionWh / asset.siteArea.voltage),
+ };
+ await OCPPUtils.addSiteLimitationToConsumption(tenant.id, asset.siteArea, siteAreaLimitConsumption);
+ // Create Consumptions
+ for (const consumption of assetConsumptions) {
// Create Consumption to save
- const consumption: Consumption = {
+ const consumptionToSave: Consumption = {
startedAt: asset.lastConsumption.timestamp,
- endedAt: new Date(),
+ endedAt: consumption.lastConsumption.timestamp,
assetID: asset.id,
- cumulatedConsumptionWh: assetConsumption.currentConsumptionWh,
- cumulatedConsumptionAmps: Math.floor(assetConsumption.currentConsumptionWh / asset.siteArea.voltage),
- instantAmps: assetConsumption.currentInstantAmps,
- instantWatts: assetConsumption.currentInstantWatts,
- stateOfCharge: assetConsumption.currentStateOfCharge,
+ cumulatedConsumptionWh: consumption.currentConsumptionWh,
+ cumulatedConsumptionAmps: Math.floor(consumption.currentConsumptionWh / asset.siteArea.voltage),
+ instantAmps: consumption.currentInstantAmps,
+ instantWatts: consumption.currentInstantWatts,
+ stateOfCharge: consumption.currentStateOfCharge,
+ limitSiteAreaWatts: siteAreaLimitConsumption.limitSiteAreaWatts,
+ limitSiteAreaAmps: siteAreaLimitConsumption.limitSiteAreaAmps,
+ limitSiteAreaSource: siteAreaLimitConsumption.limitSiteAreaSource,
+ smartChargingActive: siteAreaLimitConsumption.smartChargingActive,
};
+ // Save Consumption
+ await ConsumptionStorage.saveConsumption(tenant.id, consumptionToSave);
// Set Consumption to Asset
- this.assignAssetConsumption(asset, assetConsumption);
+ this.assignAssetConsumption(asset, consumption);
+ }
// Save Asset
await AssetStorage.saveAsset(tenant.id, asset);
- // Add limits
- await OCPPUtils.addSiteLimitationToConsumption(tenant.id, asset.siteArea, consumption);
- // Save Consumption
- await ConsumptionStorage.saveConsumption(tenant.id, consumption);
}
} catch (error) {
// Log error

MODIFY
src/server/rest/v1/service/AssetService.ts
src/server/rest/v1/service/AssetService.ts
@@ -176,7 +176,8 @@ export default class AssetService {
});
}
// Retrieve consumption
- const consumption = await assetImpl.retrieveConsumption(asset, true);
+ const consumptions = await assetImpl.retrieveConsumption(asset, true);
+ const consumption = consumptions[0];
// Assign
asset.lastConsumption = consumption.lastConsumption;
asset.currentConsumptionWh = consumption.currentConsumptionWh;

Asset - Retrieve missed consumptions  caused by possible downtime