Commit 919f8a3a authored by Benedikt Zoennchen's avatar Benedikt Zoennchen
Browse files

Merge branch 'issue_46' into 'develop'

Issue 46

See merge request !23
parents 3225e84a 207317ee
......@@ -17,7 +17,7 @@ public class OutputFileTableModel extends VadereTableModelSorted<File> {
@Override
public void init(final VadereProject project) {
super.init(project);
List<File> outputFileNames = IOOutput.listAllOutputDirs(project);
List<File> outputFileNames = project.getProjectOutput().getAllOutputDirs();
setColumnCount(1);
setRowCount(0);
outputFileNames.forEach(p -> insertValue(p));
......
......@@ -197,14 +197,14 @@ public class ProjectViewModel {
public ScenarioBundle getRunningScenario() {
Scenario scenarioRM = project.getCurrentScenario();
List<String> outputDirectories = IOOutput.listSelectedOutputDirs(project, scenarioRM)
List<String> outputDirectories = project.getProjectOutput().listSelectedOutputDirs(scenarioRM)
.stream().map(file -> file.getAbsolutePath()).collect(Collectors.toList());
return new ScenarioBundle(project, scenarioRM, outputDirectories);
}
public ScenarioBundle getSelectedScenarioBundle() {
Scenario scenarioRM = getSelectedScenarioRunManager();
List<String> outputDirectories = IOOutput.listSelectedOutputDirs(project, scenarioRM)
List<String> outputDirectories = project.getProjectOutput().listSelectedOutputDirs(scenarioRM)
.stream().map(file -> file.getAbsolutePath()).collect(Collectors.toList());
return new ScenarioBundle(project, scenarioRM, outputDirectories);
}
......@@ -264,7 +264,7 @@ public class ProjectViewModel {
@Override
public void run() {
fireRefreshOutputStarted();
IOOutput.cleanOutputDirs(project);
project.getProjectOutput().update();
outputTableModel.init(project);
fireRefreshOutputCompleted();
}
......@@ -318,6 +318,10 @@ public class ProjectViewModel {
public Collection<File> getOutputDirectories() {
return outputDirectories;
}
public Scenario getScenarioRM(){
return project.getProjectOutput().getScenario(directory.getName());
}
}
public VTable createOutputTable() {
......
......@@ -522,8 +522,8 @@ public class ProjectView extends JFrame implements ProjectFinishedListener, Sing
}
private void loadScenarioIntoGui(OutputBundle bundle) throws IOException {
Scenario scenarioRM = IOOutput.readScenarioRunManager(bundle.getProject(),
bundle.getDirectory().getName());
Scenario scenarioRM = bundle.getScenarioRM();
Optional<File> optionalTrajectoryFile = IOUtils
.getFirstFile(bundle.getDirectory(), IOUtils.TRAJECTORY_FILE_EXTENSION);
if (optionalTrajectoryFile.isPresent()) {
......
package org.vadere.simulator.projects;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.vadere.simulator.projects.io.IOOutput;
import org.vadere.util.io.IOUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
/**
* Represents all run simulations within this project.
*
* @author Stefan Schuhbäck
*/
public class ProjectOutput {
private final VadereProject project;
private ConcurrentMap<String, SimulationOutput> simulationOutputs;
public ProjectOutput(final VadereProject project) {
this.project = project;
this.simulationOutputs = IOOutput.getSimulationOutputs(project);
}
/**
* Returns cached output directories. This function does not check if cached {@link SimulationOutput}
* is marked dirty. It is assumed {@link #update()} was called prior to this function.
*
* @return list of output directories corresponding to cached {@link SimulationOutput}
*/
public List<File> getAllOutputDirs() {
Path out = project.getOutputDir();
List<File> outputs = simulationOutputs.keySet().stream()
.map(k -> out.resolve(k).toFile())
.collect(Collectors.toList());
return outputs;
}
/**
* This function does not check if cached {@link SimulationOutput}
* is dirty. It is assumed {@link #update()} was called prior to this function.
*
* @param scenario Prior runs to this {@link Scenario}
* @return List of prior simulation runs matching selected {@link Scenario}
*/
public List<File> listSelectedOutputDirs(final Scenario scenario) {
List<File> out = new ArrayList<>();
try {
final String hash1 = scenario.getScenarioStore().hashOfJsonRepresentation();
for (Map.Entry<String, SimulationOutput> entry : simulationOutputs.entrySet()) {
if (!entry.getValue().isDirty()) {
String hash2 = entry.getValue().getScenarioHash();
if (hash1.equals(hash2))
out.add(project.getOutputDir().resolve(entry.getKey()).toFile());
}
}
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return out;
}
/**
* If the output directory is present it is marked dirty and will be re-check
* in the next call of {@link #update()}
*
* @param dirName directory name of {@link SimulationOutput}
*/
synchronized public void markDirty(String dirName) {
getSimulationOutput(dirName).ifPresent(SimulationOutput::setDirty);
}
public Optional<SimulationOutput> getSimulationOutput(String dirName) {
return Optional.ofNullable(simulationOutputs.get(dirName));
}
public Scenario getScenario(String dirName){
return getSimulationOutput(dirName).get().getSimulatedScenario();
}
/**
* re-check dirty {@link SimulationOutput} and add new valid output dirs to {@link ProjectOutput}
*/
public void update() {
try {
Files.walkFileTree(project.getOutputDir(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
String dirName = dir.toFile().getName();
//ignore corrupt directory and the subtree
if (dir.endsWith(IOUtils.CORRUPT_DIR))
return FileVisitResult.SKIP_SUBTREE;
//ignore output directory but continue with subtree
if (dir.endsWith(IOUtils.OUTPUT_DIR))
return FileVisitResult.CONTINUE;
Optional<SimulationOutput> outDir = getSimulationOutput(dirName);
// only re-check existing SimulationOutput if they are dirty
if (outDir.isPresent() && outDir.get().isDirty()) {
Optional<SimulationOutput> newSim = IOOutput.getSimulationOutput(project, dir.toFile());
if (newSim.isPresent()){
simulationOutputs.put(dirName, newSim.get());
} else {
simulationOutputs.remove(dirName);
}
} else {
// if new directory try to read it or move it corrupt if not valid.
Optional<SimulationOutput> newSim = IOOutput.getSimulationOutput(project, dir.toFile());
newSim.ifPresent(out -> simulationOutputs.put(dir.toFile().getName(), out));
}
return FileVisitResult.SKIP_SUBTREE;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
package org.vadere.simulator.projects;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.File;
import java.nio.file.Path;
/**
* Represents the directory holding a simulation output
*
* @author Stefan Schuhbäck
*/
public class SimulationOutput {
private final Scenario simulatedScenario;
private final Path outputDirectory;
private final String scenarioHash;
private boolean isDirty;
public SimulationOutput(Path directory, Scenario scenario) {
this.outputDirectory = directory;
this.simulatedScenario = scenario;
this.isDirty = false;
String tmpHash;
try {
tmpHash = scenario.getScenarioStore().hashOfJsonRepresentation();
} catch (JsonProcessingException e) {
tmpHash = "";
e.printStackTrace();
}
this.scenarioHash = tmpHash;
}
public String getScenarioHash() {
return this.scenarioHash;
}
public Scenario getSimulatedScenario() {
return simulatedScenario;
}
public boolean isDirty() {
return isDirty;
}
public void setDirty() {
isDirty = true;
}
}
......@@ -35,6 +35,7 @@ public class VadereProject {
new LinkedBlockingQueue<>();
private LinkedBlockingDeque<Scenario> scenariosLeft;
private Path outputDirectory;
private ProjectOutput projectOutput; //TODO initialize and wire up with rest ....
// TODO should be encapsulated in a class (we are not programming in C):
private int[] migrationStats; // scenarios: [0] total, [1] legacy'ed, [2] nonmigratable
......@@ -268,6 +269,8 @@ public class VadereProject {
public void finished(Runnable runnable) {
notifyScenarioRMListenerAboutPostRun(getCurrentScenario());
projectOutput.update();
if (scenariosLeft.isEmpty()) {
for (ProjectFinishedListener listener : projectFinishedListener) {
listener.postProjectRun(VadereProject.this);
......@@ -278,4 +281,11 @@ public class VadereProject {
}
};
public ProjectOutput getProjectOutput() {
return projectOutput;
}
public void setProjectOutput(ProjectOutput projectOutput) {
this.projectOutput = projectOutput;
}
}
package org.vadere.simulator.projects.io;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.vadere.simulator.projects.Scenario;
import org.vadere.simulator.projects.SimulationOutput;
import org.vadere.simulator.projects.VadereProject;
import org.vadere.state.scenario.Agent;
import org.vadere.state.simulation.Step;
......@@ -26,17 +29,16 @@ import java.util.stream.Collectors;
/**
* This IOUtility class provides all methods to load, delete, list, clean output directories.
* Each output directory contains two fiels *.scenario and *.trajectories.
* Each output directory contains two files *.scenario and *.trajectories.
*
*/
public abstract class IOOutput {
private static Logger logger = LogManager.getLogger(IOOutput.class);
private static final Logger logger = LogManager.getLogger(IOOutput.class);
public static List<File> listSelectedOutputDirs(final VadereProject project, final Scenario scenario) {
List<File> selectedOutputDirectories = new LinkedList<>();
selectedOutputDirectories = listAllOutputDirs(project).stream()
List<File> selectedOutputDirectories = listAllOutputDirs(project).stream()
.filter(dir -> isMatchingOutputDirectory(project, dir, scenario))
.collect(Collectors.toList());
......@@ -160,7 +162,7 @@ public abstract class IOOutput {
private static List<File> listAllDirs(final VadereProject project) {
List<File> outputDirectories = new LinkedList<>();
if (Files.exists(project.getOutputDir())) {
File[] files = new File(project.getOutputDir().toString()).listFiles(f -> f.isDirectory());
File[] files = new File(project.getOutputDir().toString()).listFiles(File::isDirectory);
if (files != null) {
outputDirectories = Arrays.stream(files).filter(dir -> !dir.getName().equals(IOUtils.CORRUPT_DIR))
.collect(Collectors.toList());
......@@ -169,22 +171,73 @@ public abstract class IOOutput {
return outputDirectories;
}
private static void cleanDirectory(final VadereProject project, final File directory) {
IOUtils.errorBox(
"The directory '"
+ directory.getName()
+ "' is corrupted and was moved to the '" + IOUtils.CORRUPT_DIR + "' folder.",
"Corrupt output file detected.");
private static void cleanDirectory(final VadereProject project, final File directory, boolean withGui){
final String info = "The directory '"
+ directory.getName()
+ "' is corrupted and was moved to the '" + IOUtils.CORRUPT_DIR + "' folder.";
if(withGui)
IOUtils.errorBox(info, "Corrupt output file detected.");
try {
Files.createDirectories(Paths.get(project.getOutputDir().toString(), IOUtils.CORRUPT_DIR));
Path sourcePath = directory.toPath();
Path targetPath = Paths.get(project.getOutputDir().toString(), IOUtils.CORRUPT_DIR, directory.getName());
Files.move(sourcePath, targetPath, StandardCopyOption.ATOMIC_MOVE);
logger.info(info);
} catch (IOException e1) {
logger.error(e1);
}
}
private static void cleanDirectory(final VadereProject project, final File directory) {
cleanDirectory(project, directory, true);
}
/**
* Returns {@link SimulationOutput} if supplied directory is a valid output directory.
* @param project VadereProject
* @param directory Directory containing a simulated data
* @return SimulationOutput contained in selected directory
*/
public static Optional<SimulationOutput> getSimulationOutput(final VadereProject project, final File directory ){
if(!directory.exists())
return Optional.empty();
Optional<Scenario> scenario = readOutputFile(project, directory);
Optional<Map<Step, List<Agent>>> trajectories = readTrajectories(project, directory);
if (scenario.isPresent() && trajectories.isPresent()){
return Optional.of(new SimulationOutput(directory.toPath(), scenario.get()));
} else {
//if directory is not a valid OutputDirectory
cleanDirectory(project, directory, false);
return Optional.empty();
}
}
/**
* Returns valid {@link SimulationOutput} of {@link VadereProject}
* @param project VadereProject
* @return All valid {@link SimulationOutput}s found in selected project.
*/
public static ConcurrentMap<String, SimulationOutput> getSimulationOutputs(final VadereProject project){
List<File> simOutDir = IOOutput.listAllDirs(project);
ConcurrentMap<String, SimulationOutput> simulationOutputs = new ConcurrentHashMap<>();
simOutDir.forEach( f -> {
Optional<Scenario> scenario = readOutputFile(project, f);
Optional<Map<Step, List<Agent>>> trajectories = readTrajectories(project, f);
if (scenario.isPresent() && trajectories.isPresent()){
SimulationOutput out = new SimulationOutput(f.toPath(), scenario.get());
simulationOutputs.put(f.getName(), out);
} else {
//invalid output directory move to corrupt.
cleanDirectory(project, f, false);
}
});
return simulationOutputs;
}
private static Optional<Scenario> readOutputFile(final VadereProject project, final File directory) {
try {
final Path pathToSnapshot = getPathToOutputFile(project, directory.getName(), IOUtils.SCENARIO_FILE_EXTENSION);
......
......@@ -2,6 +2,7 @@ package org.vadere.simulator.projects.io;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.vadere.simulator.projects.ProjectOutput;
import org.vadere.simulator.projects.Scenario;
import org.vadere.simulator.projects.VadereProject;
import org.vadere.simulator.projects.migration.MigrationAssistant;
......@@ -72,6 +73,8 @@ public class IOVadere {
VadereProject project = new VadereProject(name, scenarios);
project.setMigrationStats(migrationStats); // TODO [priority=low] [task=refactoring] better way to tunnel those results to the GUI?
project.setOutputDir(Paths.get(folderpath, IOUtils.OUTPUT_DIR));
ProjectOutput projectOutput = new ProjectOutput(project);
project.setProjectOutput(projectOutput);
logger.info("project loaded: " + project.getName());
return project;
}
......
......@@ -10,11 +10,17 @@ import org.vadere.state.scenario.Agent;
import org.vadere.state.scenario.Pedestrian;
import org.vadere.state.simulation.Step;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.io.IOUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
/**
......@@ -22,14 +28,10 @@ import java.util.stream.Collectors;
*/
public class TrajectoryReader {
private static final String SPLITTER = " ";
private static Logger logger = LogManager.getLogger(IOVadere.class);
private Path trajectoryFilePath;
private AttributesAgent attributesPedestrian;
private static final String SPLITTER = " ";
private Set<String> pedestrianIdKeys;
private Set<String> stepKeys;
private Set<String> xKeys;
......@@ -51,84 +53,84 @@ public class TrajectoryReader {
}
private TrajectoryReader(final Path trajectoryFilePath, final AttributesAgent attributesAgent) {
this.trajectoryFilePath = trajectoryFilePath;
this.attributesPedestrian = attributesAgent;
pedestrianIdKeys = new HashSet<>();
stepKeys = new HashSet<>();
xKeys = new HashSet<>();
yKeys = new HashSet<>();
targetIdKeys = new HashSet<>();
pedestrianIdKeys.add("id");
pedestrianIdKeys.add("pedestrianId");
stepKeys.add("timeStep");
stepKeys.add("step");
xKeys.add("x");
yKeys.add("y");
targetIdKeys.add("targetId");
pedIdIndex = -1;
stepIndex = -1;
xIndex = -1;
yIndex = -1;
targetIdIndex = -1;
}
this.trajectoryFilePath = trajectoryFilePath;
this.attributesPedestrian = attributesAgent;
pedestrianIdKeys = new HashSet<>();
stepKeys = new HashSet<>();
xKeys = new HashSet<>();
yKeys = new HashSet<>();
targetIdKeys = new HashSet<>();
pedestrianIdKeys.add("id");
pedestrianIdKeys.add("pedestrianId");
stepKeys.add("timeStep");
stepKeys.add("step");
xKeys.add("x");
yKeys.add("y");
targetIdKeys.add("targetId");
pedIdIndex = -1;
stepIndex = -1;
xIndex = -1;
yIndex = -1;
targetIdIndex = -1;
}
public Map<Step, List<Agent>> readFile() throws IOException {
// 1. Get the correct column
String header = Files.lines(this.trajectoryFilePath).findFirst().get();
String[] columns = header.split(SPLITTER);
for(int index = 0; index < columns.length; index++) {
if(pedestrianIdKeys.contains(columns[index])) {
pedIdIndex = index;
}
else if(stepKeys.contains(columns[index])) {
stepIndex = index;
}
else if(xKeys.contains(columns[index])) {
xIndex = index;
}
else if(yKeys.contains(columns[index])) {
yIndex = index;
}
else if(targetIdKeys.contains(columns[index])) {
targetIdIndex = index;
}
}
try {
if(pedIdIndex != -1 && xIndex != -1 && yIndex != -1 && stepIndex != -1) {
return Files.lines(this.trajectoryFilePath)
.skip(1) // Skip header line
.map(line -> line.split(SPLITTER))
.map(cells -> {
int step = Integer.parseInt(cells[stepIndex]);
int pedestrianId = Integer.parseInt(cells[pedIdIndex]);
VPoint pos = new VPoint(Double.parseDouble(cells[xIndex]), Double.parseDouble(cells[yIndex]));
int targetId = targetIdIndex != -1 ? Integer.parseInt(cells[targetIdIndex]) : -1;
Pedestrian ped = new Pedestrian(new AttributesAgent(this.attributesPedestrian, pedestrianId), new Random());
ped.setPosition(pos);
LinkedList<Integer> targets = new LinkedList<Integer>();
targets.addFirst(targetId);
ped.setTargets(targets);
return Pair.create(new Step(Integer.parseInt(cells[0])), ped);
})
.collect(Collectors.groupingBy(pair -> pair.getKey(), Collectors.mapping(pair -> pair.getValue(), Collectors.toList())));
}
else {
throw new IOException("could not read trajectory file, some colums are missing.");
}
}
catch (Exception e) {
logger.warn("could not read trajectory file. The file format might not be compatible or it is missing.");
throw e;
}
// 1. Get the correct column
String header;
//read only first line.
try (BufferedReader in = IOUtils.defaultBufferedReader(this.trajectoryFilePath)) {
header = in.readLine();