Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(charging): provide the charging stations within a certain area #446

Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,5 @@ buildNumber.properties
*.log
credentials.cached
*.lcs
.tiles
.tiles
/logs/
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.eclipse.mosaic.fed.application.ambassador.simulation.TrafficLightGroupUnit;
import org.eclipse.mosaic.fed.application.ambassador.simulation.TrafficManagementCenterUnit;
import org.eclipse.mosaic.fed.application.ambassador.simulation.communication.ReceivedV2xMessage;
import org.eclipse.mosaic.fed.application.ambassador.simulation.electric.providers.ChargingStationIndex;
import org.eclipse.mosaic.fed.application.ambassador.simulation.navigation.CentralNavigationComponent;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.CentralPerceptionComponent;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.DefaultLidarSensorModule;
Expand Down Expand Up @@ -55,6 +56,7 @@
import org.eclipse.mosaic.interactions.traffic.VehicleUpdates;
import org.eclipse.mosaic.interactions.trafficsigns.VehicleSeenTrafficSignsUpdate;
import org.eclipse.mosaic.interactions.vehicle.VehicleRouteRegistration;
import org.eclipse.mosaic.lib.geo.GeoPoint;
import org.eclipse.mosaic.lib.objects.electricity.ChargingStationData;
import org.eclipse.mosaic.lib.objects.environment.EnvironmentEvent;
import org.eclipse.mosaic.lib.objects.traffic.InductionLoopInfo;
Expand Down Expand Up @@ -158,6 +160,12 @@ public ApplicationAmbassador(AmbassadorParameter ambassadorParameter) {
SimulationKernel.SimulationKernel.setCentralPerceptionComponent(centralPerceptionComponent);
}

if (SimulationKernel.SimulationKernel.chargingStationIndex == null) {
// use same bucketsize as TrafficLightTree (see: CPercetion.java) (bucketsize := number of direct children per tree node)
ChargingStationIndex chargingStationIndex = new ChargingStationIndex(20);
SimulationKernel.SimulationKernel.setChargingStationIndex(chargingStationIndex);
}

