Added sticky limitation settings
MODIFY
src/server/rest/v1/service/security/SettingSecurity.ts
src/server/rest/v1/service/security/SettingSecurity.ts
@@ -152,6 +152,9 @@ export default class SettingSecurity {
optimizerUrl: sanitize(request.content.sapSmartCharging.optimizerUrl),
user: sanitize(request.content.sapSmartCharging.user),
password: sanitize(request.content.sapSmartCharging.password),
+ stickyLimitation: sanitize(request.content.sapSmartCharging.stickyLimitation),
+ limitBufferDC: sanitize(request.content.sapSmartCharging.limitBufferDC),
+ limitBufferAC: sanitize(request.content.sapSmartCharging.limitBufferAC),
};
break;
case AssetSettingsType.ASSET:

MODIFY
src/storage/mongodb/SettingStorage.ts
src/storage/mongodb/SettingStorage.ts
@@ -224,6 +224,9 @@ export default class SettingStorage {
optimizerUrl: config.sapSmartCharging.optimizerUrl,
user: config.sapSmartCharging.user,
password: config.sapSmartCharging.password,
+ stickyLimitation: config.sapSmartCharging.stickyLimitation ? config.sapSmartCharging.stickyLimitation : false,
+ limitBufferDC: config.sapSmartCharging.limitBufferDC ? config.sapSmartCharging.limitBufferDC : 0,
+ limitBufferAC: config.sapSmartCharging.limitBufferAC ? config.sapSmartCharging.limitBufferAC : 0,
};
}
}

