Skip to content
New issue

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

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

Already on GitHub? # to your account

fix(application): complete VehicleRoute object if it is missing node ids or connection ids #472

Merged
merged 2 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,10 @@ public Map<String, VehicleRoute> getRoutes() {
* @param route the {@link VehicleRoute} to register
*/
public void registerRoute(String id, VehicleRoute route) {
routes.put(id, Validate.notNull(route, "The given route must not be null."));
VehicleRoute completedRoute = getCentralNavigationComponent().refineRoute(
Validate.notNull(route, "The given route must not be null.")
);
routes.put(id, completedRoute);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,17 @@ public IRoadPosition refineRoadPosition(IRoadPosition roadPosition) {
return vehicleRouting.refineRoadPosition(roadPosition);
}

/**
* This method completes a route definition if it is missing either
* a list of node ids or connection ids, but provides the other.
*
* @param route the potentially incomplete {@link VehicleRoute}
* @return the completed {@link VehicleRoute} with list of node ids and connection ids
*/
public VehicleRoute refineRoute(VehicleRoute route) {
return vehicleRouting.refineRoute(route);
}

/**
* Calculates the distance to a node given the current position
* along a given route.<br><br>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
Expand Down Expand Up @@ -145,6 +146,12 @@ public void setup() throws IllegalValueException {
recentAdvanceTime = 0;
}

@Before
public void setupRouteRefinement() {
when(SimulationKernel.SimulationKernel.getCentralNavigationComponent().refineRoute(isA(VehicleRoute.class)))
.thenAnswer(c -> c.getArguments()[0]);
}

@After
public void tearDown() {
TestUtils.setPrivateField(EtsiPayloadConfiguration.class, "globalConfiguration", null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import org.eclipse.mosaic.fed.application.ambassador.SimulationKernelRule;
import org.eclipse.mosaic.fed.application.config.CApplicationAmbassador;
import org.eclipse.mosaic.interactions.traffic.VehicleRoutesInitialization;
import org.eclipse.mosaic.interactions.vehicle.VehicleRouteChange;
Expand Down Expand Up @@ -61,6 +62,8 @@
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestRule;
import org.junit.runners.model.Statement;

import java.io.File;
import java.io.IOException;
Expand All @@ -73,9 +76,19 @@ public class CentralNavigationComponentTest {

private final TemporaryFolder folderRule = new TemporaryFolder();
private final CentralNavigationComponentTestRule navigationRule = new CentralNavigationComponentTestRule(folderRule);
private final TestRule lazySimKernelRule = (base, description) -> new Statement() {
@Override
public void evaluate() {
// we must initialize the sim-kernel rule only when navigation rule has been already initialized
// therefore we use this wrapping TestRule which inits sim-kernel rule on the fly
new SimulationKernelRule(
null, null, navigationRule.getCentralNavigationComponent(), null
).apply(base, description);
}
};

@Rule // chain both junit rules in a specific order
public RuleChain testRules = RuleChain.outerRule(folderRule).around(navigationRule);
public RuleChain testRules = RuleChain.outerRule(folderRule).around(navigationRule).around(lazySimKernelRule);

private CentralNavigationComponent cnc;

Expand Down Expand Up @@ -117,6 +130,8 @@ public void setup() throws IllegalRouteException {
when(routingMock.createRouteForRTI(argThat(
argument -> argument.getConnectionIds().equals(exampleRoute2.getConnectionIds())
))).thenReturn(exampleRoute2);

when(routingMock.refineRoute(isA(VehicleRoute.class))).thenAnswer(c -> c.getArguments()[0]);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ public interface VehicleRouting {
*/
IRoadPosition refineRoadPosition(IRoadPosition roadPosition);

/**
* This method completes a route definition if it is missing either
* a list of node ids or connection ids, but provides the other.
*
* @param route the potentially incomplete {@link VehicleRoute}
* @return the completed {@link VehicleRoute} with list of node ids and connection ids
*
* @throws IllegalStateException if the route contains neither node ids nor connection ids
*/
VehicleRoute refineRoute(VehicleRoute route);

/**
* Approximates the costs of a {@link CandidateRoute}. Pass a {@link CandidateRoute}
* and the ID of the last Node the vehicle passed on to the method and it will return a new
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -298,4 +299,58 @@ public Database getScenarioDatabase() {
public CartesianRectangle getScenarioBounds() {
return scenarioDatabase.getBoundingBox().toCartesian();
}

@Override
public VehicleRoute refineRoute(VehicleRoute route) {
if (!route.getNodeIds().isEmpty() && !route.getConnectionIds().isEmpty()) {
return route;
}

if (route.getConnectionIds().isEmpty() && !route.getNodeIds().isEmpty()) {
return createVehicleRouteFromNodeIds(route.getId(), route.getNodeIds());
}

if (route.getNodeIds().isEmpty() && !route.getConnectionIds().isEmpty()) {
return createVehicleRouteFromConnectionIds(route.getId(), route.getConnectionIds());
}

throw new IllegalStateException("A route must contain at least a list of connection ids or node ids");
}

private VehicleRoute createVehicleRouteFromNodeIds(String routeId, List<String> nodeIds) {
List<String> connectionIds = new ArrayList<>();
double length = 0;
Node prev = null;
for (String nodeId : nodeIds) {
Node curr = scenarioDatabase.getNode(nodeId);
if (prev == null) {
prev = curr;
continue;
}
for (Connection prevOutCon : prev.getOutgoingConnections()) {
if (curr.getIncomingConnections().contains(prevOutCon)) {
connectionIds.add(prevOutCon.getId());
length += prevOutCon.getLength();
prev = curr;
break;
}
}
}
return new VehicleRoute(routeId, connectionIds, nodeIds, length);
}

private VehicleRoute createVehicleRouteFromConnectionIds(String routeId, List<String> connectionIds) {
List<String> nodeIds = new ArrayList<>();
double length = 0;
for (String connectionId : connectionIds) {
Connection con = scenarioDatabase.getConnection(connectionId);
for (Node node : con.getNodes()) {
if (!node.getId().equals(Iterables.getLast(nodeIds, null))) {
nodeIds.add(node.getId());
}
}
length += con.getLength();
}
return new VehicleRoute(routeId, connectionIds, nodeIds, length);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public IRoadPosition refineRoadPosition(IRoadPosition roadPosition) {
return roadPosition;
}

@Override
public VehicleRoute refineRoute(VehicleRoute route) {
return route;
}

@Override
public CandidateRoute approximateCostsForCandidateRoute(CandidateRoute route, String lastNodeId) {
return route;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.eclipse.mosaic.lib.routing.config.CVehicleRouting;
import org.eclipse.mosaic.rti.api.InternalFederateException;

import com.google.common.collect.Lists;
import org.apache.commons.io.FileUtils;
import org.junit.Before;
import org.junit.Rule;
Expand Down Expand Up @@ -296,4 +297,30 @@ public void testApproximateCostsForCandidateRoute() {
assertEquals(505, approximatedCandidateRoute.getTime(), 0);
}

@Test
public void refineRoute_fillNodeIds() throws InternalFederateException {
configuration.source = "tiergarten.db";
databaseRouting.initialize(configuration, cfgDir);

VehicleRoute route = databaseRouting.refineRoute(new VehicleRoute("0",
Lists.newArrayList( "4068038_423839224_26704448", "36337928_26704448_27537750", "4609244_27537750_27537749"),
Lists.newArrayList(), 0));

assertEquals(Lists.newArrayList("423839224", "26704448", "27537750", "27537749"), route.getNodeIds());
assertEquals(85.92, route.getLength(), 0.1);
}

@Test
public void refineRoute_fillConnectionIds() throws InternalFederateException {
configuration.source = "tiergarten.db";
databaseRouting.initialize(configuration, cfgDir);

VehicleRoute route = databaseRouting.refineRoute(new VehicleRoute("0",
Lists.newArrayList(),
Lists.newArrayList("423839224", "26704448", "27537750", "27537749"), 0));

assertEquals(Lists.newArrayList( "4068038_423839224_26704448", "36337928_26704448_27537750", "4609244_27537750_27537749"), route.getConnectionIds());
assertEquals(85.92, route.getLength(), 0.1);
}

}