Single phased site area + test
MODIFY
test/api/SmartChargingTest.ts
test/api/SmartChargingTest.ts
+import { ChargePointErrorCode, ChargePointStatus, OCPPStatusNotificationRequest } from '../../src/types/ocpp/OCPPServer';
import { SapSmartChargingSetting, SettingDB, SmartChargingSetting, SmartChargingSettingsType } from '../../src/types/Setting';
import chai, { assert, expect } from 'chai';
import CentralServerService from './client/CentralServerService';
-import { ChargePointStatus } from '../../src/types/ocpp/OCPPServer';
import ChargingStationContext from './context/ChargingStationContext';
-import { default as ClientConstants } from './client/utils/Constants';
-import { Connector } from '../types/ChargingStation';
import Constants from '../../src/utils/Constants';
import ContextDefinition from './context/ContextDefinition';
import ContextProvider from './context/ContextProvider';
@@ -19,7 +17,6 @@ import User from '../../src/types/User';
import chaiSubset from 'chai-subset';
import config from '../config';
import global from '../../src/types/GlobalType';
-import moment from 'moment';
import responseHelper from '../helpers/responseHelper';
chai.use(chaiSubset);
@@ -70,6 +67,19 @@ class TestData {
const testData: TestData = new TestData();
+const chargingStationConnector1: OCPPStatusNotificationRequest = {
+ connectorId: 1,
+ status: ChargePointStatus.AVAILABLE,
+ errorCode: ChargePointErrorCode.NO_ERROR,
+ timestamp: new Date().toISOString()
+};
+const chargingStationConnector2: OCPPStatusNotificationRequest = {
+ connectorId: 2,
+ status: ChargePointStatus.AVAILABLE,
+ errorCode: ChargePointErrorCode.NO_ERROR,
+ timestamp: new Date().toISOString()
+};
+
describe('Smart Charging Service', function() {
this.pending = testData.pending;
@@ -87,10 +97,6 @@ describe('Smart Charging Service', function() {
testData.centralUserContext
);
testData.isForcedSynchro = false;
- });
-
- describe('Test for three phased site area', () => {
- before(async () => {
testData.userContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
assert(testData.userContext, 'User context cannot be null');
if (testData.userContext === testData.centralUserContext) {
@@ -109,6 +115,15 @@ describe('Smart Charging Service', function() {
} else {
throw new Error(`Unable to get Tenant ID for tenant : ${ContextDefinition.TENANT_CONTEXTS.TENANT_SMART_CHARGING}`);
}
+ });
+
+ it('Should connect to Smart Charging Provider', async () => {
+ const response = await testData.userService.smartChargingApi.testConnection({});
+ expect(response.data).containSubset(Constants.REST_RESPONSE_SUCCESS);
+ });
+
+ describe('Test for three phased site area', () => {
+ before(async () => {
testData.siteContext = testData.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_BASIC);
testData.siteAreaContext = testData.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_SMART_CHARGING_THREE_PHASED);
testData.chargingStationContext = testData.siteAreaContext.getChargingStationContext(ContextDefinition.CHARGING_STATION_CONTEXTS.ASSIGNED_OCPP16);
@@ -116,21 +131,14 @@ describe('Smart Charging Service', function() {
testData.siteAreaContext.siteArea.numberOfPhases = 3;
});
- it('Should connect to Smart Charging Provider', async () => {
- const response = await testData.userService.smartChargingApi.testConnection({}, ClientConstants.DEFAULT_PAGING, ClientConstants.DEFAULT_ORDERING);
- expect(response.data).containSubset(Constants.REST_RESPONSE_SUCCESS);
- });
-
it('Test for one car charging', async () => {
const connectorId = 1;
const tagId = testData.userContext.tags[0].id;
const meterStart = 180;
- const startDate = moment();
+ const startDate = new Date;
const transactionResponse = await testData.chargingStationContext.startTransaction(connectorId, tagId, meterStart, startDate);
- const connector = testData.chargingStationContext.getChargingStation().connectors[0];
- connector.currentTransactionID = transactionResponse.data.transactionId;
- connector.status = ChargePointStatus.CHARGING;
- await testData.chargingStationContext.setConnectorStatus(connector);
+ chargingStationConnector1.status = ChargePointStatus.CHARGING;
+ await testData.chargingStationContext.setConnectorStatus(chargingStationConnector1);
const chargingProfiles = await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.siteArea);
expect(chargingProfiles).containSubset([{
'chargingStationID': 'cs-16-ut-site-withSmartChargingThreePhased',
@@ -167,12 +175,10 @@ describe('Smart Charging Service', function() {
const connectorId = 2;
const tagId = testData.userContext.tags[0].id;
const meterStart = 180;
- const startDate = moment();
+ const startDate = new Date;
const transactionResponse = await testData.chargingStationContext.startTransaction(connectorId, tagId, meterStart, startDate);
- const connector = testData.chargingStationContext.getChargingStation().connectors[1];
- connector.currentTransactionID = transactionResponse.data.transactionId;
- connector.status = ChargePointStatus.CHARGING;
- await testData.chargingStationContext.setConnectorStatus(connector);
+ chargingStationConnector2.status = ChargePointStatus.CHARGING;
+ await testData.chargingStationContext.setConnectorStatus(chargingStationConnector2);
const chargingProfiles = await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.siteArea);
expect(chargingProfiles[0]).containSubset({
'chargingStationID': 'cs-16-ut-site-withSmartChargingThreePhased',
@@ -236,7 +242,6 @@ describe('Smart Charging Service', function() {
it('Test for two cars charging with lower site area limit', async () => {
testData.siteAreaContext.siteArea.maximumPower = 30000;
const chargingProfiles = await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.siteArea);
- console.log(JSON.stringify(chargingProfiles, null, ' '));
expect(chargingProfiles[0]).containSubset({
'chargingStationID': 'cs-16-ut-site-withSmartChargingThreePhased',
'connectorID': 1,
@@ -297,5 +302,186 @@ describe('Smart Charging Service', function() {
});
});
});
+ describe('Test for single phased site area', () => {
+ before(async () => {
+ testData.siteContext = testData.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_BASIC);
+ testData.siteAreaContext = testData.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_SMART_CHARGING_SINGLE_PHASED);
+ testData.chargingStationContext = testData.siteAreaContext.getChargingStationContext(ContextDefinition.CHARGING_STATION_CONTEXTS.ASSIGNED_OCPP16);
+ testData.siteAreaContext.siteArea.voltage = 230;
+ testData.siteAreaContext.siteArea.numberOfPhases = 1;
+ });
+
+
+ it('Test for one car charging', async () => {
+ const connectorId = 1;
+ const tagId = testData.userContext.tags[0].id;
+ const meterStart = 180;
+ const startDate = new Date;
+ const transactionResponse = await testData.chargingStationContext.startTransaction(connectorId, tagId, meterStart, startDate);
+ chargingStationConnector1.status = ChargePointStatus.CHARGING;
+ await testData.chargingStationContext.setConnectorStatus(chargingStationConnector1);
+ const chargingProfiles = await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.siteArea);
+ expect(chargingProfiles).containSubset([{
+ 'chargingStationID': 'cs-16-ut-site-withSmartChargingSinglePhased',
+ 'connectorID': 1,
+ 'chargePointID': 1,
+ 'profile': {
+ 'chargingProfileId': 1,
+ 'chargingProfileKind': 'Absolute',
+ 'chargingProfilePurpose': 'TxProfile',
+ 'stackLevel': 2,
+ 'chargingSchedule': {
+ 'chargingRateUnit': 'A',
+ 'chargingSchedulePeriod': [
+ {
+ 'startPeriod': 0,
+ 'limit': 32
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': 32
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': 32
+ }
+ ],
+ 'duration': 2700
+ }
+ }
+ }]);
+ });
+
+ it('Test for two cars charging', async () => {
+ const connectorId = 2;
+ const tagId = testData.userContext.tags[0].id;
+ const meterStart = 180;
+ const startDate = new Date;
+ const transactionResponse = await testData.chargingStationContext.startTransaction(connectorId, tagId, meterStart, startDate);
+ chargingStationConnector2.status = ChargePointStatus.CHARGING;
+ await testData.chargingStationContext.setConnectorStatus(chargingStationConnector2);
+ const chargingProfiles = await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.siteArea);
+ expect(chargingProfiles[0]).containSubset({
+ 'chargingStationID': 'cs-16-ut-site-withSmartChargingSinglePhased',
+ 'connectorID': 1,
+ 'chargePointID': 1,
+ 'profile': {
+ 'chargingProfileId': 1,
+ 'chargingProfileKind': 'Absolute',
+ 'chargingProfilePurpose': 'TxProfile',
+ 'stackLevel': 2,
+ 'chargingSchedule': {
+ 'chargingRateUnit': 'A',
+ 'chargingSchedulePeriod': [
+ {
+ 'startPeriod': 0,
+ 'limit': 32
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': 32
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': 32
+ }
+ ],
+ 'duration': 2700
+ }
+ }
+ });
+ expect(chargingProfiles[1]).containSubset({
+ 'chargingStationID': 'cs-16-ut-site-withSmartChargingSinglePhased',
+ 'connectorID': 2,
+ 'chargePointID': 1,
+ 'profile': {
+ 'chargingProfileId': 2,
+ 'chargingProfileKind': 'Absolute',
+ 'chargingProfilePurpose': 'TxProfile',
+ 'stackLevel': 2,
+ 'chargingSchedule': {
+ 'chargingRateUnit': 'A',
+ 'chargingSchedulePeriod': [
+ {
+ 'startPeriod': 0,
+ 'limit': 32
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': 32
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': 32
+ }
+ ],
+ 'duration': 2700
+ }
+ }
+ });
+ });
+ it('Test for two cars charging with lower site area limit', async () => {
+ testData.siteAreaContext.siteArea.maximumPower = 10000;
+ const chargingProfiles = await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.siteArea);
+ expect(chargingProfiles[0]).containSubset({
+ 'chargingStationID': 'cs-16-ut-site-withSmartChargingSinglePhased',
+ 'connectorID': 1,
+ 'chargePointID': 1,
+ 'profile': {
+ 'chargingProfileId': 1,
+ 'chargingProfileKind': 'Absolute',
+ 'chargingProfilePurpose': 'TxProfile',
+ 'stackLevel': 2,
+ 'chargingSchedule': {
+ 'chargingRateUnit': 'A',
+ 'chargingSchedulePeriod': [
+ {
+ 'startPeriod': 0,
+ 'limit': 11
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': 11
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': 11
+ }
+ ],
+ 'duration': 2700
+ }
+ }
+ });
+ expect(chargingProfiles[1]).containSubset({
+ 'chargingStationID': 'cs-16-ut-site-withSmartChargingSinglePhased',
+ 'connectorID': 2,
+ 'chargePointID': 1,
+ 'profile': {
+ 'chargingProfileId': 2,
+ 'chargingProfileKind': 'Absolute',
+ 'chargingProfilePurpose': 'TxProfile',
+ 'stackLevel': 2,
+ 'chargingSchedule': {
+ 'chargingRateUnit': 'A',
+ 'chargingSchedulePeriod': [
+ {
+ 'startPeriod': 0,
+ 'limit': 32
+ },
+ {
+ 'startPeriod': 900,
+ 'limit': 32
+ },
+ {
+ 'startPeriod': 1800,
+ 'limit': 32
+ }
+ ],
+ 'duration': 2700
+ }
+ }
+ });
+ });
+ });
});
});

MODIFY
test/api/client/SmartChargingApi.ts
test/api/client/SmartChargingApi.ts
-import Constants from './utils/Constants';
import CrudApi from './utils/CrudApi';
+import TestConstants from './utils/TestConstants';
export default class SmartChargingApi extends CrudApi {
public constructor(authenticatedApi) {
super(authenticatedApi);
}
- public async testConnection(params?, paging = Constants.DEFAULT_PAGING, ordering = Constants.DEFAULT_ORDERING) {
+ public async testConnection(params?, paging = TestConstants.DEFAULT_PAGING, ordering = TestConstants.DEFAULT_ORDERING) {
return await super.readAll(params, paging, ordering, '/client/api/CheckSmartChargingConnection');
}
- public async getChargingProfiles(params?, paging = Constants.DEFAULT_PAGING, ordering = Constants.DEFAULT_ORDERING) {
+ public async getChargingProfiles(params?, paging = TestConstants.DEFAULT_PAGING, ordering = TestConstants.DEFAULT_ORDERING) {
return await super.readAll(params, paging, ordering, '/client/api/ChargingProfiles');
}
- public async triggerSmartCharging(params?, paging = Constants.DEFAULT_PAGING, ordering = Constants.DEFAULT_ORDERING) {
+ public async triggerSmartCharging(params?, paging = TestConstants.DEFAULT_PAGING, ordering = TestConstants.DEFAULT_ORDERING) {
return await super.readAll(params, paging, ordering, '/client/api/TriggerSmartCharging');
}
}

MODIFY
test/api/context/ContextBuilder.ts
test/api/context/ContextBuilder.ts
@@ -297,17 +297,19 @@ export default class ContextBuilder {
// Create Charging Station for site area
for (const chargingStationDef of relevantCS) {
if (siteAreaModel.smartCharging && siteAreaModel.numberOfPhases === 1) {
- const chargingStationTemplate = Factory.chargingStation.build();
+ const chargingStationTemplate = Factory.chargingStation.buildChargingStationSinglePhased();
chargingStationTemplate.id = chargingStationDef.baseName + '-' + siteAreaModel.name;
console.log(`${buildTenant.id} (${buildTenant.name}) - Charging Station '${chargingStationTemplate.id}'`);
- const newChargingStationContext = await newTenantContext.createChargingStation(chargingStationDef.ocppVersion, chargingStationTemplate, null, siteAreaModel);
+ const newChargingStationContext = await newTenantContext.createSinglePhasedChargingStation(chargingStationDef.ocppVersion, chargingStationTemplate, null, siteAreaModel);
await siteAreaContext.addChargingStation(newChargingStationContext.getChargingStation());
+
} else if (siteAreaModel.smartCharging && siteAreaModel.numberOfPhases === 3) {
const chargingStationTemplate = Factory.chargingStation.build();
chargingStationTemplate.id = chargingStationDef.baseName + '-' + siteAreaModel.name;
console.log(`${buildTenant.id} (${buildTenant.name}) - Charging Station '${chargingStationTemplate.id}'`);
const newChargingStationContext = await newTenantContext.createChargingStation(chargingStationDef.ocppVersion, chargingStationTemplate, null, siteAreaModel);
await siteAreaContext.addChargingStation(newChargingStationContext.getChargingStation());
+
} else {
const chargingStationTemplate = Factory.chargingStation.build();
chargingStationTemplate.id = chargingStationDef.baseName + '-' + siteAreaModel.name;

MODIFY
test/api/context/TenantContext.ts
test/api/context/TenantContext.ts
@@ -258,7 +258,8 @@ export default class TenantContext {
timestamp: (connectorsDef && connectorsDef[i].timestamp ? connectorsDef[i].timestamp : new Date().toISOString()),
type: (connectorsDef && connectorsDef[i].type ? connectorsDef[i].type : 'U'),
power: (connectorsDef && connectorsDef[i].power ? connectorsDef[i].power : 22170),
- numberOfConnectedPhase: (connectorsDef && connectorsDef[i].numberOfConnectedPhase ? connectorsDef[i].numberOfConnectedPhase : 3)
+ numberOfConnectedPhase: (connectorsDef && connectorsDef[i].numberOfConnectedPhase ? connectorsDef[i].numberOfConnectedPhase : 3),
+ amperage: (connectorsDef && connectorsDef[i].amperage ? connectorsDef[i].amperage : 96),
};
}
for (const connector of createdChargingStation.connectors) {
@@ -309,6 +310,79 @@ export default class TenantContext {
return createdCS;
}
+ async createSinglePhasedChargingStation(ocppVersion, chargingStation = Factory.chargingStation.buildChargingStationSinglePhased({
+ id: faker.random.alphaNumeric(12)
+ }), connectorsDef = null, siteArea = null) {
+ const ocppService = await this.getOCPPService(ocppVersion);
+ const response = await ocppService.executeBootNotification(chargingStation.id, chargingStation);
+ // Check
+ expect(response).to.not.be.null;
+ expect(response.status).to.eql('Accepted');
+ expect(response).to.have.property('currentTime');
+ let createdChargingStation = await this.getAdminCentralServerService().getEntityById(
+ this.getAdminCentralServerService().chargingStationApi, chargingStation);
+ expect(createdChargingStation.maximumPower).to.eql(14720);
+ expect(createdChargingStation.powerLimitUnit).to.eql('A');
+ for (let i = 0; i < (connectorsDef ? connectorsDef.length : 2); i++) {
+ createdChargingStation.connectors[i] = {
+ connectorId: i + 1,
+ status: (connectorsDef && connectorsDef[i].status ? connectorsDef[i].status : 'Available'),
+ errorCode: (connectorsDef && connectorsDef[i].errorCode ? connectorsDef[i].errorCode : 'NoError'),
+ timestamp: (connectorsDef && connectorsDef[i].timestamp ? connectorsDef[i].timestamp : new Date().toISOString()),
+ type: (connectorsDef && connectorsDef[i].type ? connectorsDef[i].type : 'U'),
+ power: (connectorsDef && connectorsDef[i].power ? connectorsDef[i].power : 7360),
+ numberOfConnectedPhase: (connectorsDef && connectorsDef[i].numberOfConnectedPhase ? connectorsDef[i].numberOfConnectedPhase : 1),
+ amperage: (connectorsDef && connectorsDef[i].amperage ? connectorsDef[i].amperage : 32),
+ };
+ }
+ for (const connector of createdChargingStation.connectors) {
+ await ocppService.executeStatusNotification(createdChargingStation.id, connector);
+ }
+ createdChargingStation = await this.getAdminCentralServerService().getEntityById(
+ this.getAdminCentralServerService().chargingStationApi, chargingStation);
+ // Charging Station
+ expect(createdChargingStation.voltage).to.eql(230);
+ // Connectors
+ expect(createdChargingStation.connectors.length).to.eql(2,
+ `Number of connector of charging station ${createdChargingStation.id} must be 2`);
+ expect(createdChargingStation.connectors[0].power).to.eql(7360,
+ `Connector ID 1 of charging station ${createdChargingStation.id} must have 7360 W`);
+ expect(createdChargingStation.connectors[0].amperage).to.eql(32,
+ `Connector ID 1 of charging station ${createdChargingStation.id} must have 32 A`);
+ expect(createdChargingStation.connectors[0].type).to.eql('T2',
+ `Connector ID 1 of charging station ${createdChargingStation.id} must have Type 2 connector`);
+ expect(createdChargingStation.connectors[1].power).to.eql(7360,
+ `Connector ID 2 of charging station ${createdChargingStation.id} must have 7360 W`);
+ expect(createdChargingStation.connectors[1].amperage).to.eql(32,
+ `Connector ID 2 of charging station ${createdChargingStation.id} must have 32 A`);
+ expect(createdChargingStation.connectors[1].type).to.eql('T2',
+ `Connector ID 2 of charging station ${createdChargingStation.id} must have Type 2 connector`);
+ // Charge Points
+ expect(createdChargingStation.chargePoints.length).to.eql(1,
+ `Number of charge point of charging station ${createdChargingStation.id} must be 1`);
+ expect(createdChargingStation.chargePoints[0].currentType).to.eql('AC');
+ expect(createdChargingStation.chargePoints[0].numberOfConnectedPhase).to.eql(1,
+ `Charge Point ID 1 of charging station ${createdChargingStation.id} must have 1 phases`);
+ expect(createdChargingStation.chargePoints[0].cannotChargeInParallel).to.eql(false,
+ `Charge Point ID 1 of charging station ${createdChargingStation.id} cannot charge in parallel`);
+ expect(createdChargingStation.chargePoints[0].sharePowerToAllConnectors).to.eql(false,
+ `Charge Point ID 1 of charging station ${createdChargingStation.id} cannot share power`);
+ expect(createdChargingStation.chargePoints[0].excludeFromPowerLimitation).to.eql(false,
+ `Charge Point ID 1 of charging station ${createdChargingStation.id} cannot be excluded from power limitation`);
+ expect(createdChargingStation.chargePoints[0].ocppParamForPowerLimitation).to.eql('maxintensitysocket',
+ `Charge Point ID 1 of charging station ${createdChargingStation.id} must have OCPP param 'maxintensitysocket'`);
+ // Assign to Site Area
+ if (siteArea) {
+ createdChargingStation.siteAreaID = siteArea.id;
+ await this.getAdminCentralServerService().updateEntity(
+ this.getAdminCentralServerService().chargingStationApi, createdChargingStation);
+ }
+ const createdCS = new ChargingStationContext(createdChargingStation, this);
+ await createdCS.initialize();
+ this.context.createdChargingStations.push(createdCS);
+ return createdCS;
+ }
+
async createRegistrationToken(siteAreaID: string = null): Promise<string> {
const registrationTokenResponse = await this.centralAdminServerService.registrationApi.create({
description: `Test Token for site area ${siteAreaID}`,

MODIFY
test/factories/ChargingStationFactory.ts
test/factories/ChargingStationFactory.ts
import { Factory } from 'rosie';
import faker from 'faker';
-export default Factory.define('chargingStation')
+const chargingStationThreePhased = Factory.define('chargingStation')
.attr('chargePointVendor', () => 'Schneider Electric')
.attr('chargePointModel', () => 'MONOBLOCK')
.attr('chargePointSerialNumber', () => faker.random.alphaNumeric(25))
.attr('chargeBoxSerialNumber', () => 'EV.2S22P44' + faker.random.alphaNumeric(15).toUpperCase())
.attr('firmwareVersion', () => faker.random.alphaNumeric(25));
+
+const chargingStationSinglePhased = Factory.define('chargingStation')
+ .attr('chargePointVendor', () => 'Schneider Electric')
+ .attr('chargePointModel', () => 'MONOBLOCK')
+ .attr('chargePointSerialNumber', () => faker.random.alphaNumeric(25))
+ .attr('chargeBoxSerialNumber', () => 'EV.2S7P44' + faker.random.alphaNumeric(15).toUpperCase())
+ .attr('firmwareVersion', () => faker.random.alphaNumeric(25));
+
+export default class UserFactory {
+ static build(attributes?, options?) {
+ return chargingStationThreePhased.build(attributes, options);
+ }
+
+ static buildChargingStationSinglePhased(attributes?, options?) {
+ return chargingStationSinglePhased.build(attributes, options);
+ }
+}

Single phased site area + test