Commit 0e1dc46e authored by Christina Maria Mayr's avatar Christina Maria Mayr
Browse files

[ControlModel] Handle conflicting and recurring information

parent 05e16522
......@@ -8,6 +8,7 @@ import org.vadere.state.scenario.Topography;
import org.vadere.util.logging.Logger;
import rx.Subscription;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.stream.Collectors;
......@@ -19,36 +20,70 @@ public abstract class ControlModel implements IControlModel {
public Double simTime;
private CtlCommand command;
protected ReactionModel reactionModel;
protected HashMap<Pedestrian,LinkedList<Integer>> processedAgents;
public ControlModel(){
simTime = 0.0;
this.reactionModel = new ReactionModel();
processedAgents = new HashMap<>();
}
public ControlModel(ReactionModel reactionModel){
simTime = 0.0;
this.reactionModel = reactionModel;
processedAgents = new HashMap<>();
}
public abstract boolean isPedReact();
protected abstract void triggerRedRaction(Pedestrian ped);
public void setProcessedAgents(Pedestrian ped, LinkedList<Integer> ids){
if (processedAgents.containsKey(ped)){
LinkedList<Integer> idsOld = processedAgents.get(ped);
ids.addAll(idsOld);
}
processedAgents.put(ped, ids);
}
public void setProcessedAgents(Pedestrian ped, int id){
LinkedList<Integer> ids = new LinkedList<>();
ids.add(id);
setProcessedAgents(ped,ids);
}
public abstract void applyPedControl(Pedestrian ped, JSONObject command);
public boolean isIdInList(Pedestrian ped, int id){
if (processedAgents.containsKey(ped)){
if (processedAgents.get(ped).contains(id)){
logger.info("Skip command, because agent with id=" + ped.getId() + " has already received commandId " + id);
}
return processedAgents.get(ped).contains(id);
}
return false;
}
public abstract void getControlAction(Pedestrian ped, JSONObject command);
public void update(Topography topo, Double time, String commandStr, Integer pedId) {
topography = topo;
simTime = time;
command = new CtlCommand(commandStr);
for (int i : get_pedIds(pedId)){
for (int i : get_pedIds(pedId)) {
Pedestrian ped = topography.getPedestrianDynamicElements().getElement(i);
if (isInfoInTime() && isPedInDefinedArea(ped)){
this.applyPedControl(ped, command.getPedCommand());
this.setAction(ped);
if (this.isInformationProcessed(ped, getCommandId())){
if (isInfoInTime() && isPedInDefinedArea(ped)) {
this.getControlAction(ped, command.getPedCommand());
this.setAction(ped);
this.setProcessedAgents(ped,getCommandId());
}
}
}
}
public void update(Topography topography, Double time, String command) {
......@@ -81,20 +116,43 @@ public abstract class ControlModel implements IControlModel {
return command.getExecTime() >= simTime;
}
public abstract boolean isPedReact();
public int getCommandId(){
return command.getCommandId();
}
public void setAction(Pedestrian ped){
if (isPedReact()){
triggerRedRaction(ped);
}
}
protected abstract void triggerRedRaction(Pedestrian ped);
public void setReactionModel(ReactionModel reactionModel){
this.reactionModel = reactionModel;
}
public boolean isInformationProcessed(Pedestrian ped, int commandId){
// 1. handle conflicting instructions over time
if (reactionModel.isReactingToFirstInformationOnly()){
return isFirstInformation(ped);
}
// 2. handle recurring information that is received multiple times.
if (isIdInList(ped, commandId)){
return reactionModel.isReactingToRecurringInformation();
}
return true;
}
public boolean isFirstInformation(Pedestrian ped){
if (processedAgents.containsKey(ped)) {
return processedAgents.get(ped).isEmpty();
}
return false;
}
}
......@@ -3,13 +3,17 @@ package org.vadere.simulator.control.external.models;
import org.json.JSONException;
import org.json.JSONObject;
import org.vadere.util.geometry.shapes.VCircle;
import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.util.geometry.shapes.VShape;
import java.time.temporal.ValueRange;
public class CtlCommand {
private int commandId = 0;
JSONObject rawCommand;
VCircle space;
VShape space;
Double time;
JSONObject pedCommand;
......@@ -39,26 +43,42 @@ public class CtlCommand {
return time;
}
public VCircle getSpace(){
public VShape getSpace(){
// only radius
VShape shape;
double x = 0;
double y = 0;
double radius = 0;
double height = 0;
double width = 0;
try {
JSONObject space = rawCommand.getJSONObject("space");
x = space.getDouble("x");
y = space.getDouble("y");
} catch (JSONException e) {
e.printStackTrace();
}
try {
JSONObject space = rawCommand.getJSONObject("space");
radius = space.getDouble("radius");
if (radius>=0) {
if (radius > 0) {
this.space = new VCircle(x, y, radius);
}
} catch (JSONException ignored) {}
try {
JSONObject space = rawCommand.getJSONObject("space");
height = space.getDouble("height");
width = space.getDouble("width");
if ((height > 0) && (width > 0)) {
this.space = new VRectangle(x, y, width, height);
}
} catch (JSONException ignored) {}
} catch (JSONException e) {
e.printStackTrace();
}
return this.space;
}
......@@ -69,7 +89,6 @@ public class CtlCommand {
public JSONObject getPedCommand(){
try{
pedCommand = rawCommand.getJSONObject("command");
......@@ -80,8 +99,11 @@ public class CtlCommand {
}
public int getCommandId() {
try {
commandId = rawCommand.getInt("commandId");
} catch (JSONException ignored) { }
return commandId;
}
}
......@@ -18,6 +18,7 @@ public class RouteChoice extends ControlModel {
private LinkedList<Integer> newTargetList;
public RouteChoice() {
super();
random = new Random(0);
......@@ -25,7 +26,8 @@ public class RouteChoice extends ControlModel {
}
public void applyPedControl(Pedestrian ped, JSONObject pedCommand) {
public void getControlAction(Pedestrian ped, JSONObject pedCommand) {
command = pedCommand;
// get information from controller
......@@ -104,8 +106,36 @@ public class RouteChoice extends ControlModel {
return probs;
}
public boolean isInformationProcessed(Pedestrian ped, int commandId){
// 1. handle conflicting instructions over time
if (reactionModel.isReactingToFirstInformationOnly()){
return isFirstInformation(ped);
}
// 2. handle recurring information that is received multiple times.
// If a command is received, the naviation app checks
// whether the command has already been displayed in an agent's app.
// This is necessary when the information is disseminated through the mobile network
// and can be received multiple times with different delays.
// In this case, the information is not further processed.
// Note: In the {@link ControlModel}, the agent makes the decision how to handle recurring information based on the reaction model setup.
// Here, the navigation app decides on how to proceed recurring information (do not proceed it).
return !isIdInList(ped, commandId);
}
public int getCommandId(){
// The navigation app requires a unique command identifier.
int id = super.getCommandId();
if (id == 0){
throw new IllegalArgumentException("Please provide a unique commandId != 0 for each command. Otherwise, information might not be processed.");
}
return id;
}
}
......@@ -11,6 +11,9 @@ import java.util.Random;
public class ReactionModel implements IReactModel{
private boolean isReactingToRecurringInformation;
private boolean isReactingToFirstInformationOnly;
private int numberOfReactionBehaviors;
private HashMap<Integer, DistParameters> distParameters = new HashMap<>();
......@@ -26,6 +29,8 @@ public class ReactionModel implements IReactModel{
ReactionParameter reactionParameter = new ReactionParameter(commandStr);
numberOfReactionBehaviors = reactionParameter.getNrOptions();
distParameters = reactionParameter.getDist();
isReactingToRecurringInformation = reactionParameter.isReactingToRecurringInformation();
isReactingToFirstInformationOnly = reactionParameter.isReactingToFirstInformationOnly();
}
public ReactionModel() {
......@@ -84,7 +89,11 @@ public class ReactionModel implements IReactModel{
}
public boolean isReactingToFirstInformationOnly() {
return isReactingToFirstInformationOnly;
}
public boolean isReactingToRecurringInformation() {
return isReactingToRecurringInformation;
}
}
......@@ -11,6 +11,8 @@ public class ReactionParameter {
int options = -1;
HashMap<Integer, DistParameters> dist = new HashMap<>();
private JSONObject rawCommand;
private boolean isReactingToRecurringInformation = false;
private boolean isReactingToFirstInformationOnly = true;
public ReactionParameter(String command) {
......@@ -18,7 +20,6 @@ public class ReactionParameter {
}
public ReactionParameter(JSONObject command) {
rawCommand = command;
}
......@@ -45,6 +46,24 @@ public class ReactionParameter {
}
}
public boolean isReactingToRecurringInformation(){
String key = "isReactingToRecurringInformation";
if (rawCommand.has(key)) {
return rawCommand.getBoolean(key);
}
return isReactingToRecurringInformation;
}
public boolean isReactingToFirstInformationOnly(){
String key = "isReactingToFirstInformationOnly";
if (rawCommand.has(key)) {
return rawCommand.getBoolean(key);
}
return isReactingToFirstInformationOnly;
}
public int getNrOptions() {
String key = "numberOfReactionProbabilities";
......
......@@ -8,5 +8,6 @@
"command" : {
"targetIds" : [1,2,3,4] ,
"probability" : [0.25, 0, 0.25, 0.5]
}
},
"commandId" : 1
}
\ No newline at end of file
{
"time" : 7.0,
"space" : {
"x" : 0.0,
"y" : 0.0,
"height": 100,
"width": 100
},
"command" : {
"targetIds" : [1,2,3,4] ,
"probability" : [0, 1, 0, 0]
},
"commandId" : 1
}
\ No newline at end of file
......@@ -97,6 +97,33 @@ public class RouteChoiceTest {
return msg;
}
private String readInputFile3(){
String dataPath = "testResources/control/external/CorridorChoiceData3.json";
String msg = "";
try {
msg = IOUtils.readTextFile(dataPath);
} catch (IOException e) {
e.printStackTrace();
}
return msg;
}
private String readInputFile2(){
String dataPath = "testResources/control/external/CorridorChoiceData2.json";
String msg = "";
try {
msg = IOUtils.readTextFile(dataPath);
} catch (IOException e) {
e.printStackTrace();
}
return msg;
}
@Test
public void updateState() {
......@@ -185,4 +212,67 @@ public class RouteChoiceTest {
assertEquals(target, 4);
}
@Test
public void pedInRectangle() {
// Create a single pedestrian with initial target nr. 5
Topography topo = createTopography(createPedestrians(1));
Pedestrian ped = topo.getPedestrianDynamicElements().getElement(0);
ped.setPosition(new VPoint(5.,5.));
String msg = readInputFile3();
RouteChoice routeChoice = new RouteChoice();
// keep old target nr.5 because timeout reached
routeChoice.update(topo, -1., msg);
int target = ped.getNextTargetId();
assertEquals(target, 2);
}
public void commandIdMissing() {
// the route choice app requires a unique command id.
Topography topo = createTopography(createPedestrians(1));
Pedestrian ped = topo.getPedestrianDynamicElements().getElement(0);
ped.setPosition(new VPoint(0.,0.));
String msg = readInputFile2();
RouteChoice routeChoice = new RouteChoice();
routeChoice.update(topo, -1., msg);
}
@Test
public void testCommandIdMissingWrapper() {
try {
commandIdMissing();
} catch (final IllegalArgumentException e) {
assertTrue(e.getMessage().equals("Please provide a unique commandId != 0 for each command. Otherwise, information might not be processed."));
}
}
@Test
public void testHandleRecurringInformation() {
// Create a single pedestrian with initial target nr. 5
Topography topo = createTopography(createPedestrians(1));
Pedestrian ped = topo.getPedestrianDynamicElements().getElement(0);
ped.setPosition(new VPoint(0.,0.));
RouteChoice routeChoice = new RouteChoice();
// keep old target nr.5 because timeout reached
routeChoice.update(topo, -1., readInputFile());
assertEquals(ped.getNextTargetId(), 4);
// readInputFile3() provides target 2. it is skipped because the command id is the same.
routeChoice.update(topo, -1., readInputFile3());
assertEquals(ped.getNextTargetId(), 4);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment