Skip to content

Commit

Permalink
feat: introducing SensorModule to provide access to environmental eve…
Browse files Browse the repository at this point in the history
…nts (eclipse#420)

* feat: introducing SensorModule to provide access to environmental events
* feat(application): allow registration of callback for lidar sensor module
* feat(application): re-use Perceptive interface for accessing new sensor modules
  • Loading branch information
kschrab authored and FunKuchen committed Nov 12, 2024
1 parent bf33487 commit c14ef1e
Show file tree
Hide file tree
Showing 14 changed files with 263 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public class EmergencyBrakeApp extends ConfigurableApplication<CEmergencyBrakeAp
@Override
public void onStartup() {
getOs().getAdHocModule().enable();
getOs().getBasicSensorModule().enable();
}

/**
Expand All @@ -67,7 +68,7 @@ public void onStartup() {
@Override
public void onVehicleUpdated(@Nullable VehicleData previousVehicleData, @Nonnull VehicleData updatedVehicleData) {

boolean obstacleDetected = getOs().getStateOfEnvironmentSensor(SensorType.OBSTACLE) > 0;
boolean obstacleDetected = getOs().getBasicSensorModule().getStrengthOf(SensorType.OBSTACLE) > 0;

// Initiate emergency brake if obstacle is detected
if (obstacleDetected && !emergencyBrake) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,13 @@ public class SlowDownApp extends AbstractApplication<VehicleOperatingSystem> imp

private boolean hazardousArea = false;

/**
* This method is used to request new data from the sensors and in that case
* react on the retrieved data.
* It is called at each simulation step when the vehicle info has been updated for
* the vehicle that has this application equipped.
*/
@Override
public void onVehicleUpdated(@Nullable VehicleData previousVehicleData, @Nonnull VehicleData updatedVehicleData) {
public void onStartup() {
getOs().getBasicSensorModule().enable();
}

// Enumeration of possible environment sensor types that are available in a vehicle
@Override
public void onVehicleUpdated(@Nullable VehicleData previousVehicleData, @Nonnull VehicleData updatedVehicleData) {
SensorType[] types = SensorType.values();

// Initialize sensor strength
Expand All @@ -63,7 +60,7 @@ public void onVehicleUpdated(@Nullable VehicleData previousVehicleData, @Nonnull
*/
for (SensorType currentType : types) {
// The strength of a detected sensor
strength = getOs().getStateOfEnvironmentSensor(currentType);
strength = getOs().getBasicSensorModule().getStrengthOf(currentType);

if (strength > 0) {
break;
Expand All @@ -81,19 +78,13 @@ public void onVehicleUpdated(@Nullable VehicleData previousVehicleData, @Nonnull
getOs().resetSpeed();
hazardousArea = false;
}

}

@Override
public void processEvent(Event event) throws Exception {

}

@Override
public void onStartup() {

}

@Override
public void onShutdown() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ public void onStartup() {
getLog().infoSimTime(this, "Activated AdHoc Module");
}

getOs().getBasicSensorModule().enable();

getOs().requestVehicleParametersUpdate()
.changeColor(Color.RED)
.apply();
Expand Down Expand Up @@ -140,28 +142,19 @@ public void onVehicleUpdated(@Nullable VehicleData previousVehicleData, @Nonnull
* This method is used to request new data from the sensors and, in case of new data, react on it.
*/
private void detectSensors() {
// Enumeration of possible environment sensor types that are available in a vehicle
SensorType[] types = SensorType.values();

// Initialize sensor type
SensorType type = null;
// Initialize sensor strength
int strength = 0;

/*
* The current strength of each environment sensor is examined here.
* If one is higher than zero, we reason that we are in a hazardous area with the
* given hazard.
*/
for (SensorType currentType : types) {
for (SensorType currentType : SensorType.values()) {

// The strength of a detected sensor
strength = getOs().getStateOfEnvironmentSensor(currentType);
int strength = getOs().getBasicSensorModule().getStrengthOf(currentType);

if (strength > 0) {
type = currentType;
// Method which is called to react on new or changed environment events
reactOnEnvironmentData(type, strength);
reactOnEnvironmentData(currentType, strength);
return; // the early exit discards other possible environmental warnings, ok for this tutorial purpose
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.mosaic.fed.application.ambassador.simulation.navigation.NavigationModule;
import org.eclipse.mosaic.fed.application.ambassador.util.UnitLogger;
import org.eclipse.mosaic.fed.application.app.api.os.VehicleOperatingSystem;
import org.eclipse.mosaic.fed.application.app.api.perception.BasicSensorModule;
import org.eclipse.mosaic.lib.enums.SensorType;
import org.eclipse.mosaic.lib.geo.GeoArea;
import org.eclipse.mosaic.lib.objects.addressing.AdHocMessageRoutingBuilder;
Expand Down Expand Up @@ -67,14 +68,19 @@ public class WeatherWarningAppTest {
@Mock
private AdHocModule adHocModuleMock;

@Mock
private BasicSensorModule sensorModuleMock;

@Before
public void setup() {
when(operatingSystem.getAdHocModule()).thenReturn(adHocModuleMock);
when(operatingSystem.getBasicSensorModule()).thenReturn(sensorModuleMock);

VehicleParameters.VehicleParametersChangeRequest vehicleParametersChangeRequestMock =
mock(VehicleParameters.VehicleParametersChangeRequest.class);
when(operatingSystem.requestVehicleParametersUpdate()).thenReturn(vehicleParametersChangeRequestMock);
when(vehicleParametersChangeRequestMock.changeColor(any())).thenReturn(vehicleParametersChangeRequestMock);

}

@Test
Expand Down Expand Up @@ -117,7 +123,7 @@ public void test_SwitchRouteIfDenmWasReceived() {
}

private void setSensor(SensorType sensorType, int value) {
when(operatingSystem.getStateOfEnvironmentSensor(same(sensorType))).thenReturn(value);
when(operatingSystem.getBasicSensorModule().getStrengthOf(same(sensorType))).thenReturn(value);
}

private void setupMessageRouting() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
import org.eclipse.mosaic.fed.application.ambassador.simulation.communication.ReceivedV2xMessage;
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;
import org.eclipse.mosaic.fed.application.ambassador.util.EventNicenessPriorityRegister;
import org.eclipse.mosaic.fed.application.app.api.MosaicApplication;
import org.eclipse.mosaic.fed.application.app.api.TrafficSignAwareApplication;
import org.eclipse.mosaic.fed.application.app.api.os.OperatingSystem;
import org.eclipse.mosaic.fed.application.app.api.os.modules.Perceptive;
import org.eclipse.mosaic.fed.application.config.CApplicationAmbassador;
import org.eclipse.mosaic.interactions.application.ApplicationInteraction;
import org.eclipse.mosaic.interactions.application.SumoTraciResponse;
Expand Down Expand Up @@ -510,26 +511,27 @@ private void process(final V2xFullMessageReception v2xFullMessageReception) {

/**
* This function does not directly fire an event, but puts it in a environmentEvents-map (see {@link AbstractSimulationUnit}).
* Use {@link OperatingSystem#getStateOfEnvironmentSensor} to determine the state of a Sensor. Keep in mind, that
* Use {@link Perceptive#getBasicSensorModule()} to determine the state of a Sensor. Keep in mind, that
* the map only stores the latest {@link EnvironmentEvent} of a specific type and overwrites old values.
* <p>Events will not directly be removed from the map, but since events are mapped to their type, there
* can't be more members than there are SensorType's. Nonetheless, the map can be cleared using
* {@link AbstractSimulationUnit#cleanPastEnvironmentEvents()}, which is also invoked by {@link SimulationKernel#garbageCollection()}.
* can't be more members than there are SensorType's. Nonetheless, the map is cleared using
* {@link EnvironmentBasicSensorModule#cleanPastEnvironmentEvents()}, which is invoked by {@link SimulationKernel#garbageCollection()}.
* </p>
*
* @param environmentSensorUpdates the Interaction of type EnvironmentSensorUpdates to be processed
*/
private void process(final EnvironmentSensorUpdates environmentSensorUpdates) {
// store the sensor data immediately, the sensor event hold their intermittent time
final AbstractSimulationUnit simulationUnit = UnitSimulator.UnitSimulator.getUnitFromId(environmentSensorUpdates.getUnitId());
// we don't simulate vehicles without an application
if (simulationUnit == null) {
// we don't simulate vehicles without application or correct environment sensor implementation
if (!(simulationUnit instanceof Perceptive sensible) ||
!(sensible.getBasicSensorModule() instanceof EnvironmentBasicSensorModule sensor)) {
return;
}
for (EnvironmentEvent event : environmentSensorUpdates.getEvents()) {
addEvent(new Event(
environmentSensorUpdates.getTime(),
e -> simulationUnit.putEnvironmentEvent(event.type, event))
e -> sensor.addEnvironmentEvent(event.type, event))
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import org.eclipse.mosaic.fed.application.ambassador.simulation.AbstractSimulationUnit;
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;
import org.eclipse.mosaic.fed.application.app.api.os.modules.Perceptive;
import org.eclipse.mosaic.fed.application.config.CApplicationAmbassador;
import org.eclipse.mosaic.interactions.communication.V2xMessageRemoval;
import org.eclipse.mosaic.lib.math.RandomNumberGenerator;
Expand Down Expand Up @@ -277,7 +279,7 @@ public Map<String, VehicleRoute> getRoutes() {
/**
* Registers a new route to the simulation kernel.
*
* @param id the id of the route
* @param id the id of the route
* @param route the {@link VehicleRoute} to register
*/
public void registerRoute(String id, VehicleRoute route) {
Expand Down Expand Up @@ -348,7 +350,10 @@ void garbageCollection() {
}
// clean past environment events
for (AbstractSimulationUnit simulationUnit : UnitSimulator.UnitSimulator.getAllUnits().values()) {
simulationUnit.cleanPastEnvironmentEvents();
if (simulationUnit instanceof Perceptive sensible &&
sensible.getBasicSensorModule() instanceof EnvironmentBasicSensorModule environmentSensor) {
environmentSensor.cleanPastEnvironmentEvents();
}
}

// is the garbage collection enabled?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@
import org.eclipse.mosaic.interactions.application.SumoTraciRequest;
import org.eclipse.mosaic.interactions.communication.V2xMessageAcknowledgement;
import org.eclipse.mosaic.interactions.communication.V2xMessageTransmission;
import org.eclipse.mosaic.lib.enums.SensorType;
import org.eclipse.mosaic.lib.geo.GeoPoint;
import org.eclipse.mosaic.lib.objects.environment.EnvironmentEvent;
import org.eclipse.mosaic.lib.objects.traffic.SumoTraciResult;
import org.eclipse.mosaic.lib.objects.v2x.V2xMessage;
import org.eclipse.mosaic.lib.util.scheduling.Event;
Expand All @@ -57,14 +55,9 @@
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nonnull;
Expand Down Expand Up @@ -98,16 +91,8 @@ public abstract class AbstractSimulationUnit implements EventProcessor, Operatin

private final EventInterceptor eventInterceptor;

/**
* Environment sensor data.
*/
private final Map<SensorType, EnvironmentEvent> environmentEvents = new HashMap<>();

private final AdHocModule adhocModule;

/**
* The {@link AbstractSimulationUnit}s cell module.
*/
private final CellModule cellModule;

/**
Expand Down Expand Up @@ -397,52 +382,6 @@ private void processV2xMessageAcknowledgement(final V2xMessageAcknowledgement v2

}

public EnvironmentEvent putEnvironmentEvent(SensorType type, EnvironmentEvent environmentEvent) {
return environmentEvents.put(type, environmentEvent);
}

/**
* The events are mapped into a map on the type. With multiple events to a
* same type, the last event is always taken. However, it should be part of
* good form to delete the event you no longer need to save some memory.
*/
public final void cleanPastEnvironmentEvents() {
// first, create a set to collect all sensor types, which should be deleted.
Set<SensorType> toRemove = new HashSet<>();

for (Entry<SensorType, EnvironmentEvent> entrySet : environmentEvents.entrySet()) {
SensorType type = entrySet.getKey();
EnvironmentEvent environmentEvent = entrySet.getValue();
// Is the event end time before the current simulation time?
if (environmentEvent.until < SimulationKernel.SimulationKernel.getCurrentSimulationTime()) {
// yes, mark the type to remove the event
toRemove.add(type);
}
}
// all events checked, now remove all the marked events
for (SensorType toRemoveType : toRemove) {
environmentEvents.remove(toRemoveType);
}
// the map should be clean now
}

@Override
public int getStateOfEnvironmentSensor(SensorType type) {
EnvironmentEvent event = environmentEvents.get(type);
// If an event of this type in the map yet?
if (event != null) {
// Is the event time window available at this simulation time?
if (
event.from <= SimulationKernel.SimulationKernel.getCurrentSimulationTime()
&& event.until >= SimulationKernel.SimulationKernel.getCurrentSimulationTime()
) {
return event.strength;
}
}

return 0;
}

@Override
public void triggerOnSendMessage(V2xMessageTransmission messageTransmission) {
for (CommunicationApplication application : getApplicationsIterator(CommunicationApplication.class)) {
Expand All @@ -465,12 +404,12 @@ final byte[] getAndResetUserTaggedValue() {
return tmp;
}

public AdHocModule getAdHocModule() {
return this.adhocModule;
public final AdHocModule getAdHocModule() {
return adhocModule;
}

public final CellModule getCellModule() {
return this.cellModule;
return cellModule;
}

void setRequiredOperatingSystem(Class<? extends OperatingSystem> operatingSystemCheck) {
Expand All @@ -484,7 +423,7 @@ public final List<Application> getApplications() {

@Override
public <A extends Application> Iterable<A> getApplicationsIterator(final Class<A> applicationClass) {
return new Iterable<A>() {
return new Iterable<>() {

@Nonnull
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import org.eclipse.mosaic.fed.application.ambassador.simulation.navigation.IRoutingModule;
import org.eclipse.mosaic.fed.application.ambassador.simulation.navigation.NavigationModule;
import org.eclipse.mosaic.fed.application.app.api.os.ServerOperatingSystem;
import org.eclipse.mosaic.lib.enums.SensorType;
import org.eclipse.mosaic.lib.objects.mapping.ServerMapping;
import org.eclipse.mosaic.lib.util.scheduling.Event;

Expand Down Expand Up @@ -58,11 +57,6 @@ public final CamBuilder assembleCamMessage(CamBuilder camBuilder) {
throw new UnsupportedOperationException("Servers can't send CAMs.");
}

@Override
public final int getStateOfEnvironmentSensor(SensorType type) {
throw new UnsupportedOperationException("Servers can't access Environment functionality.");
}

@Override
public IRoutingModule getRoutingModule() {
return routingModule;
Expand Down
Loading

0 comments on commit c14ef1e

Please sign in to comment.