// add all application jar files
addJarFiles();
}
Expand Down Expand Up @@ -368,6 +376,9 @@ private void process(final ServerRegistration serverRegistration) {

private void process(final ChargingStationRegistration chargingStationRegistration) {
UnitSimulator.UnitSimulator.registerChargingStation(chargingStationRegistration);
String id = chargingStationRegistration.getMapping().getName();
GeoPoint position = chargingStationRegistration.getMapping().getPosition();
SimulationKernel.SimulationKernel.chargingStationIndex.addChargingStation(id, position);
}

private void process(final TrafficLightRegistration trafficLightRegistration) {
Expand Down Expand Up @@ -449,6 +460,8 @@ private void process(final ChargingStationUpdate chargingStationUpdate) {
final AbstractSimulationUnit simulationUnit =
UnitSimulator.UnitSimulator.getUnitFromId(chargingStationData.getName());

SimulationKernel.SimulationKernel.chargingStationIndex.updateChargingStation(chargingStationUpdate.getUpdatedChargingStation());

if (simulationUnit == null) {
return;
}
Expand All @@ -458,6 +471,7 @@ private void process(final ChargingStationUpdate chargingStationUpdate) {
chargingStationData,
EventNicenessPriorityRegister.UPDATE_CHARGING_STATION
);

addEvent(event);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public enum ErrorRegister {
SIMULATION_KERNEL_CentralNavigationComponentAlreadySet(0x01000029, "The CentralNavigationComponent was already set."),
SIMULATION_KERNEL_CentralPerceptionComponentNotSet(0x01000200, "The CentralPerceptionComponent was not set."),
SIMULATION_KERNEL_CentralPerceptionComponentAlreadySet(0x01000201, "The CentralPerceptionComponent was already set."),
SIMULATION_KERNEL_ChargingStationIndexAlreadySet(0x01000202, "The ChargingStationIndex was already set"),
SIMULATION_KERNEL_ChargingStationIndexNotSet(0x01000203, "The ChargingStationIndex was not set"),

// 0x01000030 to 0x0100003F unit simulator
UNIT_SIMULATOR_IdAlreadyAssigned(0x01000030, "The id is already assigned."),
UNIT_SIMULATOR_IdFromUnitIsNotInMap(0x01000031, "The unit with the id couldn't be found."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.eclipse.mosaic.fed.application.ambassador;

import org.eclipse.mosaic.fed.application.ambassador.simulation.AbstractSimulationUnit;
import org.eclipse.mosaic.fed.application.ambassador.simulation.electric.providers.ChargingStationIndex;
import org.eclipse.mosaic.fed.application.ambassador.simulation.navigation.CentralNavigationComponent;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.CentralPerceptionComponent;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.EnvironmentBasicSensorModule;
Expand Down Expand Up @@ -94,6 +95,8 @@ public enum SimulationKernel {
*/
transient CentralPerceptionComponent centralPerceptionComponent;

transient ChargingStationIndex chargingStationIndex;

/**
* Map containing all the routes with the corresponding edge-id's.
*/
Expand Down Expand Up @@ -259,6 +262,22 @@ public void setCentralPerceptionComponent(CentralPerceptionComponent centralPerc
this.centralPerceptionComponent = centralPerceptionComponent;
}

public void setChargingStationIndex(ChargingStationIndex chargingStationIndex) {
if (this.chargingStationIndex != null) {
throw new RuntimeException(ErrorRegister.SIMULATION_KERNEL_ChargingStationIndexAlreadySet.toString());
}

this.chargingStationIndex = chargingStationIndex;
}

public ChargingStationIndex getChargingStationIndex() {
if (this.chargingStationIndex == null) {
throw new RuntimeException(ErrorRegister.SIMULATION_KERNEL_ChargingStationIndexNotSet.toString());
}

return this.chargingStationIndex;
}

public CentralPerceptionComponent getCentralPerceptionComponent() {
if (centralPerceptionComponent == null) {
throw new RuntimeException(ErrorRegister.SIMULATION_KERNEL_CentralPerceptionComponentNotSet.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,21 @@
package org.eclipse.mosaic.fed.application.ambassador.simulation;

import org.eclipse.mosaic.fed.application.ambassador.SimulationKernel;
import org.eclipse.mosaic.fed.application.ambassador.simulation.electric.objects.ChargingStationObject;
import org.eclipse.mosaic.fed.application.app.api.ElectricVehicleApplication;
import org.eclipse.mosaic.fed.application.app.api.os.ElectricVehicleOperatingSystem;
import org.eclipse.mosaic.interactions.electricity.VehicleChargingDenial;
import org.eclipse.mosaic.interactions.electricity.VehicleChargingStartRequest;
import org.eclipse.mosaic.interactions.electricity.VehicleChargingStopRequest;
import org.eclipse.mosaic.lib.geo.GeoCircle;
import org.eclipse.mosaic.lib.geo.GeoPoint;
import org.eclipse.mosaic.lib.objects.electricity.ChargingStationData;
import org.eclipse.mosaic.lib.objects.vehicle.BatteryData;
import org.eclipse.mosaic.lib.objects.vehicle.VehicleType;

import java.util.List;
import java.util.stream.Collectors;

/**
* This class represents an electric vehicle in the application simulator. It extends {@link VehicleUnit}
* with further functionality.
Expand Down Expand Up @@ -103,4 +109,10 @@ public void sendChargingStopRequest() {
);
sendInteractionToRti(vehicleChargingStopRequest);
}

public List<ChargingStationData> getChargingStationsInArea(GeoCircle searchArea) {
return SimulationKernel.SimulationKernel.getChargingStationIndex().getChargingStationsInCircle(searchArea)
.stream().map(ChargingStationObject::getChargingStationData)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2025 Fraunhofer FOKUS and others. All rights reserved.
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contact: mosaic@fokus.fraunhofer.de
*/

package org.eclipse.mosaic.fed.application.ambassador.simulation.electric.objects;

import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.index.objects.PointBoundingBox;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.index.objects.SpatialObject;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.index.objects.SpatialObjectBoundingBox;
import org.eclipse.mosaic.lib.geo.CartesianPoint;
import org.eclipse.mosaic.lib.objects.electricity.ChargingStationData;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

public class ChargingStationObject extends SpatialObject<ChargingStationObject> {
/**
* The data object that stores all static and dynamic information of the charging station.
*/
private ChargingStationData chargingStationData;

/**
* The bounding box of a charging station is represented by a single point.
*/
private transient PointBoundingBox boundingBox;

public ChargingStationObject(String id) {
super(id);
}

public ChargingStationObject setChargingStationData(ChargingStationData chargingStationData) {
this.chargingStationData = chargingStationData;
return this;
}

public ChargingStationData getChargingStationData() {
return chargingStationData;
}

@Override
public ChargingStationObject setPosition(CartesianPoint position) {
cartesianPosition.set(position);
position.toVector3d(this);
return this;
}

@Override
public SpatialObjectBoundingBox getBoundingBox() {
if (boundingBox == null) {
boundingBox = new PointBoundingBox(this);
}
return boundingBox;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}

if (o == null || getClass() != o.getClass()) {
return false;
}

ChargingStationObject that = (ChargingStationObject) o;

return new EqualsBuilder()
.appendSuper(super.equals(o))
.append(chargingStationData, that.chargingStationData)
.append(boundingBox, that.boundingBox)
.isEquals();
}

@Override
public int hashCode() {
return new HashCodeBuilder(5, 11)
.appendSuper(super.hashCode())
.append(chargingStationData)
.toHashCode();
}

/**
* Returns a hard copy of the {@link ChargingStationObject}, this should be used
* when the data of a perceived traffic light is to be altered or stored in memory.
*
* @return a copy of the {@link ChargingStationObject}
*/
@Override
public ChargingStationObject copy() {
return new ChargingStationObject(getId())
.setChargingStationData(chargingStationData)
.setPosition(getProjectedPosition());

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2025 Fraunhofer FOKUS and others. All rights reserved.
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*
* Contact: mosaic@fokus.fraunhofer.de
*/

package org.eclipse.mosaic.fed.application.ambassador.simulation.electric.providers;

import org.eclipse.mosaic.fed.application.ambassador.simulation.electric.objects.ChargingStationObject;
import org.eclipse.mosaic.fed.application.ambassador.simulation.perception.index.objects.SpatialObjectAdapter;
import org.eclipse.mosaic.lib.geo.GeoCircle;
import org.eclipse.mosaic.lib.geo.GeoPoint;
import org.eclipse.mosaic.lib.objects.electricity.ChargingStationData;
import org.eclipse.mosaic.lib.spatial.KdTree;
import org.eclipse.mosaic.lib.spatial.SpatialTreeTraverser;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* A {@link ChargingStationIndex} holds Charging Stations in a tree structure, sorted by their position.
* The tree is initialized and gets updated lazily when a search is performed.
*/
public class ChargingStationIndex {
private final int bucketSize;

/**
* Stores {@link ChargingStationObject}s for fast removal and position update.
*/
final Map<String, ChargingStationObject> indexedChargingStations = new HashMap<>();

private KdTree<ChargingStationObject> chargingStationTree;

private SpatialTreeTraverser.InRadius<ChargingStationObject> treeTraverser;

private boolean needsTreeUpdate = false;

public ChargingStationIndex(int bucketSize) {
this.bucketSize = bucketSize;
}

/**
* Adds a Charging Station to the tree.
* Be sure to add {@link ChargingStationData} using updateChargingStation(ChargingStationData chargingStationData).
*
* The CS is inserted into the tree when it is queried (e.g. getChargingStationsInCircle(...) or getNumberOfChargingStations(...))
* @param id
* @param position
*/
public void addChargingStation(String id, GeoPoint position) {
if (chargingStationTree != null) {
throw new RuntimeException("ChargingStationTree has already been initialized, "
+ "make sure to add all objects before calling updateCharginStation() for the first time. ");
}

needsTreeUpdate = true;
indexedChargingStations.computeIfAbsent(id, ChargingStationObject::new)
.setPosition(position.toCartesian());
}

/**
* Replaces the stations data object.
*/
public void updateChargingStation(ChargingStationData chargingStationData) {
indexedChargingStations.get(chargingStationData.getName())
.setChargingStationData(chargingStationData);
}

private void updateSearchTree() {
if (!needsTreeUpdate) {
return;
}

List<ChargingStationObject> allChargingStations = new ArrayList<>(indexedChargingStations.values());
chargingStationTree = new KdTree<>(new SpatialObjectAdapter<>(), allChargingStations, bucketSize);
treeTraverser = new SpatialTreeTraverser.InRadius<>();
needsTreeUpdate = false;
}

public List<ChargingStationObject> getChargingStationsInCircle(GeoCircle circle) {
updateSearchTree();
treeTraverser.setup(circle.getCenter().toVector3d(), circle.getRadius());
treeTraverser.traverse(chargingStationTree);
return treeTraverser.getResult();
}

public int getNumberOfChargingStations() {
updateSearchTree();
return chargingStationTree.getRoot().size();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@

package org.eclipse.mosaic.fed.application.app.api.os;

import org.eclipse.mosaic.lib.geo.GeoCircle;
import org.eclipse.mosaic.lib.objects.electricity.ChargingStationData;
import org.eclipse.mosaic.lib.objects.vehicle.BatteryData;

import java.util.List;
import javax.annotation.Nullable;

/**
Expand Down Expand Up @@ -45,4 +48,11 @@ public interface ElectricVehicleOperatingSystem extends VehicleOperatingSystem {
* Sends a request to stop charging the battery of the vehicle.
*/
void sendChargingStopRequest();

/**
* Locate all charging stations in the provided area.
*
* @param searchArea The area where the charging stations are searched
*/
List<ChargingStationData> getChargingStationsInArea(GeoCircle searchArea);
}
Loading