Commit 2a9d2b68 authored by Stefan Schuhbaeck's avatar Stefan Schuhbaeck
Browse files

add compound object handling to vadere-traci.

A compound object allows generic definition of complex objects
consiting of simple types.
e.g. [String, String, Integer, 2DPoint, String]
parent 8353a9f0
......@@ -13,6 +13,7 @@ public enum TraCIDataType {
DOUBLE(0x0B, 8, true),
STRING(0x0C, -1, true),
STRING_LIST(0x0E, -1, true),
COMPOUND_OBJECT(0x0F, -1, true),
POS_2D(0x01, 17, false),
POS_2D_LIST(0x10, -1, true),
POS_3D(0x03, 25, false),
......
package org.vadere.manager.traci.compound;
import org.apache.commons.lang3.tuple.Pair;
import org.vadere.manager.TraCIException;
import org.vadere.manager.traci.TraCIDataType;
import java.util.Iterator;
/**
* CompoundObject implementation based on TraCI as described in https://sumo.dlr.de/docs/TraCI/Protocol.html#atomar_types
*
* This implementation consist of two equally long arrays {@link #type} and {@link #data}. The
* {@link #type} array saves Type of the objects at the same index in {@link #data}.
*
* At time of creation it must be stated how many objects will be tide together. See @{@link
* CompoundObjectBuilder} for usage of Constructor and the {@link #add(int, Object)} method.
*/
public class CompoundObject {
private TraCIDataType[] type;
private Object[] data;
private int cur;
public CompoundObject(int noElements) {
this.type = new TraCIDataType[noElements];
this.data = new Object[noElements];
this.cur = 0;
}
public String types() {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (TraCIDataType i : this.type) {
sb.append(i.name()).append(", ");
}
sb.delete(sb.length() - 2, sb.length());
sb.append("]");
return sb.toString();
}
public int size() {
return data.length;
}
public CompoundObject add(int type, Object data) {
return add(TraCIDataType.fromId(type), data);
}
public CompoundObject add(TraCIDataType type, Object data) {
if (cur > this.data.length)
throw new TraCIException("CompoundObject already full. Received " + types());
this.type[cur] = type;
this.data[cur] = data;
cur++;
return this;
}
public boolean hasIndex(int index) {
return hasIndex(index, null);
}
public boolean hasIndex(int index, TraCIDataType type) {
if (index >= 0 && index < this.data.length) {
if (type != null) {
return this.type[index].equals(type);
}
return true;
}
return false;
}
public Object getData(int index, TraCIDataType type) {
if (index > this.data.length)
throw new TraCIException("Cannot access data with index %d", index);
if (!this.type[index].equals(type))
throw new TraCIException("Type mismatch of CompoundObject element %s != %s at index %d",
this.type[index].name(), type.name(), index);
return this.data[index];
}
public Object getData(int index) {
if (index > this.data.length)
throw new TraCIException("Cannot access data with index %d", index);
return this.data[index];
}
public Iterator<Pair<TraCIDataType, Object>> itemIterator() {
return new Iter(this, null);
}
public Iterator<Pair<TraCIDataType, Object>> itemIterator(TraCIDataType typeAssertion) {
return new Iter(this, typeAssertion);
}
private class Iter implements Iterator<Pair<TraCIDataType, Object>> {
private final CompoundObject compoundObject;
private final TraCIDataType typeAssertion;
private int curr;
Iter(CompoundObject compoundObject, TraCIDataType typeAssertion) {
this.compoundObject = compoundObject;
this.typeAssertion = typeAssertion;
this.curr = 0;
}
@Override
public boolean hasNext() {
return curr < compoundObject.size();
}
@Override
public Pair<TraCIDataType, Object> next() {
Pair<TraCIDataType, Object> p = Pair.of(compoundObject.type[curr], compoundObject.data[curr]);
if (!p.getLeft().equals(this.typeAssertion)) {
throw new TraCIException("Type mismatch in CompoundObject. Expected '%s' but found '%s'",
this.typeAssertion.name(), p.getLeft().name());
}
curr++;
return p;
}
}
}
package org.vadere.manager.traci.compound;
import org.vadere.manager.TraCIException;
import org.vadere.manager.traci.TraCIDataType;
import java.util.LinkedList;
/**
* Builder class to create any combination atomar data types combined in a @{@link CompoundObject}.
* See static methods on how the builder is used. Ensure that the number of {@link
* #add(TraCIDataType)} calls is equal to the number of arguments to the {@link #build(Object...)}.
*/
public class CompoundObjectBuilder {
private LinkedList<TraCIDataType> types;
public CompoundObjectBuilder() {
this.types = new LinkedList<>();
}
public CompoundObjectBuilder rest() {
types.clear();
return this;
}
public CompoundObjectBuilder add(TraCIDataType type) {
types.add(type);
return this;
}
public CompoundObjectBuilder add(TraCIDataType type, int count) {
for (int i = 0; i < count; i++) {
types.add(type);
}
return this;
}
public CompoundObject build(Object... data) {
CompoundObject obj = new CompoundObject(data.length);
if (types.size() == data.length) {
int idx = 0;
for (TraCIDataType type : types) {
obj.add(type, data[idx]);
idx++;
}
} else {
throw new TraCIException("CompoundObjectBuilder error. Number of Types does not match" +
" received number of data items");
}
return obj;
}
static public CompoundObjectBuilder builder() {
return new CompoundObjectBuilder();
}
static public CompoundObject json(String json) {
return CompoundObjectBuilder.builder()
.rest()
.add(TraCIDataType.STRING)
.build(json);
}
}
package org.vadere.manager.traci.compound.object;
import org.vadere.manager.TraCIException;
import org.vadere.manager.traci.TraCIDataType;
import org.vadere.manager.traci.compound.CompoundObject;
import org.vadere.manager.traci.compound.CompoundObjectBuilder;
import java.util.Objects;
/**
* Configuration options received from TraCI client.
*
* Used to seed Random object and redirect output to a client defined location.
*/
public class SimulationCfg {
private String configName;
private String experiment;
private String dateTime;
private String resultRootDir;
private String iterationVariables;
private String repetition;
private String outputScalarFile;
private String outputVecFile;
private long seed;
public static CompoundObject asCompoundObject(String configName,
String experiment,
String dateTime,
String resultRootDir,
String iterationVariables,
String repetition,
String outputScalarFile,
String outputVecFile,
long seed) {
return CompoundObjectBuilder.builder()
.rest()
.add(TraCIDataType.STRING, 8)
.add(TraCIDataType.INTEGER)
.build(configName,
experiment,
dateTime,
resultRootDir,
iterationVariables,
repetition,
outputScalarFile,
outputVecFile,
seed);
}
public SimulationCfg(CompoundObject obj) {
if (obj.size() != 9) {
throw new TraCIException("Expected at least 9 elements");
}
configName = (String) obj.getData(0, TraCIDataType.STRING);
experiment = (String) obj.getData(1, TraCIDataType.STRING);
dateTime = (String) obj.getData(2, TraCIDataType.STRING);
resultRootDir = (String) obj.getData(3, TraCIDataType.STRING);
iterationVariables = (String) obj.getData(4, TraCIDataType.STRING);
repetition = (String) obj.getData(5, TraCIDataType.STRING);
outputScalarFile = (String) obj.getData(6, TraCIDataType.STRING);
outputVecFile = (String) obj.getData(7, TraCIDataType.STRING);
seed = Long.valueOf((int) obj.getData(8, TraCIDataType.INTEGER));
}
public String outputPath() {
if (outputVecFile.endsWith(".vec")) {
return outputVecFile.substring(0, outputVecFile.length() - 4);
}
if (outputScalarFile.endsWith(".sca")) {
return outputScalarFile.substring(0, outputScalarFile.length() - 4);
}
return String.format("%s/%s/%s/%s_vars_%s_rep_%s",
resultRootDir,
configName,
experiment,
dateTime,
iterationVariables,
repetition
);
}
public String getConfigName() {
return configName;
}
public void setConfigName(String configName) {
this.configName = configName;
}
public String getExperiment() {
return experiment;
}
public void setExperiment(String experiment) {
this.experiment = experiment;
}
public String getDateTime() {
return dateTime;
}
public void setDateTime(String dateTime) {
this.dateTime = dateTime;
}
public String getResultRootDir() {
return resultRootDir;
}
public void setResultRootDir(String resultRootDir) {
this.resultRootDir = resultRootDir;
}
public String getIterationVariables() {
return iterationVariables;
}
public void setIterationVariables(String iterationVariables) {
this.iterationVariables = iterationVariables;
}
public String getRepetition() {
return repetition;
}
public void setRepetition(String repetition) {
this.repetition = repetition;
}
public String getOutputScalarFile() {
return outputScalarFile;
}
public void setOutputScalarFile(String outputScalarFile) {
this.outputScalarFile = outputScalarFile;
}
public String getOutputVecFile() {
return outputVecFile;
}
public void setOutputVecFile(String outputVecFile) {
this.outputVecFile = outputVecFile;
}
public long getSeed() {
return seed;
}
public void setSeed(long seed) {
this.seed = seed;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SimulationCfg that = (SimulationCfg) o;
return seed == that.seed &&
configName.equals(that.configName) &&
experiment.equals(that.experiment) &&
dateTime.equals(that.dateTime) &&
resultRootDir.equals(that.resultRootDir) &&
iterationVariables.equals(that.iterationVariables) &&
repetition.equals(that.repetition) &&
outputScalarFile.equals(that.outputScalarFile) &&
outputVecFile.equals(that.outputVecFile);
}
@Override
public int hashCode() {
return Objects.hash(configName, experiment, dateTime, resultRootDir, iterationVariables, repetition, outputScalarFile, outputVecFile, seed);
}
@Override
public String toString() {
return "SimulationCfg{" +
"configName='" + configName + '\'' +
", experiment='" + experiment + '\'' +
", dateTime='" + dateTime + '\'' +
", resultRootDir='" + resultRootDir + '\'' +
", iterationVariables='" + iterationVariables + '\'' +
", repetition='" + repetition + '\'' +
", outputScalarFile='" + outputScalarFile + '\'' +
", outputVecFile='" + outputVecFile + '\'' +
", seed=" + seed +
'}';
}
}
......@@ -2,6 +2,7 @@ package org.vadere.manager.traci.reader;
import org.vadere.manager.TraCIException;
import org.vadere.manager.traci.TraCIDataType;
import org.vadere.manager.traci.compound.CompoundObject;
import org.vadere.manager.traci.sumo.LightPhase;
import org.vadere.manager.traci.sumo.RoadMapPosition;
import org.vadere.manager.traci.sumo.TrafficLightPhase;
......@@ -223,6 +224,23 @@ public class TraCIByteBuffer implements TraCIReader {
return new Color(r, g, b, a);
}
@Override
public CompoundObject readCompoundObject() {
ensureBytes(4);
int noElements = readInt();
CompoundObject compoundObject = new CompoundObject(noElements);
for (int i = 0; i < noElements; i++) {
TraCIDataType type = TraCIDataType.fromId(readUnsignedByte());
if (type.equals(TraCIDataType.COMPOUND_OBJECT))
throw new TraCIException("Recursive CompoundObject are not allowed.");
compoundObject.add(type, readTypeValue(type));
}
return compoundObject;
}
@Override
public Object readTypeValue(TraCIDataType type) {
......@@ -257,6 +275,8 @@ public class TraCIByteBuffer implements TraCIReader {
return readTrafficLightPhaseList();
case COLOR:
return readColor();
case COMPOUND_OBJECT:
return readCompoundObject();
case NULL:
return null;
default:
......
package org.vadere.manager.traci.reader;
import org.vadere.manager.traci.TraCIDataType;
import org.vadere.manager.traci.compound.CompoundObject;
import org.vadere.manager.traci.sumo.RoadMapPosition;
import org.vadere.manager.traci.sumo.TrafficLightPhase;
import org.vadere.util.geometry.Vector3D;
......@@ -66,6 +67,8 @@ public interface TraCIReader {
Color readColor();
CompoundObject readCompoundObject();
boolean hasRemaining();
void ensureBytes(int num);
......
package org.vadere.manager.traci.writer;
import org.apache.commons.lang3.tuple.Pair;
import org.vadere.manager.TraCIException;
import org.vadere.manager.traci.TraCIDataType;
import org.vadere.manager.traci.compound.CompoundObject;
import org.vadere.manager.traci.sumo.RoadMapPosition;
import org.vadere.manager.traci.sumo.TrafficLightPhase;
import org.vadere.util.geometry.Vector3D;
......@@ -14,6 +16,7 @@ import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
......@@ -69,8 +72,8 @@ public class ByteArrayOutputStreamTraCIWriter implements TraCIWriter {
case POS_2D:
write2DPosition((VPoint) data);
break;
case POS_2D_LIST: // new
write2DPositionListWithId((Map<String, VPoint>) data); // new
case POS_2D_LIST:
write2DPositionListWithId((Map<String, VPoint>) data);
break;
case POS_3D:
write3DPosition((Vector3D) data);
......@@ -93,12 +96,13 @@ public class ByteArrayOutputStreamTraCIWriter implements TraCIWriter {
case COLOR:
writeColor((Color) data);
break;
case COMPOUND_OBJECT:
writeCompoundObject((CompoundObject) data);
case NULL:
writeNull();
break;
default:
logger.errorf("cannot write %s", dataType.toString());
}
return this;
......@@ -227,7 +231,6 @@ public class ByteArrayOutputStreamTraCIWriter implements TraCIWriter {
return this;
}
@Override
public TraCIWriter writeRoadMapPosition(RoadMapPosition val) {
writeUnsignedByte(TraCIDataType.POS_ROAD_MAP.id);
......@@ -264,7 +267,8 @@ public class ByteArrayOutputStreamTraCIWriter implements TraCIWriter {
public TraCIWriter writePolygon(List<VPoint> points) {
writeUnsignedByte(TraCIDataType.POLYGON.id);
if (points.size() > 255)
throw new TraCIException("Polygon to big. TraCI only supports polygon up to 255 points.");
throw new TraCIException("Polygon to big. " +
"TraCI only supports polygon up to 255 points.");
writeUnsignedByte(points.size());
points.forEach(p -> {
writeDouble(p.getX());
......@@ -277,7 +281,8 @@ public class ByteArrayOutputStreamTraCIWriter implements TraCIWriter {
public TraCIWriter writeTrafficLightPhaseList(List<TrafficLightPhase> phases) {
writeUnsignedByte(TraCIDataType.TRAFFIC_LIGHT_PHASE_LIST.id);
if (phases.size() > 255)
throw new TraCIException("Traffic Light Phase List to big. TraCI only supports list up to 255 elements.");
throw new TraCIException("Traffic Light Phase List to big. " +
"TraCI only supports list up to 255 elements.");
writeUnsignedByte(phases.size());
phases.forEach(phase -> {
writeString(phase.getPrecRoad());
......@@ -297,6 +302,20 @@ public class ByteArrayOutputStreamTraCIWriter implements TraCIWriter {
return this;
}
@Override
public TraCIWriter writeCompoundObject(CompoundObject compoundObject) {
writeUnsignedByte(TraCIDataType.COMPOUND_OBJECT.id);
writeInt(compoundObject.size());
Iterator<Pair<TraCIDataType, Object>> iter = compoundObject.itemIterator();
while (iter.hasNext()) {
Pair<TraCIDataType, Object> p = iter.next();
if (p.getLeft().equals(TraCIDataType.COMPOUND_OBJECT))
throw new TraCIException("Recursive CompoundObject are not allowed.");
writeObjectWithId(p.getLeft(), p.getRight());
}
return this;
}
@Override
public TraCIWriter writeNull() {
writeUnsignedByte(TraCIDataType.NULL.id);
......
package org.vadere.manager.traci.writer;
import org.vadere.manager.traci.TraCIDataType;
import org.vadere.manager.traci.compound.CompoundObject;
import org.vadere.manager.traci.sumo.RoadMapPosition;
import org.vadere.manager.traci.sumo.TrafficLightPhase;
import org.vadere.util.geometry.Vector3D;
......@@ -34,7 +35,7 @@ public interface TraCIWriter {
TraCIWriter writeStringListWithId(List<String> val);
TraCIWriter write2DPositionListWithId(Map<String, VPoint> data); // new
TraCIWriter write2DPositionListWithId(Map<String, VPoint> data);
TraCIWriter writeByte(int val);
......@@ -78,7 +79,7 @@ public interface TraCIWriter {
TraCIWriter write2DPosition(VPoint val);
TraCIWriter write2DPositionList(Map<String, VPoint> data); // new
TraCIWriter write2DPositionList(Map<String, VPoint> data);
TraCIWriter write3DPosition(Vector3D val);
......@@ -96,6 +97,8 @@ public interface TraCIWriter {
TraCIWriter writeColor(Color color);
TraCIWriter writeCompoundObject(CompoundObject compoundObject);
TraCIWriter writeNull();
TraCIWriter writeCommandLength(int cmdLen);
......
Markdown is supported
0%