├── gen ├── temp │ ├── .gitkeep │ └── default.set ├── models │ └── .gitkeep ├── performance │ └── .gitkeep ├── data │ └── .gitignore └── .gitignore ├── frontend ├── src │ ├── assets │ │ ├── .gitkeep │ │ └── .gitignore │ ├── app │ │ ├── request-json-component │ │ │ ├── request-json-component.component.css │ │ │ ├── request-json-component.component.html │ │ │ └── request-json-component.component.ts │ │ ├── result-json-component │ │ │ ├── result-json-component.component.css │ │ │ ├── result-json-component.component.html │ │ │ └── result-json-component.component.ts │ │ ├── fuse-tree-circuit-diagram │ │ │ ├── fuse-tree-circuit-diagram.component.css │ │ │ ├── fuse-tree-circuit-diagram-dialog.html │ │ │ └── fuse-tree-circuit-diagram-dialog.ts │ │ ├── app.component.css │ │ ├── app-routing.module.ts │ │ ├── fuse-tree-component │ │ │ └── fuse-tree-component.component.css │ │ └── app.component.spec.ts │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── main.ts │ ├── index.html │ ├── test.ts │ └── styles.css ├── e2e │ ├── tsconfig.json │ ├── src │ │ ├── app.po.ts │ │ └── app.e2e-spec.ts │ └── protractor.conf.js ├── .editorconfig ├── tsconfig.spec.json ├── tsconfig.app.json ├── browserslist ├── tsconfig.json ├── .gitignore ├── karma.conf.js ├── README.md ├── package.json └── tslint.json ├── src ├── main │ ├── resources │ │ ├── application.properties │ │ ├── configuration.properties │ │ └── log4j2.properties │ └── java │ │ ├── META-INF │ │ └── MANIFEST.MF │ │ └── com │ │ └── sap │ │ └── charging │ │ ├── model │ │ ├── EnergyStorage.java │ │ ├── battery │ │ │ └── ChargeAlgorithm.java │ │ ├── FuseTreeChargingStationCounter.java │ │ ├── ChargingStationFactory.java │ │ └── FuseTreeNode.java │ │ ├── util │ │ ├── Callback.java │ │ ├── r │ │ │ └── MainR.java │ │ ├── performanceMeasurement │ │ │ ├── PerformanceMeasurementTemplate.java │ │ │ ├── paperINDIN2018 │ │ │ │ ├── PerformanceMeasurementINDIN2018.java │ │ │ │ ├── PerformanceMeasurementINDIN2018Template.java │ │ │ │ └── AppComparisonPerformanceINDIN2018.java │ │ │ ├── random │ │ │ │ ├── PerformanceMeasurementRandom.java │ │ │ │ └── PerformanceMeasurementRandomTemplate.java │ │ │ ├── MeasurementExecutor.java │ │ │ ├── PerformanceMeasurement.java │ │ │ ├── paperOR2018 │ │ │ │ └── PerformanceMeasurementPaperOR2018.java │ │ │ ├── forecasting │ │ │ │ └── PerformanceMeasurementForecasting.java │ │ │ ├── random2018_06 │ │ │ │ └── PerformanceMeasurementRandom2018_06.java │ │ │ ├── DB.java │ │ │ ├── forecasting2018_11 │ │ │ │ └── PerformanceMeasurementForecasting2018_11.java │ │ │ ├── paperJournal2018Time │ │ │ │ └── PerformanceMeasurementPaperJournal2018Time.java │ │ │ └── paperJournal2018 │ │ │ │ └── PerformanceMeasurementPaperJournal2018.java │ │ ├── cli │ │ │ ├── CLArgumentMissing.java │ │ │ ├── CLArgumentInvalidName.java │ │ │ ├── CLArgumentInvalidFormat.java │ │ │ ├── CLArgumentInvalidValue.java │ │ │ ├── CLArgumentInvalidValueType.java │ │ │ └── CLArgument.java │ │ ├── sqlite │ │ │ ├── SQLiteAttributeKey.java │ │ │ ├── SQLiteAttributeIgnore.java │ │ │ ├── SQLiteAttributeNotNull.java │ │ │ └── SQLiteAttribute.java │ │ ├── random │ │ │ ├── Distribution.java │ │ │ ├── ConstantDistribution.java │ │ │ ├── UniformDistribution.java │ │ │ ├── NormalDistribution.java │ │ │ └── DiscreteDistribution.java │ │ ├── SortableElement.java │ │ ├── Util.java │ │ ├── JSONSerializable.java │ │ ├── configuration │ │ │ └── Options.java │ │ └── Loggable.java │ │ ├── realTime │ │ ├── util │ │ │ ├── TimeslotSortingCriteria.java │ │ │ ├── PlannedCapacityKey.java │ │ │ └── TimeslotSorter.java │ │ ├── model │ │ │ ├── forecasting │ │ │ │ ├── Forecast.java │ │ │ │ ├── soc │ │ │ │ │ ├── CarSoCForecastMedian.java │ │ │ │ │ └── CarSoCForecastLinearModel.java │ │ │ │ └── departure │ │ │ │ │ ├── CarDepartureOracle.java │ │ │ │ │ ├── CarDepartureForecastMedianTimestamp.java │ │ │ │ │ ├── CarDepartureForecast.java │ │ │ │ │ ├── CarDepartureForecastMedianTimeslot.java │ │ │ │ │ ├── CarDepartureForecastLinearModel.java │ │ │ │ │ ├── CarDepartureForecastXGBoost_CarSample.java │ │ │ │ │ ├── CarDepartureForecastLinearModelAll.java │ │ │ │ │ └── CarDepartureForecastNN_CarSample.java │ │ │ └── CarAssignmentStore.java │ │ └── exception │ │ │ ├── CarNotAssignedException.java │ │ │ ├── CarAlreadyAssignedPowerException.java │ │ │ ├── ChargingStationNotOccupiedException.java │ │ │ ├── ChargingStationAlreadyOccupiedException.java │ │ │ ├── CarNotAvailableException.java │ │ │ └── CarAlreadyAssignedException.java │ │ ├── opt │ │ ├── util │ │ │ ├── TimeableMethodType.java │ │ │ ├── MethodTimer.java │ │ │ └── MethodTimerState.java │ │ ├── lp │ │ │ ├── Objective.java │ │ │ ├── LPStringBuilder.java │ │ │ ├── util │ │ │ │ ├── Solver.java │ │ │ │ └── SolverGurobi.java │ │ │ ├── Indexable.java │ │ │ └── Equation.java │ │ ├── heuristics │ │ │ ├── InstanceHeuristic.java │ │ │ └── InstanceHeuristicLP.java │ │ ├── InstanceEmpty.java │ │ └── CONSTANTS.java │ │ ├── sim │ │ ├── util │ │ │ ├── SimulationListener.java │ │ │ ├── SimulationListenerCSV.java │ │ │ ├── SimulationListenerJSON.java │ │ │ └── SimulationListenerOutputData.java │ │ ├── eval │ │ │ └── exception │ │ │ │ ├── CarAssignmentException.java │ │ │ │ ├── PowerAssignmentException.java │ │ │ │ ├── SimulationInvalidStateException.java │ │ │ │ └── ValidationException.java │ │ └── event │ │ │ ├── EventReoptimize.java │ │ │ ├── EventCarArrival.java │ │ │ ├── EventCarDeparture.java │ │ │ ├── EventEnergyPriceChange.java │ │ │ ├── EventType.java │ │ │ ├── EventCarFinished.java │ │ │ └── Event.java │ │ ├── server │ │ ├── api │ │ │ └── v1 │ │ │ │ ├── ErrorResponse.java │ │ │ │ ├── exception │ │ │ │ ├── UnknownCarException.java │ │ │ │ ├── UnknownEventTypeException.java │ │ │ │ ├── ParameterCombinationException.java │ │ │ │ ├── UnknownChargingStationException.java │ │ │ │ ├── MissingParameterException.java │ │ │ │ └── ApiError.java │ │ │ │ ├── OptimizeChargingProfilesResponse.java │ │ │ │ └── OptimizeChargingProfilesRequest.java │ │ ├── EmobilitySmartChargingApplication.java │ │ ├── security │ │ │ ├── CustomFilter.java │ │ │ ├── CustomBasicAuthenticationEntryPoint.java │ │ │ └── CustomWebSecurityConfigurerAdapter.java │ │ └── config │ │ │ ├── CustomWebMvcConfigurerAdapter.java │ │ │ └── Swagger2Config.java │ │ ├── ocpp │ │ ├── protocol │ │ │ ├── ChargingProfileKindType.java │ │ │ ├── ChargingRateUnitType.java │ │ │ ├── ChargingProfilePurposeType.java │ │ │ └── ChargingSchedulePeriod.java │ │ ├── ChargingProfileAssignment.java │ │ ├── OCPPData.java │ │ └── JSONConverter.java │ │ ├── AppOCPP.java │ │ ├── AppNonlinearBatteryRuntime.java │ │ ├── dataGeneration │ │ └── carDistributions │ │ │ ├── CarConsumptionDistributionPHEV.java │ │ │ └── CarConsumptionDistributionBEV.java │ │ ├── AppProblemInstanceExportJSON.java │ │ └── AppExportCars.java └── test │ ├── java │ └── com │ │ └── sap │ │ └── charging │ │ ├── realTime │ │ ├── model │ │ │ ├── AssignmentTest.java │ │ │ └── forecasting │ │ │ │ ├── CarDepartureForecastOracleTest.java │ │ │ │ ├── CarDepartureForecastNN_CarSampleTest.java │ │ │ │ ├── CarDepartureForecastXGBoost_CarSampleTest.java │ │ │ │ └── CarDepartureForecastXGBoostTest.java │ │ ├── StateTest.java │ │ └── util │ │ │ └── TimeslotSorterTest.java │ │ ├── opt │ │ ├── lp │ │ │ ├── util │ │ │ │ └── SolverCallScipTest.java │ │ │ ├── InstanceLPPeakShavingTest.java │ │ │ └── EquationTest.java │ │ └── heuristic │ │ │ ├── InstanceHeuristicAbsSoCTest.java │ │ │ └── InstanceHeuristicRelSoCTest.java │ │ ├── dataGeneration │ │ ├── DataGeneratorDeterministicTest.java │ │ └── DataRandomizerTest.java │ │ ├── sim │ │ └── common │ │ │ ├── StrategyMock.java │ │ │ └── SimulationUnitTest.java │ │ ├── model │ │ ├── EnergyUtilTest.java │ │ └── CarFactoryTest.java │ │ ├── server │ │ └── api │ │ │ └── v1 │ │ │ └── store │ │ │ └── OptimizerSettingsTest.java │ │ └── util │ │ └── TimeUtilTest.java │ └── resources │ ├── log4j2-test.properties │ ├── configuration.properties │ └── testCasesJSON │ ├── StateStoreTest_ThreePhaseCar_SinglePhaseStation.json │ ├── StateStoreTest_CarDepartureTime_IsNotSet.json │ ├── StateStoreTest_SinglePhaseCar_StationWithPhaseRotation_SinglePhaseFuse.json │ ├── StateStoreTest_CarDepartureTime_IsSet.json │ └── StateStoreTest_ThreePhaseCar_SinglePhaseStationWithPhaseRotation.json ├── public └── .gitignore ├── docs └── images │ ├── smart_charging.png │ └── experimental_results.png ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── manifest.yml ├── .dockerignore ├── manifest_dev.yml ├── .reuse └── dep5 ├── Dockerfile └── postman └── emobility-smart-charging.postman_collection.json /gen/temp/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gen/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gen/performance/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gen/data/.gitignore: -------------------------------------------------------------------------------- 1 | *.csv 2 | *.json -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/assets/.gitignore: -------------------------------------------------------------------------------- 1 | server_types.d.ts -------------------------------------------------------------------------------- /gen/.gitignore: -------------------------------------------------------------------------------- 1 | performanceMeasurements.db 2 | -------------------------------------------------------------------------------- /frontend/src/app/request-json-component/request-json-component.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/app/result-json-component/result-json-component.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/app/fuse-tree-circuit-diagram/fuse-tree-circuit-diagram.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Angular 8 from frontend/ directory 2 | playground/ -------------------------------------------------------------------------------- /src/main/java/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /frontend/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/emobility-smart-charging/HEAD/frontend/src/favicon.ico -------------------------------------------------------------------------------- /docs/images/smart_charging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/emobility-smart-charging/HEAD/docs/images/smart_charging.png -------------------------------------------------------------------------------- /docs/images/experimental_results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP/emobility-smart-charging/HEAD/docs/images/experimental_results.png -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/model/EnergyStorage.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.model; 2 | 3 | public class EnergyStorage { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/Callback.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util; 2 | 3 | public interface Callback { 4 | 5 | public void callback(T item); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/model/battery/ChargeAlgorithm.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.model.battery; 2 | 3 | public enum ChargeAlgorithm { 4 | 5 | CCCV, 6 | CPCV 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/util/TimeslotSortingCriteria.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.util; 2 | 3 | public enum TimeslotSortingCriteria { 4 | INDEX, 5 | PRICE, 6 | PEAK_DEMAND 7 | } -------------------------------------------------------------------------------- /gen/temp/default.set: -------------------------------------------------------------------------------- 1 | # Quiet = FALSE 2 | LogSolvingStatusFilePath = "./logs/" 3 | LogNodesTransferFilePath = "./logs/" 4 | CheckpointFilePath = "./logs/" 5 | SolutionFilePath = "./logs/" 6 | RacingStatBranching = FALSE -------------------------------------------------------------------------------- /frontend/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .inputClockComponent { 4 | width: 100px; 5 | } 6 | 7 | 8 | .checkboxChartSetting { 9 | float: left; 10 | clear: both; 11 | margin-left: 20px; 12 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/opt/util/TimeableMethodType.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.util; 2 | 3 | @Deprecated 4 | public enum TimeableMethodType { 5 | 6 | PROBLEM_CONSTRUCTION, 7 | SOLUTION 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/r/MainR.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.r; 2 | 3 | public class MainR { 4 | 5 | public static void main(String[] args) { 6 | System.out.println(StartRserve.checkLocalRserve(6311)); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/realTime/model/AssignmentTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model; 2 | 3 | import com.sap.charging.sim.common.SimulationUnitTest; 4 | 5 | public class AssignmentTest extends SimulationUnitTest { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.2/apache-maven-3.6.2-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar 3 | -------------------------------------------------------------------------------- /manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: emobility-smart-charging 4 | health-check-type: process 5 | health-check-timeout: 300 6 | memory: 2G 7 | disk_quota: 4G 8 | instances: 1 9 | env: 10 | 11 | services: 12 | - application-logs -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | 3 | 4 | gen/ 5 | postman/ 6 | 7 | # Should be built using npm 8 | frontend/node_modules 9 | 10 | # This should be built using angular 11 | public/playground/ 12 | 13 | 14 | 15 | # This should be built using mvn 16 | target/ 17 | 18 | Dockerfile 19 | -------------------------------------------------------------------------------- /frontend/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/app/request-json-component/request-json-component.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 7 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/util/SimulationListener.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.util; 2 | 3 | import com.sap.charging.realTime.State; 4 | 5 | public interface SimulationListener { 6 | 7 | void callbackAfterUpdate(State state); 8 | 9 | void callbackBeforeUpdate(State state); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /frontend/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | 5 | const routes: Routes = []; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forRoot(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class AppRoutingModule { } 12 | -------------------------------------------------------------------------------- /frontend/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/PerformanceMeasurementTemplate.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement; 2 | 3 | public abstract class PerformanceMeasurementTemplate { 4 | 5 | public abstract PerformanceMeasurementType cloneWithMethod(String method); 6 | 7 | 8 | } 9 | -------------------------------------------------------------------------------- /frontend/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /frontend/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ], 14 | "exclude": [ 15 | "src/test.ts", 16 | "src/**/*.spec.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/eval/exception/CarAssignmentException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.eval.exception; 2 | 3 | public class CarAssignmentException extends ValidationException { 4 | 5 | private static final long serialVersionUID = 6823323252568662411L; 6 | 7 | public CarAssignmentException(String message) { 8 | super(message); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/eval/exception/PowerAssignmentException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.eval.exception; 2 | 3 | public class PowerAssignmentException extends ValidationException { 4 | 5 | private static final long serialVersionUID = -53016212612873722L; 6 | 7 | public PowerAssignmentException(String message) { 8 | super(message); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/cli/CLArgumentMissing.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.cli; 2 | 3 | public class CLArgumentMissing extends Exception { 4 | 5 | private static final long serialVersionUID = -1124021975788443822L; 6 | 7 | public CLArgumentMissing(String argName) { 8 | System.out.println("Missing CL argument: " + argName); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/model/forecasting/Forecast.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting; 2 | 3 | import com.sap.charging.sim.Simulation; 4 | import com.sap.charging.util.Loggable; 5 | 6 | public abstract class Forecast implements Loggable { 7 | 8 | @Override 9 | public int getVerbosity() { 10 | return Simulation.verbosity; 11 | } 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/eval/exception/SimulationInvalidStateException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.eval.exception; 2 | 3 | public class SimulationInvalidStateException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 8121163947231813411L; 6 | 7 | public SimulationInvalidStateException(String message) { 8 | super(message); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/api/v1/ErrorResponse.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.api.v1; 2 | 3 | public class ErrorResponse { 4 | 5 | public final String exceptionName; 6 | public final String exceptionMessage; 7 | 8 | public ErrorResponse(Exception e) { 9 | this.exceptionName = e.getClass().getSimpleName(); 10 | this.exceptionMessage = e.getMessage(); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/api/v1/exception/UnknownCarException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.api.v1.exception; 2 | 3 | public class UnknownCarException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 7348969993780779108L; 6 | 7 | public UnknownCarException(int carID) { 8 | super("Car id=" + carID + " not found in state!"); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/api/v1/exception/UnknownEventTypeException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.api.v1.exception; 2 | 3 | public class UnknownEventTypeException extends IllegalArgumentException { 4 | 5 | private static final long serialVersionUID = 2853451587582770814L; 6 | 7 | public UnknownEventTypeException(String string) { 8 | super(string); 9 | } 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /manifest_dev.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: emobility-smart-charging 4 | health-check-type: process 5 | health-check-timeout: 300 6 | memory: 2G 7 | disk_quota: 4G 8 | instances: 1 9 | path: target/emobility-smart-charging-0.0.1-SNAPSHOT.jar 10 | buildpacks: 11 | - https://github.com/cloudfoundry/java-buildpack.git 12 | env: 13 | 14 | services: 15 | - application-logs -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/opt/lp/Objective.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.lp; 2 | 3 | public class Objective { 4 | 5 | private double weight; 6 | 7 | public Objective(double weight) { 8 | this.weight = weight; 9 | } 10 | 11 | public double getWeight() { 12 | return weight; 13 | } 14 | 15 | public void setWeight(double weight) { 16 | this.weight = weight; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/api/v1/exception/ParameterCombinationException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.api.v1.exception; 2 | 3 | public class ParameterCombinationException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 3066847433281552986L; 6 | 7 | public ParameterCombinationException(String message) { 8 | super(message); 9 | } 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/sqlite/SQLiteAttributeKey.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.sqlite; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.FIELD) 10 | public @interface SQLiteAttributeKey { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/cli/CLArgumentInvalidName.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.cli; 2 | 3 | public class CLArgumentInvalidName extends Exception { 4 | 5 | private static final long serialVersionUID = -5936679843484192593L; 6 | 7 | public CLArgumentInvalidName(String argName) { 8 | System.out.println("Argument " + argName + " is an invalid name. See --help for a list of parameters."); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/sqlite/SQLiteAttributeIgnore.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.sqlite; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.FIELD) 10 | public @interface SQLiteAttributeIgnore { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/sqlite/SQLiteAttributeNotNull.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.sqlite; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.FIELD) 10 | public @interface SQLiteAttributeNotNull { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/eval/exception/ValidationException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.eval.exception; 2 | 3 | public abstract class ValidationException extends Exception { 4 | 5 | private static final long serialVersionUID = -5013179691348196812L; 6 | 7 | public ValidationException() { 8 | super(); 9 | } 10 | 11 | public ValidationException(String message) { 12 | super(message); 13 | } 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/cli/CLArgumentInvalidFormat.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.cli; 2 | 3 | public class CLArgumentInvalidFormat extends Exception { 4 | 5 | private static final long serialVersionUID = 1798849042249121220L; 6 | 7 | public CLArgumentInvalidFormat(String rawArg) { 8 | System.out.println("Invalid argument format for arg=" + rawArg + ": excepting arg1=value1 arg2=value2"); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | import 'hammerjs'; 2 | import { enableProdMode } from '@angular/core'; 3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 4 | 5 | import { AppModule } from './app/app.module'; 6 | import { environment } from './environments/environment'; 7 | 8 | if (environment.production) { 9 | enableProdMode(); 10 | } 11 | 12 | platformBrowserDynamic().bootstrapModule(AppModule) 13 | .catch(err => console.error(err)); 14 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/model/forecasting/soc/CarSoCForecastMedian.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting.soc; 2 | 3 | import com.sap.charging.model.Car; 4 | 5 | public class CarSoCForecastMedian extends CarSoCForecast { 6 | 7 | @Override 8 | public double getExpectedSoC(Car carPreviousDay, Car car, int lastDay, int currentDay) { 9 | // TODO Auto-generated method stub 10 | return 0.3253; 11 | } 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/cli/CLArgumentInvalidValue.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.cli; 2 | 3 | public class CLArgumentInvalidValue extends Exception { 4 | 5 | private static final long serialVersionUID = 3421423031197856146L; 6 | 7 | public CLArgumentInvalidValue(String argName, Object value) { 8 | String message = "Argument " + argName + " has invalid value: " + value; 9 | System.out.println(message); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/exception/CarNotAssignedException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.exception; 2 | 3 | import com.sap.charging.model.Car; 4 | 5 | public class CarNotAssignedException extends RuntimeException { 6 | 7 | private static final long serialVersionUID = -7255812508189813106L; 8 | 9 | public CarNotAssignedException(Car car) { 10 | super("Car n=" + car.getId() + " is not assigned."); 11 | } 12 | 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/EmobilitySmartChargingApplication.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class EmobilitySmartChargingApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(EmobilitySmartChargingApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /frontend/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/api/v1/exception/UnknownChargingStationException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.api.v1.exception; 2 | 3 | public class UnknownChargingStationException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 1327171431732040517L; 6 | 7 | public UnknownChargingStationException(int chargingStationID) { 8 | super("ChargingStation id=" + chargingStationID + " not found in state!"); 9 | } 10 | 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/exception/CarAlreadyAssignedPowerException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.exception; 2 | 3 | import com.sap.charging.model.Car; 4 | 5 | public class CarAlreadyAssignedPowerException extends RuntimeException { 6 | 7 | private static final long serialVersionUID = -8896866028319010991L; 8 | 9 | public CarAlreadyAssignedPowerException(Car car) { 10 | super("Car n=" + car.getId() + " already has a power assignment."); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/cli/CLArgumentInvalidValueType.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.cli; 2 | 3 | public class CLArgumentInvalidValueType extends Exception { 4 | 5 | private static final long serialVersionUID = 5571676315973306837L; 6 | 7 | @SuppressWarnings("rawtypes") 8 | public CLArgumentInvalidValueType(CLArgument arg, Object value) { 9 | System.out.println("Invalid value type for " + arg.name + ": Passed in " + value + " but expecting type " + arg.type); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/random/Distribution.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.random; 2 | 3 | import java.util.Random; 4 | 5 | public abstract class Distribution { 6 | 7 | private final Random random; 8 | 9 | public Distribution(Random random) { 10 | this.random = random; 11 | } 12 | 13 | public Random getRandom() { 14 | return random; 15 | } 16 | 17 | public abstract Distribution clone(Random random); 18 | 19 | public abstract double getNextDouble(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/opt/lp/util/SolverCallScipTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.lp.util; 2 | 3 | import java.io.IOException; 4 | 5 | public class SolverCallScipTest { 6 | 7 | //@Test 8 | public void testCallScip() throws IOException, InterruptedException { 9 | // Check if scip is installed and reachable from the command line 10 | /*ProcessBuilder b = new ProcessBuilder("scip", "-v"); 11 | Process pr = b.start(); 12 | pr.waitFor();*/ 13 | } 14 | 15 | 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/event/EventReoptimize.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.event; 2 | 3 | import java.time.LocalTime; 4 | 5 | import org.json.simple.JSONObject; 6 | 7 | public class EventReoptimize extends Event { 8 | 9 | public EventReoptimize(LocalTime timestamp) { 10 | super(timestamp); 11 | } 12 | 13 | public EventReoptimize(JSONObject jsonObject) { 14 | super(jsonObject); 15 | } 16 | 17 | @Override 18 | public void addChildJSONAttributes(JSONObject object) { 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/exception/ChargingStationNotOccupiedException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.exception; 2 | 3 | import com.sap.charging.model.ChargingStation; 4 | 5 | public class ChargingStationNotOccupiedException extends RuntimeException { 6 | 7 | private static final long serialVersionUID = -7705511373441646605L; 8 | 9 | public ChargingStationNotOccupiedException(ChargingStation chargingStation) { 10 | super("ChargingStation i=" + chargingStation.getId() + " is not occupied."); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/exception/ChargingStationAlreadyOccupiedException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.exception; 2 | 3 | import com.sap.charging.model.ChargingStation; 4 | 5 | public class ChargingStationAlreadyOccupiedException extends RuntimeException { 6 | 7 | private static final long serialVersionUID = 4856888426498557201L; 8 | 9 | public ChargingStationAlreadyOccupiedException(ChargingStation chargingStation) { 10 | super("ChargingStation i=" + chargingStation.getId() + " is already occupied."); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/test/resources/log4j2-test.properties: -------------------------------------------------------------------------------- 1 | status = error 2 | name = PropertiesConfig 3 | 4 | filters = threshold 5 | 6 | filter.threshold.type = ThresholdFilter 7 | filter.threshold.level = debug 8 | 9 | appenders = console 10 | 11 | appender.console.type = Console 12 | appender.console.name = STDOUT 13 | appender.console.layout.type = PatternLayout 14 | appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 15 | 16 | rootLogger.level = debug 17 | rootLogger.appenderRefs = stdout 18 | rootLogger.appenderRef.stdout.ref = STDOUT -------------------------------------------------------------------------------- /frontend/src/app/result-json-component/result-json-component.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |

REST API JSON result

4 | 5 |
6 | 7 | 8 |
9 |

An error has occured: {{restApiResponse.error.name}}

10 |

{{restApiResponse.error.message}}

11 |
12 | 13 | -------------------------------------------------------------------------------- /frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Playground 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/api/v1/OptimizeChargingProfilesResponse.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.api.v1; 2 | 3 | import java.util.List; 4 | 5 | import com.fasterxml.jackson.annotation.JsonCreator; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.sap.charging.model.Car; 8 | 9 | public class OptimizeChargingProfilesResponse { 10 | 11 | public final List cars; 12 | 13 | @JsonCreator 14 | public OptimizeChargingProfilesResponse(@JsonProperty("cars") List cars) { 15 | this.cars = cars; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/api/v1/exception/MissingParameterException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.api.v1.exception; 2 | 3 | public class MissingParameterException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 6533920595937545649L; 6 | 7 | public MissingParameterException(String parameterName) { 8 | super("Parameter '" + parameterName + "' is required."); 9 | } 10 | 11 | public MissingParameterException(String p1, String p2) { 12 | super("Parameter '" + p1 + "' or '" + p2 + "' is required."); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/app/fuse-tree-component/fuse-tree-component.component.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .divFuseTreeComponentContainer { 4 | padding: 0px 0px 0px 0px; /* top right bottom left */ 5 | margin-bottom: 5px; 6 | margin-top: 5px; 7 | } 8 | 9 | .blueBorderLeft { 10 | border-left: 1px dotted blue; 11 | } 12 | 13 | .icon-text { 14 | display: flex; 15 | align-items: center; 16 | } 17 | 18 | .buttonChangeInfrastructure { 19 | margin-left: 5px; 20 | } 21 | 22 | 23 | .divCarContainer { 24 | padding-left: 20px; 25 | margin-top: 5px; 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/exception/CarNotAvailableException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.exception; 2 | 3 | import com.sap.charging.model.Car; 4 | 5 | public class CarNotAvailableException extends RuntimeException { 6 | 7 | private static final long serialVersionUID = -1492868871745905270L; 8 | 9 | public CarNotAvailableException(Car car, int timestamp) { 10 | super("Car n=" + car.getId() + " is not available for state t=" + timestamp + ". Earliest timestamp for car is t=" + car.timestampArrival.toSecondOfDay() + ", timestamp departure is t=" + car.timestampDeparture.toSecondOfDay()); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/security/CustomFilter.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.security; 2 | 3 | import org.springframework.web.filter.GenericFilterBean; 4 | 5 | import javax.servlet.FilterChain; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.ServletRequest; 8 | import javax.servlet.ServletResponse; 9 | import java.io.IOException; 10 | 11 | public class CustomFilter extends GenericFilterBean { 12 | 13 | @Override 14 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 15 | chain.doFilter(request, response); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/model/CarAssignmentStore.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | public class CarAssignmentStore { 7 | 8 | public final int carID; 9 | public final int chargingStationID; 10 | 11 | @JsonCreator 12 | public CarAssignmentStore( 13 | @JsonProperty(value="carID", required=true) int carID, 14 | @JsonProperty(value="chargingStationID", required=true) int chargingStationID) { 15 | this.carID = carID; 16 | this.chargingStationID = chargingStationID; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/opt/util/MethodTimer.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.util; 2 | 3 | public class MethodTimer implements AutoCloseable { 4 | 5 | private MethodTimerState state; 6 | 7 | private long startTime; 8 | private long endTime; 9 | 10 | public MethodTimer(MethodTimerState state) { 11 | this.state = state; 12 | this.startTime = System.nanoTime(); 13 | } 14 | 15 | private double getDuration() { 16 | return (this.endTime - this.startTime) / (1000.0*1000.0*1000.0); 17 | } 18 | 19 | @Override 20 | public void close() { 21 | this.endTime = System.nanoTime(); 22 | state.addTime(getDuration()); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/ocpp/protocol/ChargingProfileKindType.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.ocpp.protocol; 2 | 3 | public enum ChargingProfileKindType { 4 | 5 | /** 6 | * Schedule periods are relative to a fixed point in time defined in the schedule. 7 | * 8 | * here always used. 9 | */ 10 | Absolute, 11 | 12 | /** 13 | * The schedule restarts periodically at the first schedule period. 14 | */ 15 | Recurring, 16 | 17 | /** 18 | * Schedule periods are relative to a situation-specific start point (such as the start of a Transaction) that is determined by the 19 | charge point. 20 | */ 21 | Relative 22 | 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/app/result-json-component/result-json-component.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | import { restApiResponseType } from 'src/global'; 3 | import { ChartDataSets, ChartOptions } from 'chart.js'; 4 | import { Color, Label } from 'ng2-charts'; 5 | 6 | @Component({ 7 | selector: 'app-result-json-component', 8 | templateUrl: './result-json-component.component.html', 9 | styleUrls: ['./result-json-component.component.css'] 10 | }) 11 | export class ResultJsonComponentComponent implements OnInit { 12 | 13 | @Input() restApiResponse: restApiResponseType; 14 | 15 | constructor() { } 16 | 17 | 18 | ngOnInit() { 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/random/ConstantDistribution.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.random; 2 | 3 | import java.util.Random; 4 | 5 | public class ConstantDistribution extends Distribution { 6 | 7 | private final double constant; 8 | 9 | public ConstantDistribution(Random random, double constant) { 10 | super(random); 11 | this.constant = constant; 12 | } 13 | 14 | public double getConstant() { 15 | return constant; 16 | } 17 | 18 | public double getNextDouble() { 19 | return constant; 20 | } 21 | 22 | @Override 23 | public Distribution clone(Random random) { 24 | return new ConstantDistribution(random, constant); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/model/forecasting/departure/CarDepartureOracle.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting.departure; 2 | 3 | import com.sap.charging.model.Car; 4 | import com.sap.charging.realTime.State; 5 | import com.sap.charging.util.TimeUtil; 6 | 7 | public class CarDepartureOracle extends CarDepartureForecast { 8 | 9 | @Override 10 | public int getExpectedDepartureTimeslot(State state, Car car) { 11 | return TimeUtil.getTimeslotFromSeconds(car.timestampDeparture.toSecondOfDay()) + 1; 12 | } 13 | 14 | @Override 15 | public int getExpectedDepartureTimeSeconds(State state, Car car) { 16 | return car.timestampDeparture.toSecondOfDay(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/opt/heuristics/InstanceHeuristic.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.heuristics; 2 | 3 | import java.util.List; 4 | 5 | import com.sap.charging.model.Car; 6 | import com.sap.charging.model.ChargingStation; 7 | import com.sap.charging.model.EnergyPriceHistory; 8 | import com.sap.charging.model.FuseTree; 9 | import com.sap.charging.opt.Instance; 10 | 11 | public abstract class InstanceHeuristic extends Instance { 12 | 13 | public InstanceHeuristic(List cars, List chargingStations, 14 | EnergyPriceHistory energyPriceHistory, FuseTree fuseTree) { 15 | super(cars, chargingStations, energyPriceHistory, fuseTree); 16 | } 17 | 18 | 19 | 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/resources/configuration.properties: -------------------------------------------------------------------------------- 1 | # This is the main Optimizer Configuration File # 2 | # =============================================== # 3 | # # 4 | # All values can (some: should) be overwritten by environment variables # 5 | # References to other values can be made with ${referenced.property} # 6 | # # 7 | ############################################################################### 8 | 9 | 10 | default.charging.efficiency = 0.85 11 | default.charging.planhours = 11 12 | default.charging.slotminutes = 15 -------------------------------------------------------------------------------- /src/test/resources/configuration.properties: -------------------------------------------------------------------------------- 1 | # This is the main Optimizer Configuration File # 2 | # =============================================== # 3 | # # 4 | # All values can (some: should) be overwritten by environment variables # 5 | # References to other values can be made with ${referenced.property} # 6 | # # 7 | ############################################################################### 8 | 9 | 10 | default.charging.efficiency = 0.85 11 | default.charging.planhours = 11 12 | default.charging.slotminutes = 15 -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/SortableElement.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util; 2 | 3 | public class SortableElement implements Comparable> { 4 | 5 | public final T index; 6 | public final double value; 7 | 8 | public SortableElement(T index, double value) { 9 | this.index = index; 10 | this.value = value; 11 | } 12 | 13 | @Override 14 | public int compareTo(SortableElement otherElement) { 15 | if (this.value < otherElement.value) return -1; 16 | if (this.value > otherElement.value) return 1; 17 | return 0; 18 | //return (int) (this.value - otherElement.value); 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return "index=" + index + "; value=" + value; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /frontend/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('playground app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/paperINDIN2018/PerformanceMeasurementINDIN2018.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement.paperINDIN2018; 2 | 3 | public class PerformanceMeasurementINDIN2018 extends PerformanceMeasurementINDIN2018Template { 4 | 5 | private final String method; 6 | 7 | 8 | public PerformanceMeasurementINDIN2018(int nCars, double proportionEVs, double gridConnection, String method) { 9 | super(nCars, proportionEVs, gridConnection); 10 | this.method = method; 11 | } 12 | 13 | public String getMethod() { 14 | return method; 15 | } 16 | 17 | 18 | @Override 19 | public String toString() { 20 | return super.toString() + " method=" + getMethod(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/ocpp/ChargingProfileAssignment.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.ocpp; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.sap.charging.ocpp.protocol.ChargingProfile; 6 | 7 | public class ChargingProfileAssignment { 8 | 9 | public final int chargingStationId; 10 | public final ChargingProfile chargingProfile; 11 | 12 | @JsonCreator 13 | public ChargingProfileAssignment( 14 | @JsonProperty("chargingStationId") int chargingStationId, 15 | @JsonProperty("chargingProfile") ChargingProfile chargingProfile 16 | ) { 17 | this.chargingStationId = chargingStationId; 18 | this.chargingProfile = chargingProfile; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/config/CustomWebMvcConfigurerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | 8 | @Configuration 9 | public class CustomWebMvcConfigurerAdapter implements WebMvcConfigurer { 10 | 11 | @Override 12 | public void addViewControllers(ViewControllerRegistry registry) { 13 | registry.addViewController("/playground").setViewName("redirect:/playground/"); 14 | registry.addViewController("/playground/").setViewName("redirect:/playground/index.html"); 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/exception/CarAlreadyAssignedException.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.exception; 2 | 3 | import com.sap.charging.model.Car; 4 | import com.sap.charging.model.ChargingStation; 5 | 6 | public class CarAlreadyAssignedException extends RuntimeException { 7 | 8 | private static final long serialVersionUID = -6489094963833396164L; 9 | 10 | public CarAlreadyAssignedException(Car car) { 11 | super("Car n=" + car.getId() + " is already assigned to a charging station"); 12 | } 13 | 14 | public CarAlreadyAssignedException(Car car, ChargingStation chargingStation) { 15 | super("Car n=" + car.getId() + " is already assigned to a charging station and " 16 | + "can't be assigned to chargingStation i=" + chargingStation.getId()); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/random/PerformanceMeasurementRandom.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement.random; 2 | 3 | public class PerformanceMeasurementRandom extends PerformanceMeasurementRandomTemplate { 4 | 5 | 6 | private final String method; 7 | 8 | public PerformanceMeasurementRandom(int nCars, 9 | int nChargingStations, int nTimeslots, int seed, boolean solveLP, String method) { 10 | super(nCars, nChargingStations, nTimeslots, seed, solveLP); 11 | this.method = method; 12 | 13 | } 14 | 15 | 16 | public String getMethod() { 17 | return method; 18 | } 19 | 20 | 21 | @Override 22 | public String toString() { 23 | return super.toString() + " method=" + getMethod(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/opt/util/MethodTimerState.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.util; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class MethodTimerState { 6 | 7 | private double executionTimeOverall; 8 | private ArrayList executionTimes; 9 | 10 | public MethodTimerState() { 11 | this.executionTimes = new ArrayList<>(); 12 | } 13 | 14 | /** 15 | * Returns time in seconds 16 | * @return 17 | */ 18 | public double getTime() { 19 | return executionTimeOverall; 20 | } 21 | 22 | public void addTime(double time) { 23 | this.executionTimeOverall += time; 24 | this.executionTimes.add(time); 25 | } 26 | 27 | public ArrayList getExecutionTimes() { 28 | return executionTimes; 29 | } 30 | 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/random/UniformDistribution.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.random; 2 | 3 | import java.util.Random; 4 | 5 | public class UniformDistribution extends Distribution { 6 | 7 | private final double min; 8 | private final double max; 9 | 10 | public UniformDistribution(Random random, double min, double max) { 11 | super(random); 12 | this.min = min; 13 | this.max = max; 14 | } 15 | 16 | public double getMin() { 17 | return min; 18 | } 19 | 20 | public double getMax() { 21 | return max; 22 | } 23 | 24 | public double getNextDouble() { 25 | return (this.getRandom().nextDouble()*(max-min))+min; 26 | } 27 | 28 | @Override 29 | public Distribution clone(Random random) { 30 | return new UniformDistribution(random, getMin(), getMax()); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: emobility-smart-charging 3 | Upstream-Contact: Oliver Frendo 4 | Source: https://github.com/SAP/emobility-smart-charging 5 | 6 | Files: docs/* 7 | frontend/* 8 | src/* 9 | gen/* 10 | .travis.yml 11 | pom.xml 12 | postman/* 13 | .mvn/* 14 | mvnw 15 | mvwn.cmd 16 | Dockerfile 17 | Makefile 18 | *.md 19 | buildspec.yml 20 | manifest.yml 21 | manifest_dev.yml 22 | Copyright: 2020 SAP SE or an SAP affiliate company and emobility-smart-charging contributors 23 | License: Apache-2.0 24 | 25 | 26 | Files: *.gitignore 27 | *.dockerignore 28 | Copyright: 2020 SAP SE or an SAP affiliate company and emobility-smart-charging contributors 29 | License: CC0-1.0 30 | 31 | 32 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/util/SimulationListenerCSV.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.util; 2 | 3 | import com.sap.charging.realTime.State; 4 | 5 | public class SimulationListenerCSV extends SimulationListenerOutputData { 6 | 7 | public SimulationListenerCSV() { 8 | super(); 9 | } 10 | 11 | public String getCSVString() { 12 | StringBuilder sb = new StringBuilder(); 13 | 14 | sb.append("step;current;currentPlanLimit\n"); 15 | for (Row row : rows) { 16 | appendRow(sb, row); 17 | } 18 | 19 | return sb.toString(); 20 | } 21 | 22 | private void appendRow(StringBuilder sb, Row row) { 23 | sb.append(row.step + ";" + row.current + ";" + row.currentPlanLimit); 24 | sb.append("\n"); 25 | } 26 | 27 | @Override 28 | // Ignore 29 | public void callbackBeforeUpdate(State state) {} 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/model/forecasting/departure/CarDepartureForecastMedianTimestamp.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting.departure; 2 | 3 | import com.sap.charging.model.Car; 4 | import com.sap.charging.realTime.State; 5 | import com.sap.charging.util.TimeUtil; 6 | 7 | public class CarDepartureForecastMedianTimestamp extends CarDepartureForecast { 8 | 9 | private final int medianTimestamp; 10 | 11 | public CarDepartureForecastMedianTimestamp(int medianTimestamp) { 12 | this.medianTimestamp = medianTimestamp; 13 | } 14 | 15 | @Override 16 | public int getExpectedDepartureTimeslot(State state, Car car) { 17 | return TimeUtil.getTimeslotFromSeconds(medianTimestamp); 18 | } 19 | 20 | @Override 21 | public int getExpectedDepartureTimeSeconds(State state, Car car) { 22 | return medianTimestamp; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/app/fuse-tree-circuit-diagram/fuse-tree-circuit-diagram-dialog.html: -------------------------------------------------------------------------------- 1 |

Fuse tree circuit diagram

2 |
Hint: You can pan across and zoom in on the circuit diagram
3 |
4 | 5 |
6 | 7 | 8 |
9 | 13 | 17 |
18 | -------------------------------------------------------------------------------- /frontend/src/app/request-json-component/request-json-component.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | import { OptimizeChargingProfilesRequest } from 'src/assets/server_types'; 3 | import { Utils } from '../utils/Utils'; 4 | 5 | @Component({ 6 | selector: 'app-request-json-component', 7 | templateUrl: './request-json-component.component.html', 8 | styleUrls: ['./request-json-component.component.css'] 9 | }) 10 | export class RequestJsonComponentComponent implements OnInit { 11 | 12 | @Input() restApiRequest: OptimizeChargingProfilesRequest; 13 | 14 | constructor() { } 15 | 16 | ngOnInit() { 17 | 18 | } 19 | 20 | onClickDownloadApiRequest(): void { 21 | const content = JSON.stringify(this.restApiRequest, null, 4); 22 | Utils.createFileDownload("apiRequest.json", content); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/ocpp/OCPPData.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.ocpp; 2 | 3 | import org.json.simple.JSONObject; 4 | 5 | import com.fasterxml.jackson.annotation.JsonCreator; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.sap.charging.util.JSONSerializable; 8 | 9 | public class OCPPData implements JSONSerializable { 10 | 11 | 12 | public final String ocppVersion = "1.6"; 13 | public final ChargingProfileAssignment[] chargingProfileAssignments; 14 | 15 | @JsonCreator 16 | public OCPPData( 17 | @JsonProperty("chargingProfileAssignments") ChargingProfileAssignment[] chargingProfileAssignments 18 | ) { 19 | this.chargingProfileAssignments = chargingProfileAssignments; 20 | } 21 | 22 | 23 | @Override 24 | public JSONObject toJSONObject() { 25 | return JSONConverter.toJSONObject(this); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/MeasurementExecutor.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement; 2 | 3 | import java.util.function.Consumer; 4 | 5 | import com.sap.charging.util.Loggable; 6 | import com.sap.charging.util.sqlite.SQLiteDB; 7 | 8 | public abstract class MeasurementExecutor> 9 | implements Consumer, Loggable { 10 | 11 | protected final SQLiteDB db; 12 | 13 | protected final boolean forceMeasurement; 14 | 15 | public MeasurementExecutor(SQLiteDB db) { 16 | this(db, false); 17 | } 18 | 19 | public MeasurementExecutor(SQLiteDB db, boolean forceMeasurement) { 20 | this.db = db; 21 | this.forceMeasurement = forceMeasurement; 22 | } 23 | 24 | @Override 25 | public int getVerbosity() { 26 | return 2; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/model/FuseTreeChargingStationCounter.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.model; 2 | 3 | public class FuseTreeChargingStationCounter { 4 | 5 | private final int limitNumberChargingStations; 6 | private int currentNumberChargingStations = 0; 7 | 8 | /** 9 | * 10 | * @param max Pass 0 for no limit 11 | */ 12 | public FuseTreeChargingStationCounter(int limitNumberChargingStations) { 13 | this.limitNumberChargingStations = limitNumberChargingStations; 14 | } 15 | public void addChargingStation() { 16 | currentNumberChargingStations++; 17 | } 18 | 19 | public int getCurrentNumberChargingStation() { 20 | return currentNumberChargingStations; 21 | } 22 | 23 | public boolean isLimitReached() { 24 | if (limitNumberChargingStations <= 0) { 25 | return false; 26 | } 27 | return currentNumberChargingStations >= limitNumberChargingStations; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /frontend/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | 'browserName': 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/event/EventCarArrival.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.event; 2 | 3 | import java.time.LocalTime; 4 | 5 | import org.json.simple.JSONObject; 6 | 7 | import com.sap.charging.model.Car; 8 | import com.sap.charging.util.JSONKeys; 9 | 10 | public class EventCarArrival extends Event { 11 | 12 | public final Car car; 13 | 14 | public EventCarArrival(LocalTime timestamp, Car newCar) { 15 | super(timestamp); 16 | this.car = newCar; 17 | } 18 | 19 | public EventCarArrival(JSONObject jsonObject) { 20 | super(jsonObject); 21 | this.car = Car.fromJSON((JSONObject) jsonObject.get(JSONKeys.JSON_KEY_CAR), 96); 22 | } 23 | 24 | @SuppressWarnings("unchecked") 25 | @Override 26 | public void addChildJSONAttributes(JSONObject object) { 27 | object.put(JSONKeys.JSON_KEY_EVENT_TYPE, EventType.CarArrival.toString()); 28 | object.put(JSONKeys.JSON_KEY_CAR, car.toJSONObject()); 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/Util.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util; 2 | 3 | import java.text.DecimalFormat; 4 | 5 | public class Util { 6 | 7 | /** 8 | * Uses Thread.sleep to wait a while 9 | */ 10 | public static void sleep(long time) { 11 | try { 12 | Thread.sleep(time); 13 | } catch (InterruptedException e) {} 14 | } 15 | 16 | private static DecimalFormat doubleFormat = new DecimalFormat("#.##"); 17 | public static String formatDouble(double d) { 18 | return doubleFormat.format(d).replaceAll(",", "."); 19 | } 20 | 21 | public static String formatDoubleArray(double[] array) { 22 | String result = "["; 23 | for (int i=0;i cars, 17 | List chargingStations, 18 | EnergyPriceHistory energyPriceHistory, 19 | FuseTree fuseTree) { 20 | super(cars, chargingStations, energyPriceHistory, fuseTree); 21 | } 22 | 23 | public InstanceEmpty(DataGenerator data) { 24 | super(data); 25 | } 26 | 27 | @Override 28 | protected JSONObject getSolutionJSON() { 29 | return null; 30 | } 31 | 32 | @Override 33 | public String getMethod() { 34 | return "empty"; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/opt/heuristic/InstanceHeuristicAbsSoCTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.heuristic; 2 | 3 | 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Disabled; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import com.sap.charging.dataGeneration.DataGenerator; 9 | import com.sap.charging.dataGeneration.common.DefaultDataGenerator; 10 | import com.sap.charging.opt.heuristics.InstanceHeuristicAbsSoCLP; 11 | import com.sap.charging.opt.lp.InstanceLP; 12 | 13 | public class InstanceHeuristicAbsSoCTest { 14 | 15 | @BeforeEach 16 | public void setup() { 17 | InstanceLP.verbosity = 0; 18 | } 19 | 20 | @Disabled 21 | @Test 22 | public void testCompleteExampleReal() { 23 | //DataGeneratorReal data = new DataGeneratorReal("2017-11-16", 0, true, true); 24 | DataGenerator data = DefaultDataGenerator.getToyDataGenerator(); 25 | 26 | InstanceHeuristicAbsSoCLP instance = new InstanceHeuristicAbsSoCLP(data); 27 | instance.constructProblem(); 28 | instance.getSolvedProblemInstanceJSON(); 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/AppOCPP.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging; 2 | 3 | import com.sap.charging.dataGeneration.DataGenerator; 4 | import com.sap.charging.dataGeneration.DataGeneratorRandom; 5 | import com.sap.charging.realTime.Strategy; 6 | import com.sap.charging.realTime.StrategyAlgorithmic; 7 | import com.sap.charging.sim.Simulation; 8 | import com.sap.charging.util.FileIO; 9 | 10 | public class AppOCPP { 11 | 12 | 13 | public static void main(String[] args) { 14 | 15 | 16 | DataGenerator data = new DataGeneratorRandom(0, false); 17 | data.generateEnergyPriceHistory(96) 18 | .generateChargingStations(1) 19 | .generateCars(1) 20 | .generateFuseTree(999, true); 21 | Strategy strategy = new StrategyAlgorithmic(); 22 | 23 | Simulation sim = new Simulation(data, strategy); 24 | sim.init(); 25 | 26 | for (int i=0;i<40000;i++) { 27 | sim.simulateNextStep(); 28 | } 29 | 30 | FileIO.writeFile("state.json", sim.getState().toJSONObject()); 31 | 32 | 33 | 34 | 35 | 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/event/EventEnergyPriceChange.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.event; 2 | 3 | import java.time.LocalTime; 4 | 5 | import org.json.simple.JSONObject; 6 | 7 | import com.sap.charging.model.EnergyPriceHistory; 8 | import com.sap.charging.util.JSONKeys; 9 | 10 | public class EventEnergyPriceChange extends Event { 11 | 12 | public final EnergyPriceHistory history; 13 | 14 | public EventEnergyPriceChange(LocalTime timestamp, EnergyPriceHistory history) { 15 | super(timestamp); 16 | this.history = history; 17 | } 18 | 19 | public EventEnergyPriceChange(JSONObject jsonObject) { 20 | super(jsonObject); 21 | this.history = EnergyPriceHistory.fromJSON((JSONObject) jsonObject.get(JSONKeys.JSON_KEY_ENERGY_PRICE_HISTORY)); 22 | } 23 | 24 | @SuppressWarnings("unchecked") 25 | @Override 26 | public void addChildJSONAttributes(JSONObject object) { 27 | object.put(JSONKeys.JSON_KEY_EVENT_TYPE, EventType.EnergyPriceChange.toString()); 28 | object.put(JSONKeys.JSON_KEY_ENERGY_PRICE_HISTORY, history.toJSONObject()); 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/model/forecasting/departure/CarDepartureForecast.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting.departure; 2 | 3 | import com.sap.charging.model.Car; 4 | import com.sap.charging.model.CarProcessData; 5 | import com.sap.charging.realTime.State; 6 | import com.sap.charging.realTime.model.forecasting.Forecast; 7 | 8 | public abstract class CarDepartureForecast extends Forecast { 9 | 10 | 11 | public abstract int getExpectedDepartureTimeslot(State state, Car car); 12 | 13 | public abstract int getExpectedDepartureTimeSeconds(State state, Car car); 14 | 15 | /** 16 | * The default value (17:03) is the median departure time of a historical dataset 17 | * @return 18 | */ 19 | public static CarDepartureForecast getDefaultCarDepartureForecast() { 20 | return new CarDepartureForecastMedianTimestamp(17*3600 + 3*60); 21 | } 22 | 23 | public String getRMatrixString(Car car) { 24 | CarProcessData data = car.getCarProcessData(); 25 | String matrixString = "as.matrix(data.table::" + data.oneHotEncodedRaw + ")"; 26 | return matrixString; 27 | } 28 | 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/model/forecasting/departure/CarDepartureForecastMedianTimeslot.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting.departure; 2 | 3 | import com.sap.charging.model.Car; 4 | import com.sap.charging.realTime.State; 5 | import com.sap.charging.util.TimeUtil; 6 | 7 | @Deprecated 8 | public class CarDepartureForecastMedianTimeslot extends CarDepartureForecast { 9 | 10 | /** 11 | * Default median is 69 (17:15) 12 | */ 13 | private int medianDepartureTimeslot = 69; 14 | 15 | /** 16 | * If this is used, keep median as 69 (17:15) 17 | */ 18 | public CarDepartureForecastMedianTimeslot() { 19 | 20 | } 21 | public CarDepartureForecastMedianTimeslot(int medianDepartureTimeslot) { 22 | this.medianDepartureTimeslot = medianDepartureTimeslot; 23 | } 24 | 25 | @Override 26 | public int getExpectedDepartureTimeslot(State state, Car car) { 27 | return medianDepartureTimeslot; 28 | } 29 | 30 | @Override 31 | public int getExpectedDepartureTimeSeconds(State state, Car car) { 32 | return TimeUtil.getTimestampFromTimeslot(medianDepartureTimeslot).toSecondOfDay(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/dataGeneration/DataGeneratorDeterministicTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.dataGeneration; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.ArrayList; 6 | 7 | import org.junit.jupiter.api.Test; 8 | 9 | import com.sap.charging.model.Car; 10 | 11 | 12 | 13 | public class DataGeneratorDeterministicTest { 14 | 15 | @Test 16 | public void test() { 17 | int nTimeslots = -1; 18 | int nCars = -1; 19 | int nChargingStations = 10; 20 | 21 | DataGeneratorDeterministic data = new DataGeneratorDeterministic(); 22 | data.generateEnergyPriceHistory(nTimeslots) 23 | .generateCars(nCars) 24 | .generateChargingStations(nChargingStations) 25 | .generateFuseTree(4, true); 26 | 27 | assertEquals(DataGeneratorDeterministic.defaultEnergyPriceHistory.length, 28 | data.getEnergyPriceHistory().getNTimeslots()); 29 | 30 | ArrayList defaultCars = data.generateDefaultCars(); 31 | 32 | assertEquals(defaultCars.size(), data.getCars().size()); 33 | assertEquals(nChargingStations, data.getChargingStations().size()); 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/sim/common/StrategyMock.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.common; 2 | 3 | import com.sap.charging.model.Car; 4 | import com.sap.charging.model.EnergyPriceHistory; 5 | import com.sap.charging.realTime.State; 6 | import com.sap.charging.realTime.Strategy; 7 | 8 | public class StrategyMock extends Strategy { 9 | 10 | public int counterCarArrival = 0; 11 | public int counterCarFinished = 0; 12 | public int counterCarDeparture = 0; 13 | public int counterEnergyPriceChange = 0; 14 | 15 | @Override 16 | public void reactCarArrival(State currentState, Car newCar) { 17 | counterCarArrival++; 18 | } 19 | 20 | @Override 21 | public void reactCarFinished(State currentState, Car carFinished) { 22 | counterCarFinished++; 23 | } 24 | 25 | @Override 26 | public void reactCarDeparture(State currentState, Car carLeaving) { 27 | counterCarDeparture++; 28 | } 29 | 30 | @Override 31 | public void reactEnergyPriceChange(State currentState, EnergyPriceHistory newEnergyPriceHistory) { 32 | counterEnergyPriceChange++; 33 | } 34 | 35 | @Override 36 | public String getMethod() { 37 | return "realTimeMock"; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /frontend/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/playground'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/AppNonlinearBatteryRuntime.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging; 2 | 3 | import com.sap.charging.model.battery.BatterySim; 4 | import com.sap.charging.model.battery.BatterySimParameters; 5 | 6 | public class AppNonlinearBatteryRuntime { 7 | 8 | 9 | 10 | public static void main(String[] args) { 11 | 12 | long startTime = System.currentTimeMillis(); 13 | for (int simIndex=0;simIndex<10000;simIndex++) { 14 | if (simIndex % 100 == 0) { 15 | System.out.println("simIndex=" + simIndex); 16 | } 17 | doOneSim(); 18 | } 19 | 20 | long finishTime = System.currentTimeMillis(); 21 | long diff = finishTime - startTime; 22 | 23 | 24 | System.out.println("Done, took: " + diff + "ms. Time per sim: " + diff/10000 + "ms"); 25 | 26 | } 27 | 28 | private static void doOneSim() { 29 | 30 | BatterySimParameters params = BatterySimParameters.buildDefaultParams(); 31 | BatterySim sim = new BatterySim(params); 32 | 33 | for (int step=0;step<8*3600;step++) { 34 | sim.simulateNextStep(params.constantCurrent); 35 | } 36 | //System.out.println(soc); 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/PerformanceMeasurement.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement; 2 | 3 | import com.sap.charging.util.sqlite.SQLiteAttributeIgnore; 4 | import com.sap.charging.util.sqlite.SQLiteAttributeKey; 5 | import com.sap.charging.util.sqlite.SQLiteTableRow; 6 | 7 | public abstract class PerformanceMeasurement> extends SQLiteTableRow { 8 | 9 | @SQLiteAttributeIgnore 10 | public static int measurementNumber; 11 | @SQLiteAttributeIgnore 12 | public static int nMeasurements; 13 | 14 | 15 | 16 | @SQLiteAttributeKey 17 | public final String method; 18 | 19 | public final String guid; 20 | public final String filePath; 21 | public String dateTimeEnd; 22 | 23 | public PerformanceMeasurement() { 24 | this(null, null, null); 25 | } 26 | public PerformanceMeasurement(String guid, String filePath, String method) { 27 | this.guid = guid; 28 | this.filePath = filePath; 29 | this.method = method; 30 | } 31 | 32 | public abstract SubType cloneWithMethod(String method); 33 | 34 | } 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/sqlite/SQLiteAttribute.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.sqlite; 2 | 3 | public class SQLiteAttribute { 4 | 5 | private final Object value; 6 | public final String name; 7 | public final String dataType; 8 | public final boolean isKey; 9 | public final boolean isNotNull; 10 | 11 | public SQLiteAttribute(Object value, String name, String dataType, boolean isKey, boolean isNotNull) { 12 | this.value = value; 13 | this.name = name; 14 | this.dataType = dataType; 15 | this.isKey = isKey; 16 | this.isNotNull = isNotNull; 17 | } 18 | 19 | public String getValueString() { 20 | if (value == null) 21 | return "null"; 22 | else if (dataType.equals("TEXT")) // Strings 23 | return "'" + value.toString() + "'"; 24 | else if (dataType.equals("BOOLEAN")) 25 | return (boolean) value ? "1" : "0"; 26 | else 27 | return value.toString(); 28 | } 29 | 30 | public Object getValue() { 31 | return value; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "Name=" + name + ", value=" + value + ", dataType=" + dataType + ", isKey=" + isKey + ", isNotNull=" + isNotNull; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/event/EventType.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.event; 2 | 3 | import org.json.simple.JSONObject; 4 | 5 | import com.sap.charging.util.JSONKeys; 6 | 7 | public enum EventType { 8 | 9 | CarArrival, 10 | CarDeparture, 11 | CarFinished, 12 | EnergyPriceChange, 13 | Reoptimize; 14 | 15 | public static Event fromJSON(JSONObject jsonObject) { 16 | 17 | if (jsonObject.containsKey(JSONKeys.JSON_KEY_EVENT)) 18 | jsonObject = (JSONObject) jsonObject.get(JSONKeys.JSON_KEY_EVENT); 19 | 20 | String eventTypeString = (String) jsonObject.get(JSONKeys.JSON_KEY_EVENT_TYPE); 21 | EventType eventType = EventType.valueOf(eventTypeString); 22 | switch(eventType) { 23 | case CarArrival: 24 | return new EventCarArrival(jsonObject); 25 | case CarDeparture: 26 | return new EventCarDeparture(jsonObject); 27 | case CarFinished: 28 | return new EventCarFinished(jsonObject); 29 | case EnergyPriceChange: 30 | return new EventEnergyPriceChange(jsonObject); 31 | case Reoptimize: 32 | return new EventReoptimize(jsonObject); 33 | default: 34 | return null; 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Playground 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.3.5. 4 | 5 | ## Development server 6 | 7 | Run `npm start` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `npm run ng -- generate component component-name` to generate a new component. You can also use `npm run ng -- generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `npm run build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `npm run build:prod` for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `npm test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `npm run e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `npm run ng -- help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/ocpp/JSONConverter.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.ocpp; 2 | 3 | import org.json.simple.JSONObject; 4 | import org.json.simple.parser.JSONParser; 5 | import org.json.simple.parser.ParseException; 6 | 7 | import com.fasterxml.jackson.core.JsonProcessingException; 8 | import com.fasterxml.jackson.databind.ObjectMapper; 9 | 10 | public class JSONConverter { 11 | 12 | private static ObjectMapper mapper = new ObjectMapper(); 13 | private static JSONParser jsonSimpleParser = new JSONParser(); 14 | 15 | 16 | public static JSONObject toJSONObject(OCPPData ocppData) { 17 | try { 18 | String text = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(ocppData); 19 | JSONObject result = (JSONObject) jsonSimpleParser.parse(text); 20 | return result; 21 | } catch (JsonProcessingException | ParseException e) { 22 | e.printStackTrace(); 23 | } 24 | return null; 25 | } 26 | 27 | /*public ChargingProfile toChargingProfile(String input) { 28 | try { 29 | return mapper.readValue(input, ChargingProfile.class); 30 | } catch (IOException e) { 31 | e.printStackTrace(); 32 | } 33 | return null; 34 | }*/ 35 | 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/dataGeneration/carDistributions/CarConsumptionDistributionPHEV.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.dataGeneration.carDistributions; 2 | 3 | public class CarConsumptionDistributionPHEV { 4 | 5 | public static final double binSize = 1000; // Wh 6 | 7 | /** 8 | * Insert your consumption data here: Each cell of the matrix is the probability of being selected. The dimensions in the 2nd array can be of differing length. 9 | * For example, distribution[0][5] gives the probability of an EV charging 5kWh on a Monday. 10 | */ 11 | public static final double[][] distribution = { 12 | {000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000}, 13 | {000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000}, 14 | {000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000}, 15 | {000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000}, 16 | {000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000} 17 | }; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/dataGeneration/carDistributions/CarConsumptionDistributionBEV.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.dataGeneration.carDistributions; 2 | 3 | public class CarConsumptionDistributionBEV { 4 | 5 | /** 6 | * Wh 7 | */ 8 | public static final double binSize = 1000; 9 | 10 | /** 11 | * Insert your consumption data here: Each cell of the matrix is the probability of being selected. The dimensions in the 2nd array can be of differing length. 12 | * For example, distribution[0][5] gives the probability of an EV charging 5kWh on a Monday. 13 | */ 14 | public static final double[][] distribution = { 15 | {000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000}, 16 | {000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000}, 17 | {000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000}, 18 | {000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000}, 19 | {000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000, 000000000} 20 | }; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/model/forecasting/departure/CarDepartureForecastLinearModel.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting.departure; 2 | 3 | import com.sap.charging.model.Car; 4 | import com.sap.charging.realTime.State; 5 | import com.sap.charging.util.TimeUtil; 6 | 7 | public class CarDepartureForecastLinearModel extends CarDepartureForecast { 8 | 9 | private final double intercept; 10 | private final double timeOfDayArrivalCoefficient; 11 | 12 | public CarDepartureForecastLinearModel(double intercept, double timeOfDayArrivalCoefficient) { 13 | this.intercept = intercept; 14 | this.timeOfDayArrivalCoefficient = timeOfDayArrivalCoefficient; 15 | } 16 | 17 | @Override 18 | public int getExpectedDepartureTimeslot(State state, Car car) { 19 | double resultTimestamp = intercept + timeOfDayArrivalCoefficient * state.currentTimeSeconds; 20 | int resultTimeslot = TimeUtil.getTimeslotFromSeconds((int) resultTimestamp); 21 | return Math.min(resultTimeslot+1, 95); 22 | } 23 | 24 | @Override 25 | public int getExpectedDepartureTimeSeconds(State state, Car car) { 26 | double resultTimestamp = intercept + timeOfDayArrivalCoefficient * state.currentTimeSeconds; 27 | return (int) resultTimestamp; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'playground'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.debugElement.componentInstance; 26 | expect(app.title).toEqual('playground'); 27 | }); 28 | 29 | it('should render title', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.debugElement.nativeElement; 33 | expect(compiled.querySelector('.content span').textContent).toContain('playground app is running!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/main/resources/log4j2.properties: -------------------------------------------------------------------------------- 1 | status = error 2 | name = PropertiesConfig 3 | 4 | #Make sure to change log file path as per your need 5 | property.filename = logs/chargeOptimizer.log 6 | 7 | filters = threshold 8 | 9 | filter.threshold.type = ThresholdFilter 10 | filter.threshold.level = debug 11 | 12 | appenders = rolling 13 | 14 | appender.rolling.type = RollingFile 15 | appender.rolling.name = RollingFile 16 | appender.rolling.fileName = ${filename} 17 | appender.rolling.filePattern = debug-backup-%d{MM-dd-yy-HH-mm-ss}-%i.log.gz 18 | appender.rolling.layout.type = PatternLayout 19 | appender.rolling.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n 20 | appender.rolling.policies.type = Policies 21 | appender.rolling.policies.time.type = TimeBasedTriggeringPolicy 22 | appender.rolling.policies.time.interval = 1 23 | appender.rolling.policies.time.modulate = true 24 | appender.rolling.policies.size.type = SizeBasedTriggeringPolicy 25 | appender.rolling.policies.size.size=10MB 26 | appender.rolling.strategy.type = DefaultRolloverStrategy 27 | appender.rolling.strategy.max = 20 28 | 29 | loggers = rolling 30 | logger.rolling.name = com.sap.charging.playground 31 | logger.rolling.level = debug 32 | logger.rolling.additivity = false 33 | logger.rolling.appenderRef.rolling.ref = RollingFile -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/realTime/model/forecasting/CarDepartureForecastOracleTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import com.sap.charging.dataGeneration.DataGeneratorRandom; 8 | import com.sap.charging.model.Car; 9 | import com.sap.charging.realTime.model.forecasting.departure.CarDepartureOracle; 10 | 11 | public class CarDepartureForecastOracleTest { 12 | 13 | 14 | @Test 15 | public void departurePrediction_oracle_shouldExactlyPredict() { 16 | 17 | int nCars = 10; 18 | 19 | DataGeneratorRandom data = new DataGeneratorRandom(0, false); 20 | data.generateEnergyPriceHistory(96) 21 | .generateCars(nCars) 22 | .generateChargingStations(nCars) 23 | .generateFuseTree(100, true); 24 | 25 | CarDepartureOracle forecast = new CarDepartureOracle(); 26 | 27 | for (Car car : data.getCars()) { 28 | int resultTimestamp = forecast.getExpectedDepartureTimeSeconds(null, car); 29 | assertEquals(resultTimestamp, car.timestampDeparture.toSecondOfDay()); 30 | //System.out.println("Prediction for n=" + car.getId() + "=" + resultTimestamp + ", actual=" + car.timestampDeparture.toSecondOfDay()); 31 | } 32 | 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/api/v1/exception/ApiError.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.api.v1.exception; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import org.springframework.http.HttpStatus; 7 | 8 | public class ApiError { 9 | 10 | private HttpStatus status; 11 | private String message; 12 | private List errors; 13 | 14 | public ApiError(HttpStatus status, String message, List errors) { 15 | super(); 16 | this.setStatus(status); 17 | this.setMessage(message); 18 | this.setErrors(errors); 19 | } 20 | 21 | public ApiError(HttpStatus status, String message, String error) { 22 | super(); 23 | this.setStatus(status); 24 | this.setMessage(message); 25 | setErrors(Arrays.asList(error)); 26 | } 27 | 28 | public HttpStatus getStatus() { 29 | return status; 30 | } 31 | 32 | public void setStatus(HttpStatus status) { 33 | this.status = status; 34 | } 35 | 36 | public String getMessage() { 37 | return message; 38 | } 39 | 40 | public void setMessage(String message) { 41 | this.message = message; 42 | } 43 | 44 | public List getErrors() { 45 | return errors; 46 | } 47 | 48 | public void setErrors(List errors) { 49 | this.errors = errors; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/opt/lp/InstanceLPPeakShavingTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.lp; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Disabled; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import com.sap.charging.dataGeneration.DataGenerator; 8 | import com.sap.charging.dataGeneration.common.DefaultDataGenerator; 9 | import com.sap.charging.opt.heuristics.InstanceHeuristicAbsSoCLP; 10 | 11 | public class InstanceLPPeakShavingTest { 12 | 13 | 14 | @BeforeEach 15 | public void setup() { 16 | InstanceLP.verbosity = 0; 17 | } 18 | @Disabled 19 | @Test 20 | public void testCompleteExampleToyWithHeuristic() { 21 | DataGenerator data = DefaultDataGenerator.getToyDataGenerator(); 22 | 23 | InstanceHeuristicAbsSoCLP instance = new InstanceHeuristicAbsSoCLP(data); 24 | instance.getInstanceLP().objectiveEnergyCosts.setWeight(0); 25 | instance.getInstanceLP().objectiveLoadImbalance.setWeight(0); 26 | instance.getInstanceLP().objectivePeakShaving.setWeight(1e10); 27 | 28 | instance.constructProblem(); 29 | instance.getSolvedProblemInstanceJSON(); 30 | 31 | 32 | //FileIO.writeFile("vis/data/solution_absSoCLP.json", instance.getSolvedProblemInstanceJSON()); 33 | //InstanceHeuristicAbsSoCLP instance = new InstanceHeuristicAbsSoCLP(data); 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/config/Swagger2Config.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.config; 2 | 3 | 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.service.ApiInfo; 10 | import springfox.documentation.spi.DocumentationType; 11 | import springfox.documentation.spring.web.plugins.Docket; 12 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 13 | @Configuration 14 | @EnableSwagger2 15 | public class Swagger2Config { 16 | @Bean 17 | public Docket api() { 18 | return new Docket(DocumentationType.SWAGGER_2).select() 19 | .apis(RequestHandlerSelectors 20 | .basePackage("com.sap.charging.server.api.v1")) 21 | .paths(PathSelectors.any()) 22 | .build() 23 | .apiInfo(apiEndPointsInfo()); 24 | } 25 | private ApiInfo apiEndPointsInfo() { 26 | return new ApiInfoBuilder().title("emobility-smart-charging REST API") 27 | .description("emobility-smart-charging REST API") 28 | .version("1.0.0") 29 | .build(); 30 | } 31 | } -------------------------------------------------------------------------------- /frontend/src/app/fuse-tree-circuit-diagram/fuse-tree-circuit-diagram-dialog.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject, Input } from '@angular/core'; 2 | import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; 3 | import { Car, StateStore } from 'src/assets/server_types'; 4 | import { AppComponent } from '../app.component'; 5 | import { Utils } from '../utils/Utils'; 6 | import { SVG } from "@svgdotjs/svg.js"; 7 | 8 | @Component({ 9 | selector: 'fuse-tree-circuit-diagram-dialog', 10 | templateUrl: 'fuse-tree-circuit-diagram-dialog.html', 11 | }) 12 | export class FuseTreeCircuitDiagramDialog { 13 | 14 | constructor( 15 | public dialogRef: MatDialogRef, 16 | @Inject(MAT_DIALOG_DATA) public data: { 17 | state: StateStore, 18 | appParent: AppComponent 19 | }) { } 20 | 21 | 22 | onClickClose(): void { 23 | this.dialogRef.close(); 24 | } 25 | 26 | onClickSave(): void { 27 | //const svgCircuitDiagram: Element = document.getElementsByClassName("svgCircuitDiagram")[0]; 28 | //const svgContent = svgCircuitDiagram.outerHTML; 29 | 30 | const svgContent = SVG(".svgCircuitDiagram").svg(); 31 | Utils.createFileDownload("fuseTreeCircuitDiagram.svg", svgContent); 32 | } 33 | 34 | 35 | 36 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/security/CustomBasicAuthenticationEntryPoint.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.security; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | import org.springframework.security.core.AuthenticationException; 10 | import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | public class CustomBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint { 15 | 16 | @Override 17 | public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException authException) throws IOException { 18 | response.addHeader("WWW-Authenticate", "Basic realm=\"" + getRealmName() + "\""); 19 | response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 20 | final PrintWriter writer = response.getWriter(); 21 | writer.println("HTTP Status " + HttpServletResponse.SC_UNAUTHORIZED + " - " + authException.getMessage()); 22 | } 23 | 24 | @Override 25 | public void afterPropertiesSet() { 26 | setRealmName("EmobilitySmartChargingApplication"); 27 | super.afterPropertiesSet(); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/event/EventCarFinished.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.event; 2 | 3 | import java.time.LocalTime; 4 | 5 | import org.json.simple.JSONObject; 6 | 7 | import com.sap.charging.model.Car; 8 | import com.sap.charging.model.ChargingStation; 9 | import com.sap.charging.util.JSONKeys; 10 | 11 | public class EventCarFinished extends Event { 12 | 13 | public final Car car; 14 | public final ChargingStation chargingStation; 15 | 16 | public EventCarFinished(LocalTime timestamp, Car carFinished, ChargingStation chargingStation) { 17 | super(timestamp); 18 | this.car = carFinished; 19 | this.chargingStation = chargingStation; 20 | } 21 | 22 | public EventCarFinished(JSONObject jsonObject) { 23 | super(jsonObject); 24 | this.car = Car.fromJSON((JSONObject) jsonObject.get(JSONKeys.JSON_KEY_CAR), 96); 25 | this.chargingStation = ChargingStation.fromJSON((JSONObject) jsonObject.get(JSONKeys.JSON_KEY_CHARGING_STATION)); 26 | } 27 | 28 | @SuppressWarnings("unchecked") 29 | @Override 30 | public void addChildJSONAttributes(JSONObject object) { 31 | object.put(JSONKeys.JSON_KEY_EVENT_TYPE, EventType.CarFinished.toString()); 32 | object.put(JSONKeys.JSON_KEY_CAR, car.toJSONObject()); 33 | object.put(JSONKeys.JSON_KEY_CHARGING_STATION, chargingStation.toJSONObject()); 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/paperINDIN2018/PerformanceMeasurementINDIN2018Template.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement.paperINDIN2018; 2 | 3 | import com.sap.charging.util.performanceMeasurement.PerformanceMeasurementTemplate; 4 | 5 | public class PerformanceMeasurementINDIN2018Template extends PerformanceMeasurementTemplate { 6 | 7 | private final int nCars; 8 | private final double proportionEVs; 9 | private final double gridConnection; 10 | 11 | 12 | public PerformanceMeasurementINDIN2018Template(int nCars, double proportionEVs, double gridConnection) { 13 | this.nCars = nCars; 14 | this.proportionEVs = proportionEVs; 15 | this.gridConnection = gridConnection; 16 | } 17 | 18 | public PerformanceMeasurementINDIN2018 cloneWithMethod(String method) { 19 | return new PerformanceMeasurementINDIN2018(nCars, proportionEVs, gridConnection, method); 20 | } 21 | 22 | public int getNCars() { 23 | return nCars; 24 | } 25 | public double getProportionEVs() { 26 | return proportionEVs; 27 | } 28 | public double getGridConnection() { 29 | return gridConnection; 30 | } 31 | @Override 32 | public String toString() { 33 | return "Measurement: nCars=" + nCars + ", proportionEVs=" + proportionEVs + ", gridConnection=" + gridConnection; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/api/v1/OptimizeChargingProfilesRequest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.api.v1; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.sap.charging.server.api.v1.store.EventStore; 6 | import com.sap.charging.server.api.v1.store.OptimizerSettings; 7 | import com.sap.charging.server.api.v1.store.StateStore; 8 | 9 | public class OptimizeChargingProfilesRequest { 10 | 11 | public StateStore state; 12 | public EventStore event; 13 | public Integer verbosity; 14 | public OptimizerSettings optimizerSettings; 15 | 16 | @JsonCreator 17 | public OptimizeChargingProfilesRequest( 18 | @JsonProperty(value="state", required=true) StateStore state, 19 | @JsonProperty(value="event", required=true) EventStore event, 20 | @JsonProperty(value="optimizerSettings") OptimizerSettings optimizerSettings, 21 | @JsonProperty(value="verbosity") Integer verbosity 22 | ) { 23 | this.state = state; 24 | this.event = event; 25 | if (verbosity != null) { 26 | this.verbosity = verbosity; 27 | } else { 28 | this.verbosity = 3; 29 | } 30 | if (optimizerSettings != null) { 31 | this.optimizerSettings = optimizerSettings; 32 | } else { 33 | this.optimizerSettings = OptimizerSettings.getDefaultOptimizerSettings(); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ############ 2 | ## Server ## 3 | ############ 4 | FROM maven:3.6.1-jdk-8-alpine as build_server 5 | RUN apk add --update make 6 | 7 | # Copy from ChargingOptimizer 8 | WORKDIR /workspace/app 9 | COPY ./frontend ./frontend 10 | COPY ./src ./src 11 | COPY ./pom.xml ./pom.xml 12 | COPY ./Makefile ./Makefile 13 | 14 | # Runs mvn clean install 15 | RUN make emobility-smart-charging-build 16 | 17 | 18 | ############## 19 | ## Frontend ## 20 | ############## 21 | FROM node:lts-alpine3.13 as build_frontend 22 | RUN apk add --update make maven 23 | 24 | # Use build results so far 25 | COPY --from=build_server /workspace/app/frontend /workspace/app/frontend 26 | WORKDIR /workspace/app 27 | COPY ./Makefile ./Makefile 28 | 29 | # npm install and build frontend (Angular 9) 30 | # Separate steps so that Docker can cache npm install 31 | RUN make emobility-smart-charging-npm-install-frontend 32 | RUN make emobility-smart-charging-build-only-frontend 33 | 34 | 35 | ################## 36 | ## Start server ## 37 | ################## 38 | FROM openjdk:8-jre-alpine 39 | 40 | 41 | COPY --from=build_server /workspace/app/target/*.jar /workspace/app/target/ 42 | COPY --from=build_frontend /workspace/app/public /workspace/app/public 43 | 44 | WORKDIR /workspace/app 45 | 46 | EXPOSE 8080 47 | 48 | ENTRYPOINT ["java", "-Djava.security.egd=file:///dev/./urandom", "-jar", "target/emobility-smart-charging-0.0.1-SNAPSHOT.jar"] 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/opt/CONSTANTS.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt; 2 | 3 | import com.sap.charging.util.configuration.Options; 4 | 5 | public class CONSTANTS { 6 | 7 | /** 8 | * M Large constant for setting arbitrary high/low bounds 9 | */ 10 | public static final double M_HIGH_BOUND = 10000; 11 | 12 | 13 | /** 14 | * Fuse size of the root fuse (at the transformer) 15 | */ 16 | public static double FUSE_LEVEL_0_SIZE = 4000; 17 | 18 | /** 19 | * Fuse size of the Steigschienen 20 | */ 21 | public static double FUSE_LEVEL_1_SIZE = 1250; 22 | 23 | /** 24 | * Fuse size of the Versorgungsschienen 25 | */ 26 | public static double FUSE_LEVEL_2_SIZE = 800; 27 | 28 | /** 29 | * Charging efficiency (typically between 0.8 and 0.95) - how much 30 | * energy is lost during the charging process? 31 | * 32 | * Default is 0.85. 33 | */ 34 | public static final double CHARGING_EFFICIENCY = Options.get().chargingEfficiency; 35 | 36 | /*********************** 37 | * FILE PATHS 38 | ***********************/ 39 | /** 40 | * Path to write temp (e.g. .lp and .sol files) to 41 | */ 42 | public static String PATH_DIR_GEN_TEMP = "gen/temp/"; 43 | 44 | public static final String PATH_DIR_GEN_SOLUTION_PERFORMANCE = "gen/performance/"; 45 | 46 | 47 | } 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/ocpp/protocol/ChargingSchedulePeriod.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.ocpp.protocol; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | public class ChargingSchedulePeriod { 7 | 8 | /** 9 | * Required. Start of the period, in seconds from the start of schedule. The value of 10 | StartPeriod also defines the stop time of the previous period. 11 | 12 | Here: Always 15*60=900, since each period (=timeslot) is 15 minutes 13 | */ 14 | public final int startPeriod; 15 | 16 | /** 17 | * Required. Charging rate limit during the schedule period, in the applicable 18 | chargingRateUnit, for example in Amperes or Watts. Accepts at most one digit 19 | fraction (e.g. 8.1). 20 | 21 | Here: Always in Amperes (per phase) 22 | */ 23 | public final double limit; 24 | 25 | /** 26 | * Optional. The number of phases that can be used for charging. If a number of 27 | phases is needed, numberPhases=3 will be assumed unless another number is 28 | given. 29 | 30 | Here: car.sumUsedPhases 31 | */ 32 | public final int numberPhases; 33 | 34 | @JsonCreator 35 | public ChargingSchedulePeriod( 36 | @JsonProperty("startPeriod") int startPeriod, 37 | @JsonProperty("limit") double limit, 38 | @JsonProperty("numberPhases") int numberPhases 39 | ) { 40 | this.startPeriod = startPeriod; 41 | this.limit = limit; 42 | this.numberPhases = numberPhases; 43 | } 44 | 45 | 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/paperOR2018/PerformanceMeasurementPaperOR2018.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement.paperOR2018; 2 | 3 | import com.sap.charging.util.Util; 4 | import com.sap.charging.util.performanceMeasurement.PerformanceMeasurement; 5 | import com.sap.charging.util.sqlite.SQLiteAttributeKey; 6 | 7 | public class PerformanceMeasurementPaperOR2018 extends PerformanceMeasurement{ 8 | 9 | @SQLiteAttributeKey 10 | public final int nCars; 11 | 12 | @SQLiteAttributeKey 13 | public final int nChargingStations; 14 | 15 | @SQLiteAttributeKey 16 | public final int seed; 17 | 18 | 19 | public PerformanceMeasurementPaperOR2018(int nCars, int nChargingStations, int seed) { 20 | this(null, null, null, nCars, nChargingStations, seed); 21 | } 22 | 23 | public PerformanceMeasurementPaperOR2018(String guid, String filePath, String method, int nCars, int nChargingStations, int seed) { 24 | super(guid, filePath, method); 25 | this.nCars = nCars; 26 | this.nChargingStations = nChargingStations; 27 | this.seed = seed; 28 | } 29 | 30 | 31 | @Override 32 | public PerformanceMeasurementPaperOR2018 cloneWithMethod(String method) { 33 | String guid = Util.generateGUID(); 34 | String filePath = "gen/performance/" + guid + ".json"; 35 | return new PerformanceMeasurementPaperOR2018(guid, filePath, method, nCars, nChargingStations, seed); 36 | } 37 | 38 | @Override 39 | public String getTableName() { 40 | return "performanceMeasurementsPaperOR2018"; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/realTime/model/forecasting/CarDepartureForecastNN_CarSampleTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting; 2 | 3 | import org.junit.jupiter.api.Disabled; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import com.sap.charging.dataGeneration.DataGeneratorRandomProcesses; 7 | import com.sap.charging.model.Car; 8 | import com.sap.charging.realTime.model.forecasting.departure.CarDepartureForecastNN_CarSample; 9 | import com.sap.charging.sim.Simulation; 10 | 11 | public class CarDepartureForecastNN_CarSampleTest { 12 | 13 | // No NN models are included with this library 14 | @Disabled 15 | @Test 16 | public static void main(String[] args) { 17 | Simulation.verbosity = 1; 18 | 19 | int nCars = 10; 20 | 21 | DataGeneratorRandomProcesses data = new DataGeneratorRandomProcesses(2, nCars); 22 | data.generateEnergyPriceHistory(96).generateCars(nCars).generateChargingStations(1).generateFuseTree(1, true); 23 | 24 | CarDepartureForecastNN_CarSample forecast = new CarDepartureForecastNN_CarSample( 25 | "gen/models/departureForecastNN_Keras.hdf5"); 26 | 27 | int sum = 0; 28 | for (Car car : data.getCars()) { 29 | int resultTimestamp = forecast.getExpectedDepartureTimeSeconds(null, car); 30 | System.out.println(forecast.getRMatrixString(car)); 31 | sum += resultTimestamp; 32 | System.out.println("Prediction for n=" + car.getId() + "=" + resultTimestamp + ", actual=" 33 | + car.timestampDeparture.toSecondOfDay()); 34 | } 35 | 36 | System.out.println("Mean: " + sum / data.getCars().size()); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/forecasting/PerformanceMeasurementForecasting.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement.forecasting; 2 | 3 | import com.sap.charging.util.Util; 4 | import com.sap.charging.util.performanceMeasurement.PerformanceMeasurement; 5 | import com.sap.charging.util.sqlite.SQLiteAttributeKey; 6 | 7 | public class PerformanceMeasurementForecasting extends PerformanceMeasurement { 8 | 9 | @SQLiteAttributeKey 10 | public final int nCars; 11 | 12 | @SQLiteAttributeKey 13 | public final String forecastingMethod; 14 | 15 | @SQLiteAttributeKey 16 | public final int seed; 17 | 18 | public PerformanceMeasurementForecasting(int nCars, String forecastingMethod, int seed) { 19 | this(null, null, null, nCars, forecastingMethod, seed); 20 | } 21 | 22 | public PerformanceMeasurementForecasting(String guid, String filePath, String method, int nCars, String forecastingMethod, int seed) { 23 | super(guid, filePath, method); 24 | this.nCars = nCars; 25 | this.forecastingMethod = forecastingMethod; 26 | this.seed = seed; 27 | } 28 | 29 | 30 | @Override 31 | public PerformanceMeasurementForecasting cloneWithMethod(String method) { 32 | String guid = Util.generateGUID(); 33 | String filePath = "gen/performance/" + guid + ".json"; 34 | return new PerformanceMeasurementForecasting(guid, filePath, method, nCars, forecastingMethod, seed); 35 | } 36 | 37 | @Override 38 | public String getTableName() { 39 | return "performanceMeasurementsForecasting"; 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/random2018_06/PerformanceMeasurementRandom2018_06.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement.random2018_06; 2 | 3 | import com.sap.charging.util.Util; 4 | import com.sap.charging.util.performanceMeasurement.PerformanceMeasurement; 5 | import com.sap.charging.util.sqlite.SQLiteAttributeKey; 6 | 7 | public class PerformanceMeasurementRandom2018_06 extends PerformanceMeasurement { 8 | 9 | 10 | @SQLiteAttributeKey 11 | public final int nCars; 12 | 13 | @SQLiteAttributeKey 14 | public final int nChargingStations; 15 | 16 | @SQLiteAttributeKey 17 | public final int seed; 18 | 19 | public PerformanceMeasurementRandom2018_06(int nCars, int nChargingStations, int seed) { 20 | this(null, null, null, nCars, nChargingStations, seed); 21 | } 22 | 23 | public PerformanceMeasurementRandom2018_06(String guid, String filePath, String method, int nCars, int nChargingStations, int seed) { 24 | super(guid, filePath, method); 25 | this.nCars = nCars; 26 | this.nChargingStations = nChargingStations; 27 | this.seed = seed; 28 | } 29 | 30 | 31 | @Override 32 | public PerformanceMeasurementRandom2018_06 cloneWithMethod(String method) { 33 | String guid = Util.generateGUID(); 34 | String filePath = "gen/performance/" + guid + ".json"; 35 | return new PerformanceMeasurementRandom2018_06(guid, filePath, method, nCars, nChargingStations, seed); 36 | } 37 | 38 | @Override 39 | public String getTableName() { 40 | return "performanceMeasurementsRandom2018_06"; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/JSONSerializable.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util; 2 | 3 | import org.json.simple.JSONArray; 4 | import org.json.simple.JSONObject; 5 | 6 | public interface JSONSerializable { 7 | 8 | public JSONObject toJSONObject(); 9 | 10 | static int getJSONAttributeAsInt(Object value) { 11 | if (value instanceof Integer) { 12 | return (int) value; 13 | } 14 | else if (value instanceof Long) { 15 | return Math.toIntExact((long) value); 16 | } 17 | throw new RuntimeException("Weird value: " + value); 18 | } 19 | 20 | static double getJSONAttributeAsDouble(Object value) { 21 | if (value instanceof Long) { 22 | return ((Long) value).doubleValue(); 23 | } 24 | else if (value instanceof Double) { 25 | return (Double) value; 26 | } 27 | throw new RuntimeException("Weird value: " + value); 28 | } 29 | 30 | static double[] getJSONAttributeAsDoubleArray(Object value) { 31 | double[] result; 32 | if (value instanceof JSONArray) { 33 | JSONArray array = (JSONArray) value; 34 | result = new double[array.size()]; 35 | for (int i=0;i * { 59 | float: left; 60 | clear: both; 61 | } 62 | 63 | .inputEditDialog { 64 | 65 | width: 90%; 66 | } 67 | 68 | .radioGroupPhaseMatching { 69 | display: flex; 70 | flex-direction: column; 71 | margin: 15px 0; 72 | } 73 | .radioButtonPhaseMatching { 74 | margin: 5px; 75 | } 76 | /* Label of radio buttons should be wider */ 77 | .radioButtonPhaseMatching .mat-radio-label-content { 78 | width: 80%; 79 | } 80 | 81 | /*** 82 | SVG circuit diagram 83 | ***/ 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/opt/lp/util/Solver.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.lp.util; 2 | 3 | import java.io.IOException; 4 | import java.lang.ProcessBuilder.Redirect; 5 | import java.util.Arrays; 6 | 7 | import org.json.simple.JSONObject; 8 | 9 | import com.sap.charging.opt.lp.InstanceLP; 10 | import com.sap.charging.util.Loggable; 11 | 12 | 13 | public abstract class Solver implements Loggable { 14 | 15 | public int getVerbosity() { 16 | return InstanceLP.verbosity; 17 | } 18 | 19 | protected abstract String[] getCLCommand(String pathInputFile, String pathOutputFile); 20 | 21 | /** 22 | * Execute either SCIP or Gurobi to solve the constructed file, e.g. .lp or .mps file 23 | */ 24 | public void solveProblem(String pathInputFile, String pathOutputFile) { 25 | 26 | log(1, "Running command: "); 27 | String[] command = getCLCommand(pathInputFile, pathOutputFile); 28 | log(1, Arrays.toString(command)); 29 | 30 | try { 31 | ProcessBuilder b = new ProcessBuilder(command); 32 | 33 | if (InstanceLP.verbosity >= 2) { 34 | b.redirectOutput(Redirect.INHERIT); 35 | } 36 | b.redirectError(Redirect.INHERIT); 37 | 38 | Process pr = b.start(); 39 | pr.waitFor(); 40 | } catch (IOException | InterruptedException e) { 41 | e.printStackTrace(); 42 | } 43 | 44 | } 45 | 46 | 47 | public abstract JSONObject getSolutionJSON(String pathInputFileSolution); 48 | 49 | protected boolean isWindows() { 50 | String os = System.getProperty("os.name").toLowerCase(); 51 | return (os.indexOf("win") >= 0); 52 | 53 | } 54 | 55 | 56 | } 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/realTime/model/forecasting/CarDepartureForecastXGBoostTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting; 2 | 3 | import org.junit.jupiter.api.Disabled; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import com.sap.charging.dataGeneration.DataGeneratorRandomProcesses; 7 | import com.sap.charging.model.Car; 8 | import com.sap.charging.realTime.model.forecasting.departure.CarDepartureForecastXGBoost_CarDistribution; 9 | import com.sap.charging.sim.Simulation; 10 | 11 | public class CarDepartureForecastXGBoostTest { 12 | 13 | //@Test 14 | /*public void testRConnection() { 15 | CarDepartureForecastXGBoost forecast = new CarDepartureForecastXGBoost("gen/models/xgboost.bin"); 16 | }*/ 17 | 18 | 19 | // No XGBoost models are included with this library 20 | @Disabled 21 | @Test 22 | public void testForecast() { 23 | Simulation.verbosity = 1; 24 | 25 | int nCars = 1000; 26 | DataGeneratorRandomProcesses data = new DataGeneratorRandomProcesses(2, nCars); 27 | data.generateEnergyPriceHistory(96) 28 | .generateCars(nCars) 29 | .generateChargingStations(1) 30 | .generateFuseTree(1, true); 31 | 32 | CarDepartureForecastXGBoost_CarDistribution forecast = new CarDepartureForecastXGBoost_CarDistribution("gen/models/departureForecastXGBoost.bin"); 33 | 34 | int sum = 0; 35 | for (Car car : data.getCars()) { 36 | int resultTimestamp = forecast.getExpectedDepartureTimeSeconds(null, car); 37 | sum += resultTimestamp; 38 | System.out.println("Prediction for n=" + car.getId() + "=" + resultTimestamp); 39 | } 40 | 41 | System.out.println("Mean: " + sum / data.getCars().size()); 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/model/EnergyUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.model; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | import com.sap.charging.model.EnergyUtil.Phase; 9 | 10 | public class EnergyUtilTest { 11 | 12 | 13 | @Test 14 | public void testPhases() { 15 | new EnergyUtil(); 16 | Phase.values(); // superficial call for test coverage 17 | 18 | Phase result = Phase.valueOf("PHASE_1"); 19 | assertEquals(Phase.PHASE_1, result); 20 | assertEquals(1, result.asInt()); 21 | assertEquals("phase1", result.asStringConst()); 22 | 23 | assertEquals(Phase.PHASE_1, Phase.getByInt(1)); 24 | assertEquals(Phase.PHASE_2, Phase.getByInt(2)); 25 | assertEquals(Phase.PHASE_3, Phase.getByInt(3)); 26 | 27 | 28 | try { 29 | // Try getting an invalid phase 30 | Phase.getByInt(4); 31 | } 32 | catch (Exception e) { 33 | assertTrue(e instanceof IllegalArgumentException); 34 | } 35 | } 36 | 37 | @Test 38 | public void testGetIFromP() { 39 | double kW = 22.0; 40 | 41 | // 1 phase car 42 | double expected1 = 1000.0*kW/ (1*230.0); 43 | assertEquals(expected1, EnergyUtil.calculateIFromP(kW, 1), 1e-8); 44 | 45 | // 3 phase car 46 | double expected3 = 1000*kW / (3*230); 47 | assertEquals(expected3, EnergyUtil.calculateIFromP(kW, 3), 1e-8); 48 | } 49 | 50 | @Test 51 | public void testGetAmpereHours() { 52 | double current = 32; 53 | double ampereHours = EnergyUtil.getAmpereHours(1800, current); 54 | 55 | assertEquals(16, ampereHours, 1e-8); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/configuration/Options.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.configuration; 2 | 3 | import java.io.IOException; 4 | 5 | public class Options { 6 | public final int nrSlots; 7 | public final int slotsPerHour; 8 | public final int slotMinutes; 9 | public final int plannedHours; 10 | public final double chargingEfficiency; 11 | 12 | public Options(int hours, int slotMinutes){ 13 | this(hours, slotMinutes, 0.85); 14 | } 15 | 16 | public Options(int hours, int slotMinutes, double efficiency){ 17 | slotsPerHour = 60/slotMinutes; 18 | nrSlots = hours*slotsPerHour; 19 | this.slotMinutes = slotMinutes; 20 | this.plannedHours = hours; 21 | this.chargingEfficiency = efficiency; 22 | } 23 | 24 | private static Options instance = null; 25 | 26 | public static Options get(){ 27 | if(instance==null){ 28 | Configuration conf; 29 | try { 30 | conf = Configuration.getDefault(); 31 | instance = new Options(conf.getInteger("default.charging.planhours", 12), 32 | conf.getInteger("default.charging.slotminutes", 15), 33 | conf.getDouble("default.charging.efficiency", 0.85)); 34 | } catch (IOException | ExceptionInInitializerError | NullPointerException e) { 35 | //e.printStackTrace(); 36 | System.out.println("Error reading standard configuration file."); 37 | instance = new Options(12, 15); 38 | } 39 | } 40 | return instance; 41 | } 42 | 43 | public static void set(int hours, int slotMinutes, double efficiency){ 44 | instance = new Options(hours, slotMinutes, efficiency); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/DB.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement; 2 | 3 | import java.io.File; 4 | 5 | import org.json.simple.JSONObject; 6 | 7 | import com.sap.charging.opt.CONSTANTS; 8 | import com.sap.charging.util.Loggable; 9 | import com.sap.charging.util.sqlite.SQLiteDB; 10 | 11 | public abstract class DB extends SQLiteDB implements Loggable { 12 | 13 | public static int verbosity = 2; 14 | public int getVerbosity() { 15 | return verbosity; 16 | } 17 | 18 | public static final String dbUrl = "jdbc:sqlite:gen/performanceMeasurements.db"; 19 | 20 | public DB() { 21 | super(); 22 | if (tableExists(getTableName()) == false) { 23 | createTable(); 24 | } 25 | tidyDirectory(CONSTANTS.PATH_DIR_GEN_TEMP); 26 | } 27 | 28 | @Override 29 | protected String getDBUrl() { 30 | return dbUrl; 31 | } 32 | 33 | /** 34 | * Tidy gen/temp/ delete all files .lp and .sol files 35 | */ 36 | private void tidyDirectory(String path) { 37 | File folder = new File(path); 38 | File[] fList = folder.listFiles(); 39 | for (File file : fList) { 40 | if (file.getName().endsWith(".lp") || 41 | file.getName().endsWith(".sol")) { 42 | file.delete(); 43 | } 44 | } 45 | } 46 | 47 | protected abstract void createTable(); 48 | protected abstract String getTableName(); 49 | 50 | @SuppressWarnings("rawtypes") 51 | public abstract void insertMeasurement(PerformanceMeasurementTemplate measurementParam, JSONObject result); 52 | @SuppressWarnings("rawtypes") 53 | public abstract boolean measurementExists(PerformanceMeasurementTemplate measurementParam); 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/cli/CLArgument.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.cli; 2 | 3 | import java.util.Arrays; 4 | 5 | public class CLArgument { 6 | 7 | public final String name; 8 | private T value; 9 | private boolean isValueSet; 10 | 11 | public Class type; 12 | public final boolean optional; 13 | public final T defaultValue; 14 | public final T[] allowedValues; 15 | 16 | public CLArgument(String name, Class type, boolean optional, T defaultValue, T[] allowedValues) { 17 | this.name = name; 18 | this.type = type; 19 | this.optional = optional; 20 | this.defaultValue = defaultValue; 21 | this.allowedValues = allowedValues; 22 | } 23 | 24 | public void setValue(T value) throws CLArgumentInvalidValue { 25 | if (allowedValues == null) { 26 | this.value = value; 27 | } 28 | else { 29 | boolean allowed = false; 30 | for (T allowedValue : allowedValues) { 31 | if (value.equals(allowedValue)) { 32 | allowed = true; 33 | this.value = value; 34 | } 35 | } 36 | if (allowed == false) { 37 | throw new CLArgumentInvalidValue(this.name, value); 38 | } 39 | } 40 | isValueSet = true; 41 | } 42 | 43 | public T getValue() { 44 | return value; 45 | } 46 | 47 | public boolean isValueSet() { 48 | return isValueSet; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | String result = name + ": type=" + type.getName() + 54 | ", optional=" + optional + 55 | ", defaultValue=" + defaultValue; 56 | if (allowedValues != null) { 57 | result += ", allowedValues=" + Arrays.toString(allowedValues); 58 | } 59 | return result; 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/opt/lp/EquationTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.lp; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import com.sap.charging.opt.lp.Equation; 8 | import com.sap.charging.opt.lp.Variable; 9 | import com.sap.charging.opt.lp.Equation.ConstraintType; 10 | 11 | public class EquationTest { 12 | 13 | @Test 14 | public void testEnums() { 15 | ConstraintType.values(); 16 | assertEquals(ConstraintType.EQU, ConstraintType.valueOf("EQU")); 17 | } 18 | 19 | @Test 20 | public void test() { 21 | Variable variableX = VariableTest.getDefaultVariableX(); 22 | 23 | Equation eq = new Equation("test", ConstraintType.LEQ); 24 | eq.cloneAndAddVariable(variableX, 5); 25 | eq.RHS = 10; 26 | 27 | Variable copiedVariable = eq.getVariables().get(0); 28 | assertNotEquals(variableX, copiedVariable); 29 | assertEquals(variableX.getValue(), copiedVariable.getValue(), 1e-8); 30 | } 31 | 32 | @Test 33 | public void testToString() { 34 | Variable variableX = VariableTest.getDefaultVariableX(); 35 | 36 | Equation eq = new Equation("test", ConstraintType.LEQ); 37 | eq.cloneAndAddVariable(variableX, 5); 38 | eq.RHS = 10; 39 | 40 | assertEquals("+5.0*X_i5_n12 <= 10.0", eq.toString()); 41 | assertFalse(eq.isObjectiveFunction()); 42 | } 43 | 44 | @Test 45 | public void testToStringObj() { 46 | Variable variableX = VariableTest.getDefaultVariableX(); 47 | 48 | Equation eq = new Equation("test", ConstraintType.OBJ); 49 | eq.cloneAndAddVariable(variableX, 5); 50 | 51 | assertTrue(eq.isObjectiveFunction()); 52 | assertEquals("z=+5.0*X_i5_n12", eq.toString()); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/resources/testCasesJSON/StateStoreTest_ThreePhaseCar_SinglePhaseStation.json: -------------------------------------------------------------------------------- 1 | { 2 | "event": { 3 | "eventType": "Reoptimize" 4 | }, 5 | "state": { 6 | "currentTimeSeconds": 43200, 7 | "cars": [ 8 | { 9 | "id": 0, 10 | "canLoadPhase1": 1, 11 | "canLoadPhase2": 1, 12 | "canLoadPhase3": 1, 13 | "timestampArrival": 28800, 14 | "carType": "BEV", 15 | "maxCapacity": 100, 16 | "minLoadingState": 50, 17 | "startCapacity": 10, 18 | "minCurrent": 18, 19 | "minCurrentPerPhase": 6, 20 | "maxCurrent": 96, 21 | "maxCurrentPerPhase": 32 22 | } 23 | ], 24 | "fuseTree": { 25 | "rootFuse": { 26 | "@type": "Fuse", 27 | "id": 0, 28 | "fusePhase1": 32, 29 | "fusePhase2": 32, 30 | "fusePhase3": 32, 31 | "children": [ 32 | { 33 | "@type": "ChargingStation", 34 | "id": 0, 35 | "fusePhase1": 32, 36 | "fusePhase2": 0, 37 | "fusePhase3": 0, 38 | "phase1Connected": true, 39 | "phase2Connected": false, 40 | "phase3Connected": false 41 | } 42 | ] 43 | } 44 | }, 45 | "carAssignments": [ 46 | { 47 | "carID": 0, 48 | "chargingStationID": 0 49 | } 50 | ] 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/opt/lp/util/SolverGurobi.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.lp.util; 2 | 3 | import org.json.simple.JSONArray; 4 | import org.json.simple.JSONObject; 5 | 6 | import com.sap.charging.util.FileIO; 7 | import com.sap.charging.util.JSONKeys; 8 | 9 | public class SolverGurobi extends Solver { 10 | 11 | @Override 12 | public String[] getCLCommand(String pathInputFile, String pathOutputFile) { 13 | return new String[] { 14 | "gurobi_cl", 15 | "ResultFile=" + pathOutputFile, 16 | pathInputFile 17 | }; 18 | } 19 | 20 | @SuppressWarnings("unchecked") 21 | @Override 22 | public JSONObject getSolutionJSON(String pathInputFileSolution) { 23 | String content = FileIO.readFile(pathInputFileSolution); 24 | JSONObject result = new JSONObject(); 25 | JSONArray variables = new JSONArray(); 26 | result.put(JSONKeys.JSON_KEY_VARIABLES, variables); 27 | 28 | String[] lines = content.split("\n"); 29 | for (int i=0;i { 6 | 7 | private final int nCars; 8 | private final int nChargingStations; 9 | private final int nTimeslots; 10 | private final int seed; 11 | 12 | private final boolean solveLP; 13 | 14 | public PerformanceMeasurementRandomTemplate(int nCars, int nChargingStations, int nTimeslots, int seed, boolean solveLP) { 15 | this.nCars = nCars; 16 | this.nChargingStations = nChargingStations; 17 | this.nTimeslots = nTimeslots; 18 | this.seed = seed; 19 | 20 | this.solveLP = solveLP; 21 | } 22 | 23 | public int getNCars() { 24 | return nCars; 25 | } 26 | public int getNChargingStations() { 27 | return nChargingStations; 28 | } 29 | public int getNTimeslots() { 30 | return nTimeslots; 31 | } 32 | public int getSeed() { 33 | return seed; 34 | } 35 | public boolean getSolveLP() { 36 | return solveLP; 37 | } 38 | 39 | @Override 40 | public PerformanceMeasurementRandom cloneWithMethod(String method) { 41 | PerformanceMeasurementRandom result = new PerformanceMeasurementRandom(this.getNCars(), 42 | this.getNChargingStations(), this.getNTimeslots(), 43 | this.getSeed(), this.getSolveLP(), method); 44 | return result; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "Measurement: nCars=" + nCars + 50 | " nChargingStations=" + nChargingStations + 51 | " nTimeslots=" + nTimeslots + 52 | " seed=" + seed + 53 | " solveLP=" + solveLP; 54 | } 55 | 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/model/forecasting/soc/CarSoCForecastLinearModel.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting.soc; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | 6 | import com.sap.charging.model.Car; 7 | import com.sap.charging.opt.lp.Variable; 8 | import com.sap.charging.util.FileIO; 9 | 10 | public class CarSoCForecastLinearModel extends CarSoCForecast { 11 | 12 | public final HashMap featureCoefficients; 13 | 14 | 15 | public CarSoCForecastLinearModel(String modelFilePath) { 16 | super(); 17 | featureCoefficients = new HashMap<>(); 18 | String featureCoefficientsRaw = FileIO.readFile(modelFilePath); 19 | String[] lines = featureCoefficientsRaw.split("\\n"); 20 | for (String line : lines) { 21 | String[] keyValue = line.split(","); 22 | String key = keyValue[0].replaceAll("`", ""); 23 | double value = (keyValue[1].equals("NA")) ? 0 : Double.parseDouble(keyValue[1]); 24 | featureCoefficients.put(key, value); 25 | } 26 | 27 | } 28 | 29 | @Override 30 | public double getExpectedSoC(Car carPreviousDay, Car car, int lastDay, int currentDay) { 31 | Variable[] variables = getVariablesArray(carPreviousDay, car, lastDay, currentDay); 32 | if (variables.length != featureCoefficients.size()-1) { // Intercept doesn't count 33 | System.out.println("Variables: " + Arrays.toString(variables)); 34 | System.out.println("featureCoefficients: "); 35 | System.out.println(featureCoefficients); 36 | throw new RuntimeException("Variables length (" + variables.length + ") != featureCoefficients.length (" + featureCoefficients.size() + ")"); 37 | } 38 | 39 | double result = featureCoefficients.get("(Intercept)"); 40 | for (Variable v : variables) { 41 | result += featureCoefficients.get(v.getName()) * v.getValue(); 42 | } 43 | 44 | return result; 45 | } 46 | 47 | 48 | } 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/model/forecasting/departure/CarDepartureForecastLinearModelAll.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting.departure; 2 | 3 | import org.rosuda.REngine.REXP; 4 | import org.rosuda.REngine.REXPMismatchException; 5 | 6 | import com.sap.charging.model.Car; 7 | import com.sap.charging.model.CarProcessData; 8 | import com.sap.charging.realTime.State; 9 | import com.sap.charging.util.r.RConnector; 10 | 11 | public class CarDepartureForecastLinearModelAll extends CarDepartureForecast { 12 | 13 | private RConnector rConnector; 14 | 15 | public CarDepartureForecastLinearModelAll(String modelFilePath) { 16 | rConnector = new RConnector(); 17 | String rString = "load('" + modelFilePath + "')\n"; 18 | rString = rConnector.addRTryCtach(rString); 19 | rConnector.evalRString(rString); 20 | log(3, "Executed rString:"); 21 | log(3, rString); 22 | 23 | log(1, "LM all model loaded with path=" + modelFilePath); 24 | 25 | } 26 | 27 | @Override 28 | @Deprecated 29 | public int getExpectedDepartureTimeslot(State state, Car car) { 30 | return -1; 31 | } 32 | 33 | @Override 34 | public int getExpectedDepartureTimeSeconds(State state, Car car) { 35 | CarProcessData data = car.getCarProcessData(); 36 | String matrixString = "data.table::" + data.oneHotEncodedRaw + ""; 37 | String rString = rConnector.addRTryCtach("\tpredict(modelLMAll, " + matrixString + ")\n"); 38 | 39 | log(2, "Executing r String: "); 40 | log(2, rString); 41 | REXP result = rConnector.evalRString(rString); 42 | if (result.isNumeric()) { 43 | try { 44 | int resultTimestamp = (int) result.asDouble(); 45 | return resultTimestamp; 46 | } catch (REXPMismatchException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | else { 51 | System.out.println(result.toDebugString()); 52 | throw new RuntimeException("Error executing LM All prediction"); 53 | } 54 | return -1; 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/util/SimulationListenerJSON.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.util; 2 | 3 | import org.json.simple.JSONArray; 4 | import org.json.simple.JSONObject; 5 | 6 | import com.sap.charging.realTime.State; 7 | import com.sap.charging.util.JSONKeys; 8 | 9 | 10 | public class SimulationListenerJSON extends SimulationListenerOutputData 11 | { 12 | 13 | public SimulationListenerJSON() 14 | { 15 | super(); 16 | } 17 | 18 | @SuppressWarnings("unchecked") 19 | public JSONObject getJSONData() 20 | { 21 | JSONObject info = new JSONObject(); 22 | info.put(JSONKeys.JSON_KEY_JSON_RESULT_CURRENT_UNIT, "ampere"); 23 | info.put(JSONKeys.JSON_KEY_JSON_RESULT_START_TIME, "00:00:00"); 24 | info.put(JSONKeys.JSON_KEY_JSON_RESULT_END_TIME, "23:59:59"); 25 | info.put(JSONKeys.JSON_KEY_JSON_RESULT_STEP_UNIT, "seconds"); 26 | info.put(JSONKeys.JSON_KEY_JSON_RESULT_INFO, "Steps that are not displayed have the value of their predecessor"); 27 | info.put(JSONKeys.JSON_KEY_JSON_RESULT_START_STEP, 0); 28 | info.put(JSONKeys.JSON_KEY_JSON_RESULT_END_STEP, rows.size() - 1); 29 | JSONArray data = new JSONArray(); 30 | 31 | double lastSavedRowCurrent = Double.MAX_VALUE; 32 | for (Row row : rows) 33 | { 34 | if(row.current != lastSavedRowCurrent) 35 | { 36 | JSONObject rowData = new JSONObject(); 37 | 38 | rowData.put(JSONKeys.JSON_KEY_JSON_RESULT_STEP, row.step); 39 | rowData.put(JSONKeys.JSON_KEY_JSON_RESULT_CURRENT, row.current); 40 | 41 | data.add(rowData); 42 | lastSavedRowCurrent = row.current; 43 | } 44 | } 45 | 46 | info.put(JSONKeys.JSON_KEY_JSON_RESULT_DATA, data); 47 | 48 | return info; 49 | } 50 | 51 | @Override 52 | // Ignore 53 | public void callbackBeforeUpdate(State state) 54 | { 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/forecasting2018_11/PerformanceMeasurementForecasting2018_11.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement.forecasting2018_11; 2 | 3 | import com.sap.charging.util.Util; 4 | import com.sap.charging.util.performanceMeasurement.PerformanceMeasurement; 5 | import com.sap.charging.util.sqlite.SQLiteAttributeKey; 6 | 7 | public class PerformanceMeasurementForecasting2018_11 extends PerformanceMeasurement { 8 | 9 | @SQLiteAttributeKey 10 | public final int nCars; 11 | 12 | @SQLiteAttributeKey 13 | public final int nChargingStations; 14 | 15 | @SQLiteAttributeKey 16 | public final String forecastingMethod; 17 | 18 | @SQLiteAttributeKey 19 | public final int seed; 20 | 21 | public PerformanceMeasurementForecasting2018_11() { 22 | this (null, null, null, -1, -1, null, -1); 23 | } 24 | 25 | public PerformanceMeasurementForecasting2018_11(int nCars, int nChargingStations, String forecastingMethod, int seed) { 26 | this(null, null, null, nCars, nChargingStations, forecastingMethod, seed); 27 | } 28 | 29 | public PerformanceMeasurementForecasting2018_11(String guid, String filePath, String method, int nCars, int nChargingStations, String forecastingMethod, int seed) { 30 | super(guid, filePath, method); 31 | this.nCars = nCars; 32 | this.nChargingStations = nChargingStations; 33 | this.forecastingMethod = forecastingMethod; 34 | this.seed = seed; 35 | } 36 | 37 | 38 | @Override 39 | public PerformanceMeasurementForecasting2018_11 cloneWithMethod(String method) { 40 | String guid = Util.generateGUID(); 41 | String filePath = "gen/performance/" + guid + ".json"; 42 | return new PerformanceMeasurementForecasting2018_11(guid, filePath, method, nCars, nChargingStations, forecastingMethod, seed); 43 | } 44 | 45 | @Override 46 | public String getTableName() { 47 | return "performanceMeasurementsForecasting2018_11"; 48 | } 49 | 50 | 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/paperINDIN2018/AppComparisonPerformanceINDIN2018.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement.paperINDIN2018; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.sap.charging.sim.Simulation; 7 | import com.sap.charging.util.configuration.Options; 8 | 9 | public class AppComparisonPerformanceINDIN2018 { 10 | 11 | public static void main(String[] args) { 12 | Simulation.verbosity = 1; 13 | 14 | int nThreads = 30; 15 | System.out.println("AppComparisonPerformanceINDIN2018::main Init with nThreads=" + nThreads + "..."); 16 | Options.set(24, 15, 0.85); 17 | 18 | List measurementJobs = new ArrayList<>(); 19 | // Fill list of jobs 20 | 21 | //int[] nCarsParams = new int[]{}; // 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 22 | List nCarsParams = new ArrayList<>(); 23 | for (int i=2100;i<=10000;i=i+100) { 24 | nCarsParams.add(i); 25 | } 26 | double[] proportionEVsParams = new double[]{0.75}; 27 | double[] gridConnectionParams = new double[]{1450}; 28 | 29 | for (int nCars : nCarsParams) { 30 | for (double proportionEVs : proportionEVsParams) { 31 | for (double gridConnection : gridConnectionParams) { 32 | measurementJobs.add(new PerformanceMeasurementINDIN2018Template(nCars, proportionEVs, gridConnection)); 33 | } 34 | } 35 | } 36 | //measurementJobs.add(new PerformanceMeasurementINDIN2018Template(100, 0.75, 4000.2)); 37 | 38 | // Stream jobs in parallel 39 | System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "" + nThreads); 40 | DBINDIN2018 db = new DBINDIN2018(); 41 | //measurementJobs.stream().forEach(new MeasurementExecutor(db)); 42 | //measurementJobs.stream().forEach(new MeasurementExecutorINDIN2018(db)); 43 | measurementJobs.parallelStream().forEach(new MeasurementExecutorINDIN2018(db)); 44 | 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/AppProblemInstanceExportJSON.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging; 2 | 3 | import com.sap.charging.dataGeneration.DataGeneratorRandom; 4 | import com.sap.charging.opt.CONSTANTS; 5 | import com.sap.charging.realTime.Strategy; 6 | import com.sap.charging.realTime.StrategyGreedy; 7 | import com.sap.charging.sim.Simulation; 8 | import com.sap.charging.util.FileIO; 9 | import com.sap.charging.util.random.Distribution; 10 | import com.sap.charging.util.random.NormalDistribution; 11 | 12 | public class AppProblemInstanceExportJSON { 13 | 14 | public static void main(String[] args) { 15 | Simulation.verbosity = 1; 16 | 17 | int seed = 0; 18 | 19 | double fuseSize = 1000000; 20 | 21 | CONSTANTS.FUSE_LEVEL_0_SIZE = fuseSize; 22 | CONSTANTS.FUSE_LEVEL_1_SIZE = fuseSize; 23 | CONSTANTS.FUSE_LEVEL_2_SIZE = fuseSize; 24 | 25 | 26 | 27 | DataGeneratorRandom data = new DataGeneratorRandom(seed, false); 28 | 29 | data.setIdealCars(true); 30 | data.setIdealChargingStations(true); 31 | 32 | Distribution normalDistribution = new NormalDistribution(data.getRandom(), 0.2, 0.2/3); 33 | data.setCurCapacityDistribution(normalDistribution); 34 | 35 | 36 | data.generateEnergyPriceHistory(96) 37 | .generateCars(20000) 38 | .generateChargingStations(100) 39 | .generateFuseTree(50, true); 40 | 41 | double[] energyPrices = data.getEnergyPriceHistory().getPrices(); 42 | for (int i=0;i>> 32)); 37 | return result; 38 | } 39 | 40 | @Override 41 | public boolean equals(Object obj) { 42 | if (this == obj) 43 | return true; 44 | if (obj == null) 45 | return false; 46 | if (getClass() != obj.getClass()) 47 | return false; 48 | PlannedCapacityKey other = (PlannedCapacityKey) obj; 49 | if (!Arrays.equals(currentPlan, other.currentPlan)) 50 | return false; 51 | if (currentTimeSeconds != other.currentTimeSeconds) 52 | return false; 53 | if (intervalSecondsEnd != other.intervalSecondsEnd) 54 | return false; 55 | if (intervalSecondsStart != other.intervalSecondsStart) 56 | return false; 57 | if (Double.doubleToLongBits(soc) != Double.doubleToLongBits(other.soc)) 58 | return false; 59 | return true; 60 | } 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/sim/util/SimulationListenerOutputData.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.sim.util; 2 | 3 | import java.util.ArrayList; 4 | 5 | import com.sap.charging.realTime.State; 6 | 7 | public class SimulationListenerOutputData implements SimulationListener 8 | { 9 | 10 | protected class Row { 11 | final int step; 12 | final double current; 13 | final double currentPlanLimit; 14 | //final double power; 15 | 16 | 17 | Row(int second, double current, double currentPlanLimit) { 18 | this.step = second; 19 | this.current = current; 20 | this.currentPlanLimit = currentPlanLimit; 21 | //this.power = power; 22 | } 23 | } 24 | 25 | protected ArrayList rows; 26 | 27 | public SimulationListenerOutputData() 28 | { 29 | this.rows = new ArrayList<>(); 30 | } 31 | 32 | @Override 33 | /** 34 | * Records per second the current amount of aggregated energy 35 | * in kW 36 | */ 37 | public void callbackAfterUpdate(State state) 38 | { 39 | double aggregatedCurrent = state.getCurrentPowerAssignments().stream() 40 | .mapToDouble(p -> p.getPhase1()+p.getPhase2()+p.getPhase3()) 41 | .sum(); 42 | //double aggregatedPower = EnergyUtil.calculatePFromI(aggregatedCurrent, 1); 43 | double aggregatedCurrentPlanLimit = state.getCurrentPowerAssignments().stream() 44 | .map(powerAssignment -> powerAssignment.car) 45 | .mapToDouble(car -> car.getCurrentPlan() != null ? 46 | car.getCurrentPlan()[state.currentTimeslot] * car.sumUsedPhases : 47 | car.maxCurrent) 48 | .sum(); 49 | 50 | Row row = new Row(state.currentTimeSeconds, aggregatedCurrent, aggregatedCurrentPlanLimit); 51 | this.rows.add(row); 52 | } 53 | 54 | @Override 55 | // Ignore 56 | public void callbackBeforeUpdate(State state) {} 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/realTime/model/forecasting/departure/CarDepartureForecastNN_CarSample.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.model.forecasting.departure; 2 | 3 | import org.rosuda.REngine.REXP; 4 | import org.rosuda.REngine.REXPMismatchException; 5 | 6 | import com.sap.charging.model.Car; 7 | import com.sap.charging.realTime.State; 8 | import com.sap.charging.util.r.RConnector; 9 | 10 | public class CarDepartureForecastNN_CarSample extends CarDepartureForecast { 11 | 12 | private RConnector rConnector; 13 | 14 | public CarDepartureForecastNN_CarSample(String modelFilePath) { 15 | rConnector = new RConnector(); 16 | String rString = "library(keras)\n" + 17 | "modelNN <- load_model_hdf5('" + modelFilePath + "')\n"; 18 | rString = rConnector.addRTryCtach(rString); 19 | rConnector.evalRString(rString); 20 | log(3, "Executed rString:"); 21 | log(3, rString); 22 | 23 | log(1, "NN model loaded with path=" + modelFilePath); 24 | 25 | } 26 | 27 | @Override 28 | public int getExpectedDepartureTimeSeconds(State state, Car car) { 29 | String matrixString = getRMatrixString(car); 30 | 31 | String rString = rConnector.addRTryCtach("\tpredict(modelNN, " + matrixString + ")[1,1]\n"); 32 | 33 | log(2, "Executing r String: "); 34 | log(2, rString); 35 | REXP result = rConnector.evalRString(rString); 36 | if (result.isNumeric()) { 37 | try { 38 | int resultTimestamp = (int) result.asDouble(); 39 | if (resultTimestamp <= 0) { 40 | resultTimestamp = 1; 41 | } 42 | return resultTimestamp; 43 | } catch (REXPMismatchException e) { 44 | e.printStackTrace(); 45 | } 46 | } 47 | else { 48 | System.out.println(result.toDebugString()); 49 | throw new RuntimeException("Error executing NN prediction"); 50 | } 51 | return -1; 52 | } 53 | 54 | 55 | 56 | 57 | @Override 58 | @Deprecated 59 | public int getExpectedDepartureTimeslot(State state, Car car) { 60 | return -1; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/server/api/v1/store/OptimizerSettingsTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.api.v1.store; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 4 | import static org.junit.jupiter.api.Assertions.assertThrows; 5 | import static org.junit.jupiter.params.provider.Arguments.arguments; 6 | import java.util.stream.Stream; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.DisplayName; 9 | import org.junit.jupiter.params.ParameterizedTest; 10 | import org.junit.jupiter.params.provider.Arguments; 11 | import org.junit.jupiter.params.provider.MethodSource; 12 | import com.fasterxml.jackson.databind.ObjectMapper; 13 | import com.fasterxml.jackson.databind.exc.ValueInstantiationException; 14 | 15 | class OptimizerSettingsTest { 16 | 17 | private ObjectMapper objectMapper; 18 | 19 | @BeforeEach 20 | public void setup() { 21 | this.objectMapper = new ObjectMapper(); 22 | } 23 | 24 | private static Stream deserializeOptimizerSettingsValues() { 25 | return Stream.of( 26 | arguments("Null value", "{\"weightObjectiveFairShare\": null}", false), 27 | arguments("Negative value", "{\"weightObjectiveFairShare\": -1}", true), 28 | arguments("Valid JSON object (zero weight)", "{\"weightObjectiveFairShare\": 0}", false), 29 | arguments("Valid JSON object (positive weight)", "{\"weightObjectiveFairShare\": 1}", false)); 30 | } 31 | 32 | @ParameterizedTest 33 | @MethodSource("deserializeOptimizerSettingsValues") 34 | @DisplayName("Build strategy based on incoming API request") 35 | void deserializeOptimizerSettings(String name, String json, boolean throwsException) { 36 | if (throwsException) { 37 | assertThrows(ValueInstantiationException.class, () -> objectMapper.readValue(json, OptimizerSettings.class)); 38 | } else { 39 | assertDoesNotThrow(() -> objectMapper.readValue(json, OptimizerSettings.class)); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/dataGeneration/DataRandomizerTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.dataGeneration; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import com.sap.charging.dataGeneration.common.DefaultDataGenerator; 9 | import com.sap.charging.model.Car; 10 | import com.sap.charging.sim.Simulation; 11 | 12 | 13 | public class DataRandomizerTest { 14 | 15 | DataGenerator dataOriginal; 16 | 17 | @BeforeEach 18 | public void setup() { 19 | Simulation.verbosity = 0; 20 | dataOriginal = DefaultDataGenerator.getDefaultDataGenerator(); 21 | } 22 | 23 | 24 | @Test 25 | public void testSeed() { 26 | int seed = 0; 27 | 28 | //DataGeneratorReal dataOriginal = new DataGeneratorReal("2016-12-01", seed, true, true); 29 | //dataOriginal.setIdealCars(true); 30 | //dataOriginal.setIdealChargingStations(true); 31 | //dataOriginal.generateAll(); 32 | 33 | DataGenerator dataClone1 = dataOriginal.clone(); 34 | DataGenerator dataClone2 = dataOriginal.clone(); 35 | for (int n=0;n> getSortedTimeslots(State state, int minK, int maxK, TimeslotSortingCriteria sortingCriteria) { 15 | return getSortedTimeslots(state, minK, maxK, sortingCriteria, null); 16 | } 17 | 18 | public static List> getSortedTimeslots(State state, int minK, int maxK, 19 | TimeslotSortingCriteria sortingCriteria, boolean[] blockedTimeslots) { 20 | 21 | List> sortedTimeslots = new ArrayList<>(); 22 | for (int k=minK;k(k, value)); 26 | } 27 | } 28 | Collections.sort(sortedTimeslots); 29 | return sortedTimeslots; 30 | } 31 | 32 | public static double getTimeslotValue(State state, int k, TimeslotSortingCriteria sortingCriteria) { 33 | switch (sortingCriteria) { 34 | case INDEX: 35 | return k; 36 | case PRICE: 37 | return state.energyPriceHistory.getPrice(k); 38 | case PEAK_DEMAND: 39 | double sumPlanned = 0; 40 | for (Car car : state.cars) { 41 | if (car.getCurrentPlan() != null) 42 | sumPlanned += car.sumUsedPhases * car.getCurrentPlan()[k]; 43 | } 44 | return sumPlanned; 45 | default: 46 | throw new RuntimeException("NOT IMPLEMENTED"); 47 | } 48 | } 49 | 50 | public static List> getSortedTimeslotsByIndex(int minK, int maxK) { 51 | return TimeslotSorter.getSortedTimeslots(null, minK, maxK, TimeslotSortingCriteria.INDEX); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/resources/testCasesJSON/StateStoreTest_CarDepartureTime_IsNotSet.json: -------------------------------------------------------------------------------- 1 | { 2 | "event": { 3 | "eventType": "Reoptimize" 4 | }, 5 | "state": { 6 | "currentTimeSeconds": 1, 7 | "cars": [ 8 | { 9 | "id": 0, 10 | "canLoadPhase1": 1, 11 | "canLoadPhase2": 1, 12 | "canLoadPhase3": 1, 13 | "carType": "BEV", 14 | "maxCapacity": 10, 15 | "minLoadingState": 5, 16 | "startCapacity": 1, 17 | "minCurrent": 18, 18 | "minCurrentPerPhase": 6, 19 | "maxCurrent": 96, 20 | "maxCurrentPerPhase": 32, 21 | "timestampArrival": 0 22 | } 23 | ], 24 | "fuseTree": { 25 | "rootFuse": { 26 | "@type": "Fuse", 27 | "id": 0, 28 | "fusePhase1": 32, 29 | "fusePhase2": 32, 30 | "fusePhase3": 32, 31 | "children": [ 32 | { 33 | "@type": "ChargingStation", 34 | "id": 0, 35 | "fusePhase1": 32, 36 | "fusePhase2": 32, 37 | "fusePhase3": 32, 38 | "phaseToGrid": { 39 | "PHASE_1": "PHASE_1", 40 | "PHASE_2": "PHASE_2", 41 | "PHASE_3": "PHASE_3" 42 | }, 43 | "phaseToChargingStation": { 44 | "PHASE_1": "PHASE_1", 45 | "PHASE_2": "PHASE_2", 46 | "PHASE_3": "PHASE_3" 47 | } 48 | } 49 | ] 50 | } 51 | }, 52 | "carAssignments": [ 53 | { 54 | "carID": 0, 55 | "chargingStationID": 0 56 | } 57 | ] 58 | } 59 | } -------------------------------------------------------------------------------- /src/test/resources/testCasesJSON/StateStoreTest_SinglePhaseCar_StationWithPhaseRotation_SinglePhaseFuse.json: -------------------------------------------------------------------------------- 1 | { 2 | "event": { 3 | "eventType": "Reoptimize" 4 | }, 5 | "state": { 6 | "currentTimeSeconds": 43200, 7 | "cars": [ 8 | { 9 | "id": 0, 10 | "canLoadPhase1": 1, 11 | "canLoadPhase2": 0, 12 | "canLoadPhase3": 0, 13 | "timestampArrival": 28800, 14 | "carType": "BEV", 15 | "maxCapacity": 100, 16 | "minLoadingState": 50, 17 | "startCapacity": 10, 18 | "minCurrent": 6, 19 | "minCurrentPerPhase": 6, 20 | "maxCurrent": 32, 21 | "maxCurrentPerPhase": 32 22 | } 23 | ], 24 | "fuseTree": { 25 | "rootFuse": { 26 | "@type": "Fuse", 27 | "id": 0, 28 | "fusePhase1": 32, 29 | "fusePhase2": 0, 30 | "fusePhase3": 0, 31 | "children": [ 32 | { 33 | "@type": "ChargingStation", 34 | "id": 0, 35 | "fusePhase1": 32, 36 | "fusePhase2": 0, 37 | "fusePhase3": 0, 38 | "phaseToGrid": { 39 | "PHASE_1": "PHASE_2", 40 | "PHASE_2": "PHASE_3", 41 | "PHASE_3": "PHASE_1" 42 | }, 43 | "phaseToChargingStation": { 44 | "PHASE_1": "PHASE_3", 45 | "PHASE_2": "PHASE_1", 46 | "PHASE_3": "PHASE_2" 47 | } 48 | } 49 | ] 50 | } 51 | }, 52 | "carAssignments": [ 53 | { 54 | "carID": 0, 55 | "chargingStationID": 0 56 | } 57 | ] 58 | } 59 | } -------------------------------------------------------------------------------- /src/test/resources/testCasesJSON/StateStoreTest_CarDepartureTime_IsSet.json: -------------------------------------------------------------------------------- 1 | { 2 | "event": { 3 | "eventType": "Reoptimize" 4 | }, 5 | "state": { 6 | "currentTimeSeconds": 1, 7 | "cars": [ 8 | { 9 | "id": 0, 10 | "canLoadPhase1": 1, 11 | "canLoadPhase2": 1, 12 | "canLoadPhase3": 1, 13 | "carType": "BEV", 14 | "maxCapacity": 10, 15 | "minLoadingState": 5, 16 | "startCapacity": 1, 17 | "minCurrent": 18, 18 | "minCurrentPerPhase": 6, 19 | "maxCurrent": 96, 20 | "maxCurrentPerPhase": 32, 21 | "timestampArrival": 0, 22 | "timestampDeparture": 3600 23 | } 24 | ], 25 | "fuseTree": { 26 | "rootFuse": { 27 | "@type": "Fuse", 28 | "id": 0, 29 | "fusePhase1": 32, 30 | "fusePhase2": 32, 31 | "fusePhase3": 32, 32 | "children": [ 33 | { 34 | "@type": "ChargingStation", 35 | "id": 0, 36 | "fusePhase1": 32, 37 | "fusePhase2": 32, 38 | "fusePhase3": 32, 39 | "phaseToGrid": { 40 | "PHASE_1": "PHASE_1", 41 | "PHASE_2": "PHASE_2", 42 | "PHASE_3": "PHASE_3" 43 | }, 44 | "phaseToChargingStation": { 45 | "PHASE_1": "PHASE_1", 46 | "PHASE_2": "PHASE_2", 47 | "PHASE_3": "PHASE_3" 48 | } 49 | } 50 | ] 51 | } 52 | }, 53 | "carAssignments": [ 54 | { 55 | "carID": 0, 56 | "chargingStationID": 0 57 | } 58 | ] 59 | } 60 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/Loggable.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | 5 | public interface Loggable { 6 | 7 | @JsonIgnore 8 | int getVerbosity(); 9 | 10 | default void println(String message) { 11 | System.out.println(message); 12 | } 13 | 14 | default void print(String message) { 15 | System.out.print(message); 16 | } 17 | 18 | default void printMessage(String message, boolean insertNewLine) { 19 | if (insertNewLine == true) { 20 | println(message); 21 | } 22 | else { 23 | print(message); 24 | } 25 | } 26 | 27 | default void printWithClassName(String message, boolean insertNewLine) { 28 | StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); 29 | String className = stackTraceElements[3].getClassName(); 30 | 31 | className = className.substring(className.lastIndexOf(".")+1); 32 | printMessage(className + "::" + stackTraceElements[3].getMethodName() + " " + message, insertNewLine); 33 | } 34 | 35 | /** 36 | * Default behaviour: If getVerbosity() >= minVerbosity, print the message (complete line) 37 | * @param minVerbosity 38 | * @param message 39 | */ 40 | default void log(int minVerbosity, String message) { 41 | if (getVerbosity() >= minVerbosity) { 42 | printWithClassName(message, true); 43 | } 44 | } 45 | 46 | default void log(int minVerbosity, String message, boolean showClassName) { 47 | if (getVerbosity() >= minVerbosity) { 48 | if (showClassName==false) { 49 | printMessage(message, true); 50 | } 51 | else if (showClassName==true) { 52 | printWithClassName(message, true); 53 | } 54 | } 55 | } 56 | 57 | default void log(int minVerbosity, String message, boolean showClassName, boolean insertNewLine) { 58 | if (getVerbosity() >= minVerbosity) { 59 | if (showClassName==false) { 60 | printMessage(message, insertNewLine); 61 | } 62 | else if (showClassName==true) { 63 | printWithClassName(message, insertNewLine); 64 | } 65 | } 66 | } 67 | 68 | default void log(int minVerbosity, Object message) { 69 | log(minVerbosity, message.toString()); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/opt/heuristic/InstanceHeuristicRelSoCTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.heuristic; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import java.util.List; 6 | 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Disabled; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import com.sap.charging.dataGeneration.DataGeneratorRandom; 12 | import com.sap.charging.model.Car; 13 | import com.sap.charging.opt.heuristics.InstanceHeuristicRelSoCLP; 14 | import com.sap.charging.opt.heuristics.util.CarAssignmentPriority; 15 | import com.sap.charging.opt.lp.InstanceLP; 16 | 17 | public class InstanceHeuristicRelSoCTest { 18 | 19 | @BeforeEach 20 | public void setup() { 21 | InstanceLP.verbosity = 0; 22 | } 23 | 24 | 25 | @Test 26 | public void testOrdering() { 27 | DataGeneratorRandom data = new DataGeneratorRandom(0, false); 28 | 29 | data.setIdealCars(true); 30 | data.setIdealChargingStations(true); 31 | 32 | data.generateEnergyPriceHistory(96) 33 | .generateCars(10) 34 | .generateChargingStations(5) 35 | .generateFuseTree(100, true); 36 | 37 | List sortedCarIDs = CarAssignmentPriority.sortCarIdByRelSoC(data.getCars()); 38 | 39 | double previousSoC = 0; 40 | for (int indexList=0;indexList0) { 46 | assertTrue(previousSoC <= soc); 47 | } 48 | 49 | previousSoC = soc; 50 | } 51 | } 52 | @Disabled 53 | @Test 54 | public void testCompleteExample() { 55 | DataGeneratorRandom data = new DataGeneratorRandom(0, false); 56 | 57 | data.setIdealCars(true); 58 | data.setIdealChargingStations(true); 59 | 60 | data.generateEnergyPriceHistory(96) 61 | .generateCars(10) 62 | .generateChargingStations(5) 63 | .generateFuseTree(100, true); 64 | 65 | InstanceHeuristicRelSoCLP instance = new InstanceHeuristicRelSoCLP(data); 66 | instance.constructProblem(); 67 | instance.getSolvedProblemInstanceJSON(); 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/AppExportCars.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging; 2 | 3 | import org.json.simple.JSONArray; 4 | 5 | import com.sap.charging.dataGeneration.DataGeneratorRandom; 6 | import com.sap.charging.model.Car; 7 | import com.sap.charging.opt.CONSTANTS; 8 | import com.sap.charging.util.FileIO; 9 | 10 | public class AppExportCars { 11 | 12 | private static String path = "gen/carsData/"; 13 | 14 | public static void main(String[] args) { 15 | exportRandomCars(); 16 | } 17 | 18 | @SuppressWarnings("unchecked") 19 | private static void exportRandomCars() { 20 | CONSTANTS.FUSE_LEVEL_0_SIZE = 4000; 21 | CONSTANTS.FUSE_LEVEL_1_SIZE = 4000; 22 | CONSTANTS.FUSE_LEVEL_2_SIZE = 4000; 23 | int seed = 0; 24 | 25 | for (int nCars=100;nCars<=2000;nCars=nCars+100) { 26 | DataGeneratorRandom data = new DataGeneratorRandom(seed, false); 27 | data.setIdealCars(true); 28 | data.setIdealChargingStations(true); 29 | data.generateEnergyPriceHistory(96) 30 | .generateCars(nCars); 31 | 32 | JSONArray carsArray = new JSONArray(); 33 | for (Car car : data.getCars()) { 34 | carsArray.add(car.toJSONObject()); 35 | } 36 | String filePath = path + "cars" + nCars + ".json"; 37 | FileIO.writeFile(filePath, carsArray.toJSONString()); 38 | } 39 | 40 | 41 | 42 | 43 | 44 | } 45 | 46 | /*private static void exportAllRealCars() { 47 | DataGeneratorRealDB dbDates = new DataGeneratorRealDB(); 48 | ArrayList dates = dbDates.retrieveAvailableDates(); 49 | JSONArray carsArrayAll = new JSONArray(); 50 | 51 | for (String date : dates) { 52 | DataGeneratorReal data = new DataGeneratorReal(date, 0, true, true); 53 | data.generateEnergyPriceHistory(-1).generateCars(-1); 54 | 55 | JSONArray carsArray = new JSONArray(); 56 | for (Car car : data.getCars()) { 57 | carsArray.add(car.toJSONObject()); 58 | carsArrayAll.add(car.toJSONObject()); 59 | } 60 | String filePath = path + date + ".json"; 61 | FileIO.writeFile(filePath, carsArray.toJSONString()); 62 | } 63 | FileIO.writeFile(path + "all.json", carsArrayAll.toJSONString()); 64 | } 65 | */ 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/model/ChargingStationFactory.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.model; 2 | 3 | import com.sap.charging.model.EnergyUtil.Phase; 4 | 5 | public class ChargingStationFactory { 6 | 7 | public enum Standard { 8 | /** 9 | * Currently the wallbox used at sap. Type 2, 1-phase=7.4kw, 3-phase=22kw 10 | */ 11 | KeContact_P30 { 12 | @Override 13 | public void applyToChargingStation(ChargingStation instance) { 14 | instance.fusePhase1 = 32; 15 | instance.fusePhase2 = 32; 16 | instance.fusePhase3 = 32; 17 | 18 | instance.isBEVAllowed = true; 19 | instance.isPHEVAllowed = true; 20 | } 21 | }; 22 | 23 | public abstract void applyToChargingStation(ChargingStation instance); 24 | } 25 | 26 | private ChargingStation instance; 27 | 28 | public ChargingStationFactory() { 29 | this.instance = new ChargingStation(); 30 | } 31 | 32 | public static ChargingStationFactory builder() { 33 | return new ChargingStationFactory(); 34 | } 35 | 36 | public ChargingStationFactory fusePhases(double fusePhase1, 37 | double fusePhase2, double fusePhase3) { 38 | instance.fusePhase1 = fusePhase1; 39 | instance.fusePhase2 = fusePhase2; 40 | instance.fusePhase3 = fusePhase3; 41 | return this; 42 | } 43 | 44 | public ChargingStationFactory isBEVAllowed(boolean allowed) { 45 | instance.isBEVAllowed = allowed; 46 | return this; 47 | } 48 | public ChargingStationFactory isPHEVAllowed(boolean allowed) { 49 | instance.isPHEVAllowed = allowed; 50 | return this; 51 | } 52 | 53 | public ChargingStationFactory buildFromStandard(Standard standard) { 54 | standard.applyToChargingStation(instance); 55 | return this; 56 | } 57 | 58 | public ChargingStationFactory setIndexI(int indexI) { 59 | this.instance.setID(indexI); 60 | return this; 61 | } 62 | 63 | public ChargingStationFactory setPhaseMatching(Phase phase1Consumed, Phase phase2Consumed, Phase phase3Consumed) { 64 | this.instance.setPhaseMatching(phase1Consumed, phase2Consumed, phase3Consumed); 65 | return this; 66 | } 67 | 68 | public ChargingStation build() { 69 | return instance; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/server/security/CustomWebSecurityConfigurerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.server.security; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 9 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 10 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 11 | import org.springframework.security.crypto.password.PasswordEncoder; 12 | 13 | @Configuration 14 | @EnableWebSecurity 15 | public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { 16 | 17 | // @Autowired private CustomBasicAuthenticationEntryPoint authenticationEntryPoint; 18 | 19 | @Autowired 20 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 21 | // Example of Basic authentication, if needed: 22 | // auth 23 | // .inMemoryAuthentication() 24 | // .withUser("user1") 25 | // .password(passwordEncoder().encode("BE4kpTZHkCpMMVj38zpj")) 26 | // .authorities("ROLE_USER"); 27 | } 28 | 29 | @Override 30 | protected void configure(HttpSecurity http) throws Exception { 31 | // Example of Basic authentication, if needed: 32 | http 33 | // .authorizeRequests() 34 | // .antMatchers("/actuator/*") 35 | // .permitAll() 36 | // .anyRequest() 37 | // .authenticated() 38 | // .and() 39 | // .httpBasic() 40 | // .authenticationEntryPoint(authenticationEntryPoint) 41 | // .and() 42 | .csrf().disable(); 43 | 44 | // http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class); 45 | } 46 | 47 | @Bean 48 | public PasswordEncoder passwordEncoder() { 49 | return new BCryptPasswordEncoder(); 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/paperJournal2018Time/PerformanceMeasurementPaperJournal2018Time.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement.paperJournal2018Time; 2 | 3 | import com.sap.charging.util.Util; 4 | import com.sap.charging.util.performanceMeasurement.PerformanceMeasurement; 5 | import com.sap.charging.util.sqlite.SQLiteAttributeKey; 6 | 7 | public class PerformanceMeasurementPaperJournal2018Time extends PerformanceMeasurement { 8 | 9 | 10 | @SQLiteAttributeKey 11 | public final int nCars; 12 | 13 | @SQLiteAttributeKey 14 | public final int nChargingStations; 15 | 16 | @SQLiteAttributeKey 17 | public final double randomness; 18 | 19 | @SQLiteAttributeKey 20 | public final String methodDayahead; 21 | 22 | @SQLiteAttributeKey 23 | public final String methodRealtime; 24 | 25 | @SQLiteAttributeKey 26 | public final int seed; 27 | 28 | public PerformanceMeasurementPaperJournal2018Time(int nCars, int nChargingStations, double randomness, String methodDayahead, String methodRealtime, int seed) { 29 | this(null, null, null, nCars, nChargingStations, randomness, methodDayahead, methodRealtime, seed); 30 | } 31 | 32 | public PerformanceMeasurementPaperJournal2018Time(String guid, String filePath, String method, int nCars, int nChargingStations, double randomness, String methodDayahead, String methodRealtime, int seed) { 33 | super(guid, filePath, method); 34 | this.nCars = nCars; 35 | this.nChargingStations = nChargingStations; 36 | this.randomness = randomness; 37 | this.methodDayahead = methodDayahead; 38 | this.methodRealtime = methodRealtime; 39 | this.seed = seed; 40 | } 41 | 42 | 43 | @Override 44 | public PerformanceMeasurementPaperJournal2018Time cloneWithMethod(String method) { 45 | String guid = Util.generateGUID(); 46 | String filePath = "gen/performance/" + guid + ".json"; 47 | return new PerformanceMeasurementPaperJournal2018Time(guid, filePath, method, nCars, nChargingStations, randomness, methodDayahead, methodRealtime, seed); 48 | } 49 | 50 | @Override 51 | public String getTableName() { 52 | return "performanceMeasurementsPaperJournal2018Time"; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/util/TimeUtilTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util; 2 | 3 | import java.time.LocalTime; 4 | 5 | import org.junit.Assert; 6 | import org.junit.jupiter.api.Test; 7 | 8 | 9 | import com.sap.charging.util.TimeUtil; 10 | 11 | public class TimeUtilTest { 12 | 13 | 14 | @Test 15 | public void testTimestampFromTimeslot() { 16 | LocalTime t1 = TimeUtil.getTimestampFromTimeslot(0); 17 | Assert.assertEquals("00:00", t1.toString()); 18 | 19 | LocalTime t2 = TimeUtil.getTimestampFromTimeslot(3); 20 | Assert.assertEquals("00:45", t2.toString()); 21 | 22 | LocalTime t3 = TimeUtil.getTimestampFromTimeslot(20); 23 | Assert.assertEquals("05:00", t3.toString()); 24 | 25 | LocalTime t4 = TimeUtil.getTimestampFromTimeslot(37); 26 | Assert.assertEquals("09:15", t4.toString()); 27 | 28 | } 29 | 30 | @Test 31 | public void testTimestampFromSeconds() { 32 | LocalTime t1 = TimeUtil.getTimestampFromSeconds(0); 33 | Assert.assertEquals("00:00", t1.toString()); 34 | 35 | LocalTime t2 = TimeUtil.getTimestampFromSeconds(10); 36 | Assert.assertEquals("00:00:10", t2.toString()); 37 | 38 | LocalTime t3 = TimeUtil.getTimestampFromSeconds(70); 39 | Assert.assertEquals("00:01:10", t3.toString()); 40 | 41 | LocalTime t4 = TimeUtil.getTimestampFromSeconds(3661); 42 | Assert.assertEquals("01:01:01", t4.toString()); 43 | } 44 | 45 | @Test 46 | public void testTimeslotFromSeconds() { 47 | int t1 = TimeUtil.getTimeslotFromSeconds(0); 48 | Assert.assertEquals(0, t1); 49 | 50 | int t2 = TimeUtil.getTimeslotFromSeconds(899); 51 | Assert.assertEquals(0, t2); 52 | 53 | int t3 = TimeUtil.getTimeslotFromSeconds(900); 54 | Assert.assertEquals(1, t3); 55 | 56 | int t4 = TimeUtil.getTimeslotFromSeconds(901); 57 | Assert.assertEquals(1, t4); 58 | 59 | int t5 = TimeUtil.getTimeslotFromSeconds(7200); 60 | Assert.assertEquals(8, t5); 61 | } 62 | 63 | @Test 64 | public void testTimeDiffInMS() { 65 | new TimeUtil(); 66 | long diff = TimeUtil.getDifferenceInMS(LocalTime.of(1, 0, 0), 67 | LocalTime.of(0, 0,0)); 68 | Assert.assertEquals(60*60*1000, diff); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/util/performanceMeasurement/paperJournal2018/PerformanceMeasurementPaperJournal2018.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.util.performanceMeasurement.paperJournal2018; 2 | 3 | import com.sap.charging.util.Util; 4 | import com.sap.charging.util.performanceMeasurement.PerformanceMeasurement; 5 | import com.sap.charging.util.sqlite.SQLiteAttributeKey; 6 | 7 | public class PerformanceMeasurementPaperJournal2018 extends PerformanceMeasurement { 8 | 9 | 10 | @SQLiteAttributeKey 11 | public final int nCars; 12 | 13 | @SQLiteAttributeKey 14 | public final int nChargingStations; 15 | 16 | @SQLiteAttributeKey 17 | public final double randomness; 18 | 19 | @SQLiteAttributeKey 20 | public final String methodDayahead; 21 | 22 | @SQLiteAttributeKey 23 | public final String methodRealtime; 24 | 25 | @SQLiteAttributeKey 26 | public final int seed; 27 | 28 | public PerformanceMeasurementPaperJournal2018(int nCars, int nChargingStations, double randomness, String methodDayahead, String methodRealtime, int seed) { 29 | this(null, null, null, nCars, nChargingStations, randomness, methodDayahead, methodRealtime, seed); 30 | } 31 | 32 | public PerformanceMeasurementPaperJournal2018(String guid, String filePath, String method, int nCars, int nChargingStations, double randomness, String methodDayahead, String methodRealtime, int seed) { 33 | super(guid, filePath, method); 34 | this.nCars = nCars; 35 | this.nChargingStations = nChargingStations; 36 | this.randomness = randomness; 37 | this.methodDayahead = methodDayahead; 38 | this.methodRealtime = methodRealtime; 39 | this.seed = seed; 40 | } 41 | 42 | 43 | @Override 44 | public PerformanceMeasurementPaperJournal2018 cloneWithMethod(String method) { 45 | String guid = Util.generateGUID(); 46 | String filePath = "gen/performance/" + guid + ".json"; 47 | return new PerformanceMeasurementPaperJournal2018(guid, filePath, method, nCars, nChargingStations, randomness, methodDayahead, methodRealtime, seed); 48 | } 49 | 50 | @Override 51 | public String getTableName() { 52 | return "performanceMeasurementsPaperJournal2018"; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/resources/testCasesJSON/StateStoreTest_ThreePhaseCar_SinglePhaseStationWithPhaseRotation.json: -------------------------------------------------------------------------------- 1 | { 2 | "event": { 3 | "eventType": "Reoptimize" 4 | }, 5 | "state": { 6 | "currentTimeSeconds": 43200, 7 | "cars": [ 8 | { 9 | "id": 0, 10 | "canLoadPhase1": 1, 11 | "canLoadPhase2": 1, 12 | "canLoadPhase3": 1, 13 | "timestampArrival": 28800, 14 | "carType": "BEV", 15 | "maxCapacity": 100, 16 | "minLoadingState": 50, 17 | "startCapacity": 10, 18 | "minCurrent": 18, 19 | "minCurrentPerPhase": 6, 20 | "maxCurrent": 96, 21 | "maxCurrentPerPhase": 32 22 | } 23 | ], 24 | "fuseTree": { 25 | "rootFuse": { 26 | "@type": "Fuse", 27 | "id": 0, 28 | "fusePhase1": 32, 29 | "fusePhase2": 32, 30 | "fusePhase3": 32, 31 | "children": [ 32 | { 33 | "@type": "ChargingStation", 34 | "id": 0, 35 | "fusePhase1": 32, 36 | "fusePhase2": 0, 37 | "fusePhase3": 0, 38 | "phase1Connected": true, 39 | "phase2Connected": false, 40 | "phase3Connected": false, 41 | "phaseToGrid": { 42 | "PHASE_1": "PHASE_2", 43 | "PHASE_2": "PHASE_3", 44 | "PHASE_3": "PHASE_1" 45 | }, 46 | "phaseToChargingStation": { 47 | "PHASE_1": "PHASE_3", 48 | "PHASE_2": "PHASE_1", 49 | "PHASE_3": "PHASE_2" 50 | } 51 | } 52 | ] 53 | } 54 | }, 55 | "carAssignments": [ 56 | { 57 | "carID": 0, 58 | "chargingStationID": 0 59 | } 60 | ] 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/opt/lp/Indexable.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.lp; 2 | 3 | import java.util.HashMap; 4 | import java.util.LinkedHashMap; 5 | 6 | public abstract class Indexable { 7 | 8 | /** 9 | * For example, P_{i,j,k} has three indices. 10 | * This HashMap stores them 11 | */ 12 | protected HashMap indices; 13 | 14 | private final String name; 15 | 16 | public Indexable(String name) { 17 | this.name = name; 18 | this.indices = new LinkedHashMap<>(); 19 | } 20 | 21 | /** 22 | * Returns only the name of the variable (e.g. "P" or "X") 23 | * @return 24 | */ 25 | public String getName() { 26 | return this.name; 27 | } 28 | 29 | /** 30 | * Returns the name of the variable with its indices (e.g. "P_i1_j2_k3" or "X_i1_n2") 31 | * Example: 32 | * @return 33 | */ 34 | public String getNameWithIndices() { 35 | if (this.indices.size() == 0) 36 | return this.name; 37 | 38 | String result = this.name + "_"; 39 | for (String index : indices.keySet()) { 40 | result += index + getIndex(index) + "_"; 41 | } 42 | result = result.substring(0,result.length()-1); // remove last "_" 43 | return result; 44 | } 45 | 46 | /** 47 | * Returns the name of the variable with its indices in a long form 48 | * (e.g. P_{i=1,j=2,k=3} or X_{i=1,n=2} 49 | */ 50 | public String getNameWithIndicesLong() { 51 | if (this.indices.size() == 0) 52 | return this.name; 53 | 54 | String result = this.name + "_{"; 55 | for (String index : indices.keySet()) { 56 | result += index + "=" + getIndex(index) + ","; 57 | } 58 | result = result.substring(0,result.length()-1); // remove last "," 59 | result += "}"; 60 | return result; 61 | } 62 | 63 | 64 | 65 | public Indexable setIndex(String indexName, int value) { 66 | this.indices.put(indexName, value); 67 | return this; 68 | } 69 | 70 | public HashMap getIndices() { 71 | return indices; 72 | } 73 | public int getIndex(String indexName) { 74 | if (this.indices.containsKey(indexName)) { 75 | return this.indices.get(indexName); 76 | } 77 | else { 78 | return -1; 79 | } 80 | 81 | } 82 | 83 | 84 | } 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/opt/heuristics/InstanceHeuristicLP.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.heuristics; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.json.simple.JSONObject; 7 | 8 | import com.sap.charging.model.Car; 9 | import com.sap.charging.model.ChargingStation; 10 | import com.sap.charging.model.EnergyPriceHistory; 11 | import com.sap.charging.model.FuseTree; 12 | import com.sap.charging.opt.lp.Equation; 13 | import com.sap.charging.opt.lp.InstanceLP; 14 | import com.sap.charging.opt.lp.util.SolverSCIP; 15 | 16 | public abstract class InstanceHeuristicLP extends InstanceHeuristic { 17 | 18 | public SolverSCIP solver = null; 19 | 20 | public InstanceHeuristicLP(List cars, List chargingStations, 21 | EnergyPriceHistory energyPriceHistory, FuseTree fuseTree) { 22 | super(cars, chargingStations, energyPriceHistory, fuseTree); 23 | solver = new SolverSCIP(); 24 | } 25 | 26 | protected InstanceLP instanceLP; 27 | protected boolean isProblemConstructed = false; 28 | public boolean scipApplyRelativeGapSetting = true; 29 | 30 | public InstanceLP getInstanceLP() { 31 | return this.instanceLP; 32 | } 33 | 34 | public abstract void prepareInstanceLP(); 35 | 36 | 37 | 38 | public void constructProblem() { 39 | this.prepareInstanceLP(); 40 | instanceLP.constructProblem(); 41 | this.isProblemConstructed = true; 42 | } 43 | public void constructProblem(ArrayList allRestrictions) { 44 | this.prepareInstanceLP(); 45 | instanceLP.constructProblem(allRestrictions); 46 | this.isProblemConstructed = true; 47 | } 48 | 49 | 50 | @Override 51 | public JSONObject getSolutionJSON() { 52 | if (this.isProblemConstructed == false) { 53 | throw new RuntimeException("ERROR: Call .constructProblem first!"); 54 | } 55 | 56 | if (scipApplyRelativeGapSetting == true) { 57 | solver.setApplyRelativeGapSetting(true); 58 | } 59 | 60 | instanceLP.setSolver(solver); 61 | instanceLP.solveProblem(); 62 | 63 | this.timeProblemConstruction.addTime(instanceLP.timeProblemConstruction.getTime()); 64 | this.timeSolution.addTime(instanceLP.timeSolution.getTime()); 65 | 66 | JSONObject solution = instanceLP.getSolutionJSON(); 67 | return solution; 68 | } 69 | 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /postman/emobility-smart-charging.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "192cde92-4b1b-47c6-9758-e2a4f56ad3e6", 4 | "name": "emobility-smart-charging", 5 | "description": "Collection of API requests for emobility-smart-charging", 6 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 7 | }, 8 | "item": [ 9 | { 10 | "name": "Health", 11 | "request": { 12 | "method": "GET", 13 | "header": [], 14 | "url": { 15 | "raw": "localhost:8080/actuator/health", 16 | "host": [ 17 | "localhost" 18 | ], 19 | "port": "8080", 20 | "path": [ 21 | "actuator", 22 | "health" 23 | ] 24 | }, 25 | "description": "Check if server is running" 26 | }, 27 | "response": [] 28 | }, 29 | { 30 | "name": "OptimizeChargingProfiles", 31 | "request": { 32 | "method": "POST", 33 | "header": [ 34 | { 35 | "key": "Content-Type", 36 | "name": "Content-Type", 37 | "value": "application/json", 38 | "type": "text" 39 | } 40 | ], 41 | "body": { 42 | "mode": "raw", 43 | "raw": "{\n\t\"state\": {\n\t\t\"currentTimeSeconds\": 85800,\n\t\t\"chargingStations\": [{\n\t\t\t\"id\": 4,\n\t\t\t\"fusePhase1\": 32,\n\t\t\t\"fusePhase2\": 32,\n\t\t\t\"fusePhase3\": 32\n\t\t}],\n\t\t\"cars\": [{\n\t\t\t\"id\": 3,\n\t\t\t\"carType\": \"BEV\", \n\t\t\t\"minCurrent\": 18,\n\t\t\t\"minCurrentPerPhase\": 6,\n\t\t\t\"maxCurrent\": 96,\n\t\t\t\"maxCurrentPerPhase\": 32,\n\t\t\t\"canLoadPhase1\": 1,\n\t\t\t\"canLoadPhase2\": 1,\n\t\t\t\"canLoadPhase3\": 1,\n\t\t\t\"maxCapacity\": 100,\n\t\t\t\"minLoadingState\": 50,\n\t\t\t\"startCapacity\": 10,\n\t\t\t\"timestampArrival\": 32400\n\t\t}],\n\t\t\"carAssignments\": [{\n\t\t\t\"carID\": 3,\n\t\t\t\"chargingStationID\": 4\n\t\t}], \n\t\t\"maximumSiteLimitKW\": 100,\n\t\t\"energyPriceHistory\": {\n\t\t\t\"numberOfTimeslots\": 96\n\t\t}\n\t},\n\t\"event\": {\n\t\t\"eventType\": \"Reoptimize\"\n\t}\n}\n\n\n" 44 | }, 45 | "url": { 46 | "raw": "localhost:8080/api/v1/OptimizeChargingProfiles", 47 | "host": [ 48 | "localhost" 49 | ], 50 | "port": "8080", 51 | "path": [ 52 | "api", 53 | "v1", 54 | "OptimizeChargingProfiles" 55 | ] 56 | } 57 | }, 58 | "response": [] 59 | } 60 | ] 61 | } -------------------------------------------------------------------------------- /src/test/java/com/sap/charging/realTime/util/TimeslotSorterTest.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.realTime.util; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.util.List; 7 | 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import com.sap.charging.dataGeneration.DataGenerator; 12 | import com.sap.charging.dataGeneration.DataGeneratorRandom; 13 | import com.sap.charging.realTime.StrategyAlgorithmic; 14 | import com.sap.charging.sim.Simulation; 15 | import com.sap.charging.util.SortableElement; 16 | 17 | public class TimeslotSorterTest { 18 | 19 | DataGenerator data; 20 | StrategyAlgorithmic strategy; 21 | Simulation sim; 22 | 23 | @BeforeEach 24 | public void setup() { 25 | Simulation.verbosity = 0; 26 | 27 | data = new DataGeneratorRandom(0, false); 28 | data.generateEnergyPriceHistory(96) 29 | .generateCars(20) 30 | .generateChargingStations(20) 31 | .generateFuseTree(20, true); 32 | 33 | strategy = new StrategyAlgorithmic(); 34 | sim = new Simulation(data, strategy); 35 | sim.init(); 36 | sim.simulateNextStep(); 37 | } 38 | 39 | @Test 40 | public void sortByPeakDemand() { 41 | 42 | data.getCar(0).setCurrentPlan(new double[96]); 43 | data.getCar(0).getCurrentPlan()[11] = 32; 44 | 45 | 46 | data.getCar(1).setCurrentPlan(new double[96]); 47 | data.getCar(1).getCurrentPlan()[9] = 32; 48 | data.getCar(1).getCurrentPlan()[11] = 32; 49 | data.getCar(1).getCurrentPlan()[15] = 32; 50 | 51 | List> result = TimeslotSorter.getSortedTimeslots(sim.getState(), 10, 20, TimeslotSortingCriteria.PEAK_DEMAND); 52 | assertEquals(10, result.get(0).index, 1e-8); 53 | assertEquals(15, result.get(8).index, 1e-8); 54 | assertEquals(11, result.get(9).index, 1e-8); 55 | 56 | } 57 | 58 | 59 | 60 | 61 | @Test 62 | public void sortByPrice() { 63 | List> result = TimeslotSorter.getSortedTimeslots(sim.getState(), 10, 20, TimeslotSortingCriteria.PRICE); 64 | 65 | assertTrue(result.get(0).value <= result.get(1).value); 66 | assertTrue(result.get(1).value <= result.get(2).value); 67 | } 68 | 69 | } 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /frontend/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "array-type": false, 5 | "arrow-parens": false, 6 | "deprecation": { 7 | "severity": "warning" 8 | }, 9 | "component-class-suffix": true, 10 | "contextual-lifecycle": true, 11 | "directive-class-suffix": true, 12 | "directive-selector": [ 13 | true, 14 | "attribute", 15 | "app", 16 | "camelCase" 17 | ], 18 | "component-selector": [ 19 | true, 20 | "element", 21 | "app", 22 | "kebab-case" 23 | ], 24 | "import-blacklist": [ 25 | true, 26 | "rxjs/Rx" 27 | ], 28 | "interface-name": false, 29 | "max-classes-per-file": false, 30 | "max-line-length": [ 31 | true, 32 | 140 33 | ], 34 | "member-access": false, 35 | "member-ordering": [ 36 | true, 37 | { 38 | "order": [ 39 | "static-field", 40 | "instance-field", 41 | "static-method", 42 | "instance-method" 43 | ] 44 | } 45 | ], 46 | "no-consecutive-blank-lines": false, 47 | "no-console": [ 48 | true, 49 | "debug", 50 | "info", 51 | "time", 52 | "timeEnd", 53 | "trace" 54 | ], 55 | "no-empty": false, 56 | "no-inferrable-types": [ 57 | true, 58 | "ignore-params" 59 | ], 60 | "no-non-null-assertion": true, 61 | "no-redundant-jsdoc": true, 62 | "no-switch-case-fall-through": true, 63 | "no-use-before-declare": true, 64 | "no-var-requires": false, 65 | "object-literal-key-quotes": [ 66 | true, 67 | "as-needed" 68 | ], 69 | "object-literal-sort-keys": false, 70 | "ordered-imports": false, 71 | "quotemark": [ 72 | true, 73 | "single" 74 | ], 75 | "trailing-comma": false, 76 | "no-conflicting-lifecycle": true, 77 | "no-host-metadata-property": true, 78 | "no-input-rename": true, 79 | "no-inputs-metadata-property": true, 80 | "no-output-native": true, 81 | "no-output-on-prefix": true, 82 | "no-output-rename": true, 83 | "no-outputs-metadata-property": true, 84 | "template-banana-in-box": true, 85 | "template-no-negated-async": true, 86 | "use-lifecycle-interface": true, 87 | "use-pipe-transform-interface": true 88 | }, 89 | "rulesDirectory": [ 90 | "codelyzer" 91 | ] 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/opt/lp/Equation.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.opt.lp; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class Equation extends Indexable { 6 | 7 | /** 8 | * Different constraint types and their strings in .mps and .lp CPLEX file formats. 9 | */ 10 | public enum ConstraintType { 11 | 12 | OBJ("N", ""), 13 | EQU("E", "="), 14 | GEQ("G", ">="), 15 | LEQ("L", "<="); 16 | 17 | final String mpsString; 18 | final String lpString; 19 | ConstraintType(String mpsString, String lpString) { 20 | this.mpsString = mpsString; 21 | this.lpString = lpString; 22 | } 23 | } 24 | 25 | 26 | 27 | public Equation(String name, ConstraintType constraintType) { 28 | super(name); 29 | this.constraintType = constraintType; 30 | } 31 | 32 | /** 33 | * LHS variables 34 | */ 35 | private ArrayList variables = new ArrayList<>(); 36 | 37 | /** 38 | * Constraint type: like in MPS file format 39 | * E equality 40 | * L less than or equal 41 | * G greater than or equal 42 | * N free row (first is used as objective function) 43 | * http://www.gurobi.com/documentation/7.5/refman/mps_format.html 44 | */ 45 | public final ConstraintType constraintType; 46 | 47 | /** 48 | * RHS Constant 49 | */ 50 | public double RHS = Double.MIN_VALUE; 51 | 52 | /** 53 | * Objective function does not need a constraintType and RHS 54 | */ 55 | public boolean isObjectiveFunction() { 56 | return constraintType == ConstraintType.OBJ; 57 | } 58 | 59 | public void cloneAndAddVariable(Variable variable, double coefficient) { 60 | Variable clonedVariable = variable.clone(); 61 | clonedVariable.setCoefficient(coefficient); 62 | variables.add(clonedVariable); 63 | } 64 | 65 | public ArrayList getVariables() { 66 | return variables; 67 | } 68 | 69 | public String toString() { 70 | String result = ""; 71 | 72 | if (isObjectiveFunction() == true) { 73 | result += "z="; 74 | } 75 | 76 | for (Variable v : variables) { 77 | result += v.toString(); 78 | } 79 | 80 | if (isObjectiveFunction() == false) { 81 | result += " " + constraintType.lpString + " " + RHS; 82 | } 83 | return result; 84 | } 85 | 86 | public boolean hasVariables() { 87 | return getVariables().size() > 0; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/sap/charging/model/FuseTreeNode.java: -------------------------------------------------------------------------------- 1 | package com.sap.charging.model; 2 | 3 | import static com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY; 4 | import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME; 5 | 6 | import java.util.ArrayList; 7 | 8 | import com.fasterxml.jackson.annotation.JsonIgnore; 9 | import com.fasterxml.jackson.annotation.JsonSubTypes; 10 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 11 | import com.sap.charging.model.EnergyUtil.Phase; 12 | import com.sap.charging.util.JSONSerializable; 13 | 14 | @JsonTypeInfo(use = NAME, include = PROPERTY) 15 | @JsonSubTypes({ 16 | @JsonSubTypes.Type(value = Fuse.class, name = "Fuse"), 17 | @JsonSubTypes.Type(value = ChargingStation.class, name = "ChargingStation") 18 | }) 19 | public interface FuseTreeNode extends JSONSerializable { 20 | 21 | public int getId(); 22 | 23 | public default boolean isPhaseConnected(Phase phase) { 24 | switch (phase) { 25 | case PHASE_1: return isPhase1Connected(); 26 | case PHASE_2: return isPhase2Connected(); 27 | case PHASE_3: return isPhase3Connected(); 28 | } 29 | throw new IllegalArgumentException("unknown phase=" + phase); 30 | } 31 | public boolean isPhaseAtGridConnectedInFuseTree(Phase phase); 32 | 33 | public boolean isPhase1Connected(); 34 | public boolean isPhase2Connected(); 35 | public boolean isPhase3Connected(); 36 | 37 | public double getFusePhase(Phase phase); 38 | 39 | public ArrayList getChildren(); 40 | 41 | @JsonIgnore 42 | public FuseTreeNode getParent(); 43 | 44 | public void setParent(FuseTreeNode parent); 45 | 46 | public boolean hasChildren(); 47 | 48 | /** 49 | * Valid states: 50 | * phase is connected and fuseSize = 0 51 | * phase is connected and fuseSize > 0 52 | * phase is not connected fuseSize = 0 53 | * Invalid state: 54 | * phase is NOT connected but fuseSize > 0 55 | */ 56 | public default void sanityCheckPhaseConnected() { 57 | for (Phase phase : Phase.values()) { 58 | if (isPhaseConnected(phase) == false && this.getFusePhase(phase) > 0) { 59 | throw new IllegalArgumentException("Error on fuseTreeNode id=" + this.getId() + ": Phase " + 60 | phase + " is not connected but fuseSize=" + this.getFusePhase(phase) + 61 | ". For consistency fuseSize should be 0 when phase is not connected."); 62 | } 63 | 64 | } 65 | 66 | } 67 | 68 | } 69 | 70 | 71 | --------------------------------------------------------------------------------