The expiration time for new job artifacts in CI/CD pipelines is now 30 days (GitLab default). Previously generated artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit a75d75fd authored by Stefan Schuhbaeck's avatar Stefan Schuhbaeck
Browse files

Merge branch 'develop' into TestSimulationRunner

parents 1ae01dea ac78f73f
......@@ -8,7 +8,34 @@ unit_tests_with_coverage:
#
# Watch out: OpenCL code cannot be run on virtual machine. Therefore,
# skip this tests.
#
# The comma-separated list of tests after "-Dtest=<list>" is skipped.
# All other tests are executed normally.
#
# Watch out: on some CI instances the exclamation mark must be escaped by a
# backslash. Otherwise, it is interpreted by the executing shell as special
# command.
#
# Following syntaxes allow you to skip whole classes and single methods:
#
# #Exclude one test class
# mvn test -Dtest=!LegacyTest
# # Exclude one test method
# mvn verify -Dtest=!LegacyTest#testFoo
# # Exclude two test methods
# mvn verify -Dtest=!LegacyTest#testFoo+testBar
# # Exclude a package with a wildcard (*)
# mvn test -Dtest=!com.mycompany.app.Legacy*
script:
- mvn clean
- mvn test -Dtest=\!TestConvolution
\ No newline at end of file
- mvn test -Dtest=!TestConvolution,!TestBitonicSort,!TestCLLinkedList
- python3 Tools/collect_line_and_branch_coverage.py
run_scenario_files:
stage: deploy
script:
- mvn clean
- mvn package -Dmaven.test.skip=true
- python3 Tools/run_vadere_console_with_all_scenario_files.py
# Extract line and branch coverage (in percentage) from HTML coverage reports
# which are created by Maven's jacoco plugin.
# Use top-level pom.xml to search in correct subdirectories.
#
# Wach out: call this script from root directory of project. E.g.
#
# python Tools/my_script.py
import xml.etree.ElementTree as ET
import os
import re
def get_modules_from_pom_file(filename="pom.xml"):
"""Return a list of submodules which where found in passed "pom.xml"."""
xml_name_spaces = {
"default": "http://maven.apache.org/POM/4.0.0"
}
xml_tree = ET.parse(filename)
xml_root = xml_tree.getroot()
exclude_list = ["./VadereAnnotation", "./VadereGui"]
modules = xml_root.findall("default:modules/default:module", xml_name_spaces)
module_names = [module.text for module in modules if module.text not in exclude_list]
return module_names
def extract_line_and_branch_coverage(module_names):
"""Return a dictionary which maps module name to line and branch coverage tuple."""
module_to_coverage = dict()
default_coverage_file = "target/site/coverage-reports/index.html"
for module in module_names:
coverage_path = os.path.join(module, default_coverage_file)
with open(coverage_path, "r") as file:
coverage_report = file.read()
regex_pattern = re.compile(r"Total.*?([0-9]{1,3})%.*?([0-9]{1,3})%")
match = regex_pattern.search(coverage_report)
if match:
line_coverage = float(match.group(1))
branch_coverage = float(match.group(2))
module_to_coverage[module] = (line_coverage, branch_coverage)
else:
raise Exception("Coverage data not found for module: {}".format(module))
return module_to_coverage
def print_averaged_line_coverage(coverage_data):
"""GitLab CI tools read out the stdout output of the build process. Therefore, print coverage info to stdout."""
total_modules = len(coverage_data)
line_coverage_data = [line_coverage for (line_coverage, branch_coverage) in coverage_data.values()]
branch_coverage_data = [branch_coverage for (line_coverage, branch_coverage) in coverage_data.values()]
summed_line_coverage_data = sum(line_coverage_data)
summed_branch_coverage_data = sum(branch_coverage_data)
# By default, GitLab CI parses only integers.
averaged_line_coverage = int(round(summed_line_coverage_data / total_modules, 0))
averaged_branch_coverage = int(round(summed_branch_coverage_data / total_modules, 0))
print("Line Coverage: Total {}%".format(averaged_line_coverage))
print("Branch Coverage: {}%".format(averaged_branch_coverage))
if __name__ == "__main__":
module_names = get_modules_from_pom_file()
module_to_coverage = extract_line_and_branch_coverage(module_names)
print_averaged_line_coverage(module_to_coverage)
# Use "vadere-console.jar", which is created by "mvn package", to run all
# scenario files under "VadereModelTests" subdirectory.
#
# Note: script contains some print statements so that progress can be tracked
# a little bit
# Wach out: call this script from root directory of project. E.g.
#
# python Tools/my_script.py
import fnmatch
import os
import re
import shutil
import subprocess
def find_scenario_files(path="VadereModelTests"):
scenario_search_pattern = "*.scenario"
scenario_files = []
exclude_patterns = ["TESTOVM"]
for root, dirnames, filenames in os.walk(path):
for filename in fnmatch.filter(filenames, scenario_search_pattern):
scenario_path = os.path.join(root, filename)
for exclude_pattern in exclude_patterns:
regex_pattern = re.compile(exclude_pattern)
match = regex_pattern.search(scenario_path)
if match is None:
scenario_files.append(scenario_path)
print("Total scenario files: {}".format(len(scenario_files)))
print("Exclude patterns: {}".format(exclude_patterns))
return scenario_files
def run_scenario_files_with_vadere_console(scenario_files, vadere_console="VadereGui/target/vadere-console.jar", scenario_timeout_in_sec=60):
output_dir = "output"
if not os.path.exists(output_dir):
os.makedirs(output_dir)
total_scenario_files = len(scenario_files)
for i, scenario_file in enumerate(scenario_files):
print("Running scenario file ({}/{}): {}".format(i + 1, total_scenario_files, scenario_file))
# Use timout feature, check return value and capture stdout/stderr to a PIPE (use completed_process.stdout to get it).
completed_process = subprocess.run(args=["java", "-jar", vadere_console, scenario_file, output_dir],
timeout=scenario_timeout_in_sec,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
print("Finished scenario file: {}".format(scenario_file))
if os.path.exists(output_dir):
shutil.rmtree(output_dir)
if __name__ == "__main__":
scenario_files = find_scenario_files()
run_scenario_files_with_vadere_console(scenario_files)
......@@ -220,7 +220,7 @@ PostVis.chbShowStairs.text=Treppen anzeigen
PostVis.btnSnapshot.tooltip=Snapshot
PostVis.btnPNGSnapshot.tooltip=PNG-Snapshot
PostVis.btnSVGSnapshot.tooltip=SVG-Snapshot
PostVis.btnTikZSnapshot.tooltip=TikZ Snapshot
PostVis.btnTikZSnapshot.tooltip=TikZ-Snapshot
PostVis.menuFile.title=Datei
PostVis.menuSettings.title=Einstellungen
PostVis.menuRecentFiles.title=K\u00FCrzlich verwendete Dateien
......
......@@ -150,20 +150,20 @@ public class OnlineVisualisationWindow extends JPanel implements Observer {
};
ActionGeneratePNG generatePNG = new ActionGeneratePNG(
"generatePNG",
Messages.getString("PostVis.btnPNGSnapshot.tooltip"),
resources.getIcon("camera_png.png", iconWidth, iconHeight),
new OnlinevisualizationRenderer(model),
model);
ActionGenerateSVG generateSVG = new ActionGenerateSVG(
"generateSVG",
Messages.getString("PostVis.btnSVGSnapshot.tooltip"),
resources.getIcon("camera_svg.png", iconWidth, iconHeight),
new OnlinevisualizationRenderer(model),
model);
ActionGenerateTikz generateTikz = new ActionGenerateTikz(
"generateTikz",
Messages.getString("PostVis.btnTikZSnapshot.tooltip"),
resources.getIcon("camera_tikz.png", iconWidth, iconHeight),
new OnlinevisualizationRenderer(model),
model);
......
......@@ -100,10 +100,20 @@ public class PostvisualizationModel extends SimulationModel<PostvisualizationCon
});
}
public void init(final Map<Step, List<Agent>> agentsByStep, final Scenario vadere, final String projectPath) {
logger.info("start init postvis model");
init(vadere, projectPath);
/**
* Initialize the {@link PostvisualizationModel}.
*
* @param agentsByStep the trajectory information: a list of agent (their position, target, group...) sorted by the time step.
* @param scenario the scenario which was used to produce the output the PostVis will display.
* @param projectPath the path to the project.
*/
public void init(final Map<Step, List<Agent>> agentsByStep, final Scenario scenario, final String projectPath) {
logger.info("start the initialization of the PostvisualizationModel.");
init(scenario, projectPath);
this.agentsByStep = agentsByStep;
trajectories = new HashMap<>();
// to have fast access to the key values.
Map<Integer, Step> map = agentsByStep
.keySet().stream()
.sorted(stepComparator)
......@@ -118,21 +128,22 @@ public class PostvisualizationModel extends SimulationModel<PostvisualizationCon
for (int stepNumber = 1; stepNumber <= optLastStep.get().getStepNumber(); stepNumber++) {
if (map.containsKey(stepNumber)) {
steps.add(map.get(stepNumber));
for(Agent agent : agentsByStep.get(map.get(stepNumber))) {
if(!trajectories.containsKey(agent.getId())) {
trajectories.put(agent.getId(), new Trajectory(agent.getId()));
}
trajectories.get(agent.getId()).addStep(map.get(stepNumber), agent);
}
} else {
steps.add(new Step(stepNumber));
}
}
}
this.trajectories = agentsByStep
.entrySet()
.stream()
.flatMap(entry -> entry.getValue().stream())
.map(ped -> ped.getId())
.distinct()
.map(id -> new Trajectory(agentsByStep, id))
.collect(Collectors.toMap(t -> t.getPedestrianId(), t -> t));
for(Trajectory trajectory : trajectories.values()) {
trajectory.fill();
}
this.step = !steps.isEmpty() ? steps.get(0) : null;
logger.info("finished init postvis model");
......@@ -300,7 +311,7 @@ public class PostvisualizationModel extends SimulationModel<PostvisualizationCon
private double getSimTimeInSec(final Step step) {
return step.getSimTimeInSec()
.orElse(step.getStepNumber() * vadere.getScenarioStore().attributesSimulation.getSimTimeStepLength());
.orElse(step.getStepNumber() * vadere.getScenarioStore().getAttributesSimulation().getSimTimeStepLength());
}
public synchronized void setPotentialFieldContainer(final PotentialFieldContainer container) {
......
......@@ -2,6 +2,7 @@ package org.vadere.gui.postvisualization.utils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.vadere.gui.components.model.DefaultSimulationConfig;
import org.vadere.gui.components.model.SimulationModel;
import org.vadere.gui.components.view.SimulationRenderer;
......@@ -19,20 +20,20 @@ import static java.awt.geom.PathIterator.*;
* Convert the (Java) scenario description into a TikZ representation.
*
* Usually, each (Java) scenario element is represented as a path via
*
* @see PathIterator This PathSeparator must be converted into its TikZ representation.
* @see PathIterator This PathSeparator must be converted into its TikZ
* representation.
*
* For example, traversing a Java path with PathIterator returns two segments:
*
*
* segment type= 0 (SEG_MOVETO) with coords: [1.0, 1.0, 0.0, 0.0, 0.0, 0.0] segment type= 3
* (SEG_LINETO) with coords: [2.0, 2.0, 0.0, 0.0, 0.0, 0.0]
* segment type = 0 (SEG_MOVETO) with coords: [1.0, 1.0, 0.0, 0.0, 0.0, 0.0]
* segment type = 3 (SEG_LINETO) with coords: [2.0, 2.0, 0.0, 0.0, 0.0, 0.0]
*
* This must be transformed to TikZ:
*
* (1.0,1.0) to (2.0,2.0)
*
* The TikZGenerator should also respect the GUI settings (e.g., enabled elements, colors etc.).
* The TikZGenerator should also respect the GUI settings (e.g., enabled
* elements, colors etc.).
*/
public class TikzGenerator {
......@@ -163,22 +164,51 @@ public class TikzGenerator {
}
// TODO: maybe, draw also trajectories.
if (config.isShowPedestrians()) {
generatedCode += "% Agents\n";
for (Agent agent : model.getAgents()) {
String agentTextPattern = "\\fill[AgentColor] (%f,%f) circle [radius=%fcm];\n";
generatedCode += String.format(agentTextPattern, agent.getPosition().x, agent.getPosition().y, agent.getRadius());
// Do not draw agents as path for performance reasons. Usually, agents have a circular shape.
// generatedCode += String.format("\\fill[AgentColor] %s\n", generatePathForScenarioElement(agent));
}
} else {
generatedCode += "% Agents (not enabled in config)\n";
}
return generatedCode;
if (config.isShowPedestrians()) {
generatedCode += "% Agents\n";
generatedCode += drawAgents(config);
} else {
generatedCode = "% Agents (not enabled in config)\n";
}
return generatedCode;
}
private String generatePathForScenarioElement(ScenarioElement element) {
@NotNull
private String drawAgents(DefaultSimulationConfig config) {
String generatedCode = "";
for (Agent agent : model.getAgents()) {
if (model.getConfig().isShowGroups()) {
try {
Pedestrian pedestrian = (Pedestrian) agent;
Color pedestrianColor = renderer.getAgentRender().getColor(pedestrian);
Shape pedestrianShape = renderer.getAgentRender().getShape(pedestrian);
String colorString = String.format("{rgb,255:red,%d; green,%d; blue,%d}", pedestrianColor.getRed(), pedestrianColor.getGreen(), pedestrianColor.getBlue());
generatedCode += String.format("\\fill[fill=%s] %s\n", colorString, generatePathForShape(pedestrianShape));
} catch (ClassCastException cce) {
logger.error("Error casting to Pedestrian");
cce.printStackTrace();
// TODO: render agent as circle!
}
} else {
String agentTextPattern = "\\fill[AgentColor] (%f,%f) circle [radius=%fcm];\n";
generatedCode += String.format(agentTextPattern, agent.getPosition().x, agent.getPosition().y, agent.getRadius());
}
if (model.isElementSelected() && model.getSelectedElement().equals(agent)) {
String agentTextPattern = "\\draw[magenta] (%f,%f) circle [radius=%fcm];\n";
generatedCode += String.format(agentTextPattern, agent.getPosition().x, agent.getPosition().y, agent.getRadius());
}
// Do not draw agents as path for performance reasons. Usually, agents have a circular shape.
// generatedCode += String.format("\\fill[AgentColor] %s\n", generatePathForScenarioElement(agent));
}
return generatedCode;
}
private String generatePathForScenarioElement(ScenarioElement element) {
String generatedPath = "";
AffineTransform noTransformation = new AffineTransform();
......@@ -195,6 +225,23 @@ public class TikzGenerator {
return generatedPath;
}
private String generatePathForShape(Shape shape) {
String generatedPath = "";
AffineTransform noTransformation = new AffineTransform();
PathIterator pathIterator = shape.getPathIterator(noTransformation);
while (!pathIterator.isDone()) {
float[] coords = new float[6];
int type = pathIterator.currentSegment(coords);
generatedPath += convertJavaToTikzPath(type, coords);
pathIterator.next();
}
return generatedPath;
}
private String convertJavaToTikzPath(int type, float[] coords) {
if (type < SEG_MOVETO || type > SEG_CLOSE) {
throw new IllegalStateException(String.format("Cannot process path segment type: %d (coordinates: %s)", type, Arrays.toString(coords)));
......
......@@ -253,12 +253,13 @@ public class PostvisualizationWindow extends JPanel implements Observer {
renderer);
ActionVisualization svgImg = new ActionGenerateSVG(Messages.getString("PostVis.btnSVGSnapshot.tooltip"), resources.getIcon("camera_svg.png", iconWidth, iconHeight),
renderer);
ActionVisualization tikzImg = new ActionGenerateTikz("tikz_snapshot", resources.getIcon("camera_tikz.png", iconWidth, iconHeight),
ActionVisualization tikzImg = new ActionGenerateTikz(Messages.getString("PostVis.btnTikZSnapshot.tooltip"), resources.getIcon("camera_tikz.png", iconWidth, iconHeight),
renderer);
// add new ImageGenerator Action ...
imgOptions.add(pngImg);
imgOptions.add(svgImg);
imgOptions.add(tikzImg);
// add Action to List ....
ActionVisualizationMenu imgDialog = new ActionVisualizationMenu(
......
......@@ -173,7 +173,7 @@ public class TextView extends JPanel implements IJsonView {
switch (attributeType) {
case MODEL:
ModelDefinition modelDefinition = JsonConverter.deserializeModelDefinition(json);
currentScenario.getScenarioStore().mainModel = modelDefinition.getMainModel();
currentScenario.getScenarioStore().setMainModel(modelDefinition.getMainModel());
currentScenario.setAttributesModel(modelDefinition.getAttributesList());
break;
case SIMULATION:
......@@ -220,7 +220,7 @@ public class TextView extends JPanel implements IJsonView {
switch (attributeType) {
case MODEL:
txtrTextfiletextarea.setText(StateJsonConverter.serializeMainModelAttributesModelBundle(
scenario.getModelAttributes(), scenario.getScenarioStore().mainModel));
scenario.getModelAttributes(), scenario.getScenarioStore().getMainModel()));
break;
case SIMULATION:
txtrTextfiletextarea
......
......@@ -60,7 +60,7 @@ public class AgentRender implements Renderer {
return new Color(Color.HSBtoRGB(hue, 1f, 0.75f));
}
private Color getColor(Pedestrian ped) {
public Color getColor(Pedestrian ped) {
if (ped.getGroupIds().isEmpty()) {
return defaultColor;
}
......@@ -74,7 +74,7 @@ public class AgentRender implements Renderer {
return c;
}
private VShape getShape(Pedestrian ped) {
public VShape getShape(Pedestrian ped) {
if (ped.getGroupIds().isEmpty()) {
return ped.getShape();
} else if (ped.getGroupIds().getFirst() == 1) {
......
......@@ -31,7 +31,6 @@ public class GroupSourceController extends SourceController {
if (!isSourceFinished(simTimeInSec)) {
if (simTimeInSec >= timeOfNextEvent || !groupsToSpawn.isEmpty()) {
determineNumberOfSpawnsAndNextEvent(simTimeInSec);
LinkedList<List<VPoint>> spawnGroups = new LinkedList<>();
if (sourceAttributes.isSpawnAtRandomPositions()) {
......@@ -40,7 +39,7 @@ public class GroupSourceController extends SourceController {
while (iter.hasNext()) {
int groupSize = iter.next();
List<VPoint> newGroup = spawnArray.getNextFreeGroup(groupSize, random, getDynElementsAtSource());
if (newGroup != null) {
if (newGroup.size() > 0) {
// add immediately to Scenario to update DynElementsAtSource
addElementToScenario(newGroup);
iter.remove();
......@@ -52,7 +51,12 @@ public class GroupSourceController extends SourceController {
Iterator<Integer> iter = groupsToSpawn.iterator();
while (iter.hasNext()) {
int groupSize = iter.next();
List<VPoint> newGroup = spawnArray.getNextGroup(groupSize, random);
List<VPoint> newGroup = spawnArray.getNextGroup(groupSize, random, getDynElementsAtSource());
if (newGroup.size() == 0)
throw new RuntimeException("Cannot spawn new Group. Source " + source.getId() + " is set " +
"to useFreeSpaceOnly == false but no space is left to spawn group without exactly" +
"overlapping with neighbours which can cause numerical problems. Use useFreeSpaceOnly == true (default)" +
"to queue groups.");
addElementToScenario(newGroup);
iter.remove();
}
......@@ -77,7 +81,12 @@ public class GroupSourceController extends SourceController {
Iterator<Integer> iter = groupsToSpawn.iterator();
while (iter.hasNext()) {
int groupSize = iter.next();
List<VPoint> newGroup = spawnArray.getNextGroup(groupSize);
List<VPoint> newGroup = spawnArray.getNextGroup(groupSize, getDynElementsAtSource());
if (newGroup == null)
throw new RuntimeException("Cannot spawn new Group. Source " + source.getId() + " is set " +
"to useFreeSpaceOnly == false but no space is left to spawn group without exactly" +
"overlapping with neighbours which can cause numerical problems. Use useFreeSpaceOnly == true (default)" +
"to queue groups.");
addElementToScenario(newGroup);
iter.remove();
}
......
......@@ -69,7 +69,7 @@ public class Simulation {
this.name = name;
this.mainModel = mainModel;
this.scenarioStore = scenarioStore;
this.attributesSimulation = scenarioStore.attributesSimulation;
this.attributesSimulation = scenarioStore.getAttributesSimulation();
this.attributesAgent = scenarioStore.getTopography().getAttributesPedestrian();
this.sourceControllers = new LinkedList<>();
this.targetControllers = new LinkedList<>();
......
......@@ -9,8 +9,6 @@ import org.vadere.util.geometry.shapes.VPoint;
import java.util.LinkedList;
import java.util.Random;
import jdk.nashorn.internal.ir.annotations.Ignore;
public class SingleSourceController extends SourceController {
private int numberToSpawn;
......@@ -36,9 +34,7 @@ public class SingleSourceController extends SourceController {
numberToSpawn -= spawnPoints.size();
assert (numberToSpawn >= 0);
} else {
for (int i = 0; i < numberToSpawn; i++) {
spawnPoints.add(spawnArray.getNextRandomSpawnPoint(random));
}
spawnPoints = spawnArray.getNextRandomSpawnPoints(numberToSpawn, random, getDynElementsAtSource());
numberToSpawn -= spawnPoints.size();
assert (numberToSpawn >= 0);
}
......@@ -50,9 +46,7 @@ public class SingleSourceController extends SourceController {
numberToSpawn -= spawnPoints.size();
assert (numberToSpawn >= 0);
} else {
for (int i = 0; i < numberToSpawn; i++) {
spawnPoints.add(spawnArray.getNextSpawnPoint());
}
spawnPoints = spawnArray.getNextSpawnPoints(numberToSpawn, getDynElementsAtSource());
numberToSpawn -= spawnPoints.size();
assert (numberToSpawn >= 0);
}
......
......@@ -61,9 +61,9 @@ public class ScenarioBuilder {
// TODO: duplicated code
if(AttributesSimulation.class == clazz){
builder = new AttributesBuilder<>((E)store.attributesSimulation);
builder = new AttributesBuilder<>((E)store.getAttributesSimulation());
builder.setField(fieldName, value);
store.attributesSimulation = (AttributesSimulation) builder.build();
store.setAttributesSimulation((AttributesSimulation) builder.build());
}
else if(AttributesAgent.class == clazz){
builder = new AttributesBuilder<>((E) store.getTopography().getAttributesPedestrian());
......@@ -84,8 +84,8 @@ public class ScenarioBuilder {
else {
builder = new AttributesBuilder<>(store.getAttributes(clazz));
builder.setField(fieldName, value);
store.attributesList.removeIf(attributes -> attributes.getClass() == clazz);
store.attributesList.add(builder.build());
store.removeAttributesIf(attributes -> attributes.getClass() == clazz);
store.addAttributes(builder.build());
}
}
......
......@@ -69,8 +69,9 @@ public class VadereConsole {
new ScenarioRun(scenario, null).run();
}
} catch (IOException e) {
} catch (Exception e) {
logger.error(e);
System.exit(-1);
}
}
......
......@@ -25,7 +25,7 @@ public class MainModelBuilder {
public void createModelAndRandom()
throws ClassNotFoundException, InstantiationException, IllegalAccessException {