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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be necessary, please remove

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iwillitried yes, there is another .gitignore in mosaic-starter which ignores the logs directory. That should suffice and you can remove the /logs/ entry here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still not resolved!

/logs/
schwepmo marked this conversation as resolved.
Show resolved Hide resolved
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe define the 20 as a constant in the ChargingStationIndex, the way it defined right now is a magic number.

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use SimulationKernel.SimulationKernel.getChargingStationIndex() here

}

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());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use SimulationKernel.SimulationKernel.getChargingStationIndex() here.
Also why don't you just use the chargingStationData as input declared in line 459?


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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you add this blank line? This makes this method look different from all other in this file

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,21 @@ 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;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code style: missing blank line after this method.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this return ChargingStationObjects?

return SimulationKernel.SimulationKernel.getChargingStationIndex().getChargingStationsInCircle(searchArea)
.stream().map(ChargingStationObject::getChargingStationData)
.collect(Collectors.toList());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use toList() instead of .collect(Collectors.toList());

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

}
}
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: [email protected]
*/

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This field stores references to the objects contained within the ChargingStationUpdates, which is potentially dangerous. I believe we should copy the relevant information from the ChargingStationData and store it in these objects

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking into the ChargingStationData, I feel like we don't require it at all as we currently are just interested in the position of the charging stations. As far as I see we don't need any info on the charging spots?!


/**
* 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)
kschrab marked this conversation as resolved.
Show resolved Hide resolved
.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,87 @@
/*
* 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: [email protected]
*/

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;

public class ChargingStationIndex {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After merging Index and Tree, I think we should stick with naming "Tree". If I merge the two files from "Car extends Vehicle" then also what remains is rather a "Car" than a "Vehicle".


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;

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

/**
* Method called to initialize index after configuration has been read.
*/
public void initialize() {
// initialization at first update
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have this method if we don't do anything here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed it


public void addChargingStation(String id, GeoPoint position) {
indexedChargingStations.computeIfAbsent(id, ChargingStationObject::new)
.setPosition(position.toCartesian());
}

/**
* Perform action before an update of the {@link ChargingStationIndex} takes place.
*/
public void updateChargingStation(ChargingStationData chargingStationData) {
onChargingStationUpdate();
indexedChargingStations.get(chargingStationData.getName())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there actually anything we want to update about charging stations?

.setChargingStationData(chargingStationData);
}

public List<ChargingStationObject>getChargingStationsInCircle(GeoCircle circle) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code style: missing space before method name

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

treeTraverser.setup(circle.getCenter().toVector3d(), circle.getRadius());
treeTraverser.traverse(chargingStationTree);
return treeTraverser.getResult();
}

public void onChargingStationUpdate() {
if (chargingStationTree == null) { // initialize before first update is called
List<ChargingStationObject> allChargingStations = new ArrayList<>(indexedChargingStations.values());
chargingStationTree = new KdTree<>(new SpatialObjectAdapter<>(), allChargingStations, bucketSize);
treeTraverser = new SpatialTreeTraverser.InRadius<>();
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be public? I would make that private, rename it to initSearchTree() and move it right below updateChargingStation method where it is needed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


public int getNumberOfChargingStations() {
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this returning ChargingStationObjects?

}
Loading
Loading