MODIFY
src/types/Setting.ts
src/types/Setting.ts
@@ -140,6 +140,9 @@ export interface SapSmartChargingSetting extends SmartChargingSetting {
optimizerUrl: string;
user: string;
password: string;
+ stickyLimitation: boolean;
+ limitBufferDC?: number;
+ limitBufferAC?: number;
}
export enum RefundSettingsType {

MODIFY
test/api/SmartChargingTest.ts
test/api/SmartChargingTest.ts
@@ -27,6 +27,8 @@ chai.use(chaiSubset);
chai.use(responseHelper);
let smartChargingIntegration: SmartChargingIntegration<SmartChargingSetting>;
+let smartChargingIntegrationWithDifferentBufferValues: SmartChargingIntegration<SmartChargingSetting>;
+let smartChargingIntegrationWithoutStickyLimit: SmartChargingIntegration<SmartChargingSetting>;
class TestData {
public tenantContext: TenantContext;
@@ -46,8 +48,19 @@ class TestData {
const sapSmartChargingSettings = TestData.getSmartChargingSettings();
await TestData.saveSmartChargingSettings(testData, sapSmartChargingSettings);
sapSmartChargingSettings.password = Cypher.encrypt(sapSmartChargingSettings.password);
+ aCBufferFactor = 1 + sapSmartChargingSettings.limitBufferAC / 100,
+ dCBufferFactor = 1 + sapSmartChargingSettings.limitBufferDC / 100,
smartChargingIntegration = await SmartChargingFactory.getSmartChargingImpl(testData.tenantContext.getTenant().id);
+ sapSmartChargingSettings.limitBufferDC = 10,
+ sapSmartChargingSettings.limitBufferAC = 5,
+ await TestData.saveSmartChargingSettings(testData, sapSmartChargingSettings);
+ smartChargingIntegrationWithDifferentBufferValues = await SmartChargingFactory.getSmartChargingImpl(testData.tenantContext.getTenant().id);
+ sapSmartChargingSettings.stickyLimitation = false;
+ await TestData.saveSmartChargingSettings(testData, sapSmartChargingSettings);
+ smartChargingIntegrationWithoutStickyLimit = await SmartChargingFactory.getSmartChargingImpl(testData.tenantContext.getTenant().id);
expect(smartChargingIntegration).to.not.be.null;
+ expect(smartChargingIntegrationWithDifferentBufferValues).to.not.be.null;
+ expect(smartChargingIntegrationWithoutStickyLimit).to.not.be.null;
}
public static getSmartChargingSettings(): SapSmartChargingSetting {
@@ -55,6 +68,9 @@ class TestData {
optimizerUrl: config.get('smartCharging.optimizerUrl'),
user: config.get('smartCharging.user'),
password: config.get('smartCharging.password'),
+ stickyLimitation: config.get('smartCharging.stickyLimitation'),
+ limitBufferAC: config.get('smartCharging.limitBufferAC'),
+ limitBufferDC: config.get('smartCharging.limitBufferDC')
} as SapSmartChargingSetting;
}
@@ -241,8 +257,8 @@ const limit0 = [
];
// Buffer Factors
-const aCBufferFactor = 1 + Constants.AC_LIMIT_BUFFER_PERCENT / 100;
-const dCBufferFactor = 1 + Constants.DC_LIMIT_BUFFER_PERCENT / 100;
+let aCBufferFactor = 0;
+let dCBufferFactor = 0;
// Helpers to store ongoing transactions across different tests
@@ -465,6 +481,80 @@ describe('Smart Charging Service', function() {
]);
});
+ it('Test for sticky limit with different buffer value - 1 three phased and 2 single phased cars charging with lower site area limit and one car on a single phased station', async () => {
+ const chargingProfiles = await smartChargingIntegrationWithDifferentBufferValues.buildChargingProfiles(testData.siteAreaContext.getSiteArea());
+ // Charging Profiles should have limits according the sent meter values + buffer
+ TestData.validateChargingProfile(chargingProfiles[0], transaction);
+ expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset([
+ {
+ 'startPeriod': 0,
+ 'limit': Utils.truncTo(13 * 3 * 1.05, 0)
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': Utils.truncTo(13 * 3 * 1.05, 0)
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': Utils.truncTo(13 * 3 * 1.05, 0)
+ }
+ ]);
+ TestData.validateChargingProfile(chargingProfiles[1], transaction1);
+ expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset([
+ {
+ 'startPeriod': 0,
+ 'limit': Utils.truncTo(24 * 3 * 1.05, 0)
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': Utils.truncTo(24 * 3 * 1.05, 0)
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': Utils.truncTo(24 * 3 * 1.05, 0)
+ }
+ ]);
+ TestData.validateChargingProfile(chargingProfiles[2], transaction2);
+ expect(chargingProfiles[2].profile.chargingSchedule.chargingSchedulePeriod).containSubset([
+ {
+ 'startPeriod': 0,
+ 'limit': Utils.truncTo(20 * 1.05, 0)
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': Utils.truncTo(20 * 1.05, 0)
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': Utils.truncTo(20 * 1.05, 0)
+ }
+ ]);
+ });
+
+ it('Test for sticky limit disabled - 1 three phased and 2 single phased cars charging with lower site area limit and one car on a single phased station', async () => {
+ const chargingProfiles = await smartChargingIntegrationWithoutStickyLimit.buildChargingProfiles(testData.siteAreaContext.getSiteArea());
+ // Charging Profiles should have limits according the sent meter values + buffer
+ TestData.validateChargingProfile(chargingProfiles[0], transaction);
+ expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset([
+ {
+ 'startPeriod': 0,
+ 'limit': 43
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': 43
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': 43
+ }
+ ]);
+ TestData.validateChargingProfile(chargingProfiles[1], transaction1);
+ expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit96);
+ TestData.validateChargingProfile(chargingProfiles[2], transaction2);
+ expect(chargingProfiles[2].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit32);
+ });
+
it('Test for sticky limit - 1 three phased and 2 single phased cars charging with lower site area limit and one car on a single phased station with no consumption on two cars', async () => {
// Send meter values for all stations
await TestData.sendMeterValue(230, 0, transaction, testData.chargingStationContext, { csPhase1: true, csPhase2: true, csPhase3: true });
@@ -571,6 +661,47 @@ describe('Smart Charging Service', function() {
}
]);
});
+
+ it('Test for sticky limit with different buffer values - two cars charging with lower site area limit', async () => {
+ const chargingProfiles = await smartChargingIntegrationWithDifferentBufferValues.buildChargingProfiles(testData.siteAreaContext.getSiteArea());
+ TestData.validateChargingProfile(chargingProfiles[0], transaction);
+ expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset([
+ {
+ 'startPeriod': 0,
+ 'limit': Utils.truncTo(16 * 1.05, 0)
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': Utils.truncTo(16 * 1.05, 0)
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': Utils.truncTo(16 * 1.05, 0)
+ }
+ ]);
+ TestData.validateChargingProfile(chargingProfiles[1], transaction1);
+ expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset([
+ {
+ 'startPeriod': 0,
+ 'limit': Utils.truncTo(20 * 1.05, 0)
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': Utils.truncTo(20 * 1.05, 0)
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': Utils.truncTo(20 * 1.05, 0)
+ }
+ ]);
+ });
+ it('Test for sticky limit disabled - two cars charging with lower site area limit', async () => {
+ const chargingProfiles = await smartChargingIntegrationWithoutStickyLimit.buildChargingProfiles(testData.siteAreaContext.getSiteArea());
+ TestData.validateChargingProfile(chargingProfiles[0], transaction);
+ expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit0);
+ TestData.validateChargingProfile(chargingProfiles[1], transaction1);
+ expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit32);
+ });
it('Test for sticky limit - two cars charging with lower site area limit and no consumption on one car', async () => {
await TestData.sendMeterValue(230, 0, transaction, testData.chargingStationContext, { csPhase1: true, csPhase2: false, csPhase3:false });
await TestData.sendMeterValue(230, 20, transaction1, testData.chargingStationContext, { csPhase1: true, csPhase2: false, csPhase3:false });
@@ -740,6 +871,74 @@ describe('Smart Charging Service', function() {
}
]);
});
+
+ it('Test for sticky limit - two cars charging with lower site area limit', async () => {
+ const chargingProfiles = await smartChargingIntegrationWithDifferentBufferValues.buildChargingProfiles(testData.siteAreaContext.getSiteArea());
+ TestData.validateChargingProfile(chargingProfiles[0], transaction);
+ expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset([
+ {
+ 'startPeriod': 0,
+ 'limit': Utils.truncTo(40000 / 230 / 3 * 1.1, 0) * 3
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': Utils.truncTo(40000 / 230 / 3 * 1.1, 0) * 3
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': Utils.truncTo(40000 / 230 / 3 * 1.1, 0) * 3
+ },
+ ]);
+ TestData.validateChargingProfile(chargingProfiles[1], transaction1);
+ expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset([
+ {
+ 'startPeriod': 0,
+ 'limit': Utils.truncTo(30000 / 230 / 3 * 1.1, 0) * 3
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': Utils.truncTo(30000 / 230 / 3 * 1.1, 0) * 3
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': Utils.truncTo(30000 / 230 / 3 * 1.1, 0) * 3
+ }
+ ]);
+ });
+
+ it('Test for sticky limit disabled - two cars charging with lower site area limit', async () => {
+ const chargingProfiles = await smartChargingIntegrationWithoutStickyLimit.buildChargingProfiles(testData.siteAreaContext.getSiteArea());
+ TestData.validateChargingProfile(chargingProfiles[0], transaction);
+ expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset([
+ {
+ 'startPeriod': 0,
+ 'limit': 84
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': 84
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': 84
+ },
+ ]);
+ TestData.validateChargingProfile(chargingProfiles[1], transaction1);
+ expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset([
+ {
+ 'startPeriod': 0,
+ 'limit': 327
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': 327
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': 327
+ }
+ ]);
+ });
});
});
describe('Test for ChargingStation refusing the charging profile', () => {

MODIFY
test/api/context/ContextDefinition.ts
test/api/context/ContextDefinition.ts
@@ -162,7 +162,8 @@ export default class ContextDefinition {
sapSmartCharging: {
optimizerUrl: '',
user: '',
- password: ''
+ password: '',
+ stickyLimitation: true,
}
}
},
@@ -342,7 +343,10 @@ export default class ContextDefinition {
{ type: SmartChargingSettingsType.SAP_SMART_CHARGING, sapSmartCharging:
{ optimizerUrl: '',
user: '',
- password: '' }
+ password: '',
+ stickyLimitation: true,
+ limitBufferDC: 20,
+ limitBufferAC: 10, }
}
},
}

MODIFY
test/config.js
test/config.js
@@ -309,6 +309,21 @@ const config = convict({
format: String,
default: ''
},
+ stickyLimitation: {
+ doc: 'Sticky limitation',
+ format: Boolean,
+ default: ''
+ },
+ limitBufferDC: {
+ doc: 'Limit buffer dc',
+ format: Number,
+ default: ''
+ },
+ limitBufferAC: {
+ doc: 'Limit buffer ac',
+ format: Number,
+ default: ''
+ }
}
});

Added sticky limitation settings