- simulation result handling to use builder pattern
- result to use only arrays instead of maps
- updated references to other classes
This commit is contained in:
2025-02-12 14:33:32 +01:00
parent e232df7c5a
commit ffdb3021fd
6 changed files with 210 additions and 85 deletions

View File

@@ -6,7 +6,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;
@@ -75,26 +74,25 @@ public class CsvResult {
var results = new ArrayList<Result>();
try (var scan = new Scanner(input)) {
var headerOrder = CsvResult.extractHeaderPositions(scan.nextLine());
var nodes = new HashMap<String, NodeStats>();
var seed = 0L;
var builder = new Result.Builder();
while (scan.hasNextLine()) {
var line = scan.nextLine().split(",");
var currentSeed = Long.parseLong(line[0]);
var node = line[1];
if (currentSeed != seed && seed != 0) {
results.add(new Result(seed, 0.0, 0.0, nodes));
nodes = new HashMap<>();
if (builder.seed != currentSeed && builder.seed != 0) {
results.add(builder.build());
builder.reset();
}
seed = currentSeed;
var node = line[1];
var copy = Arrays.copyOfRange(line, 2, line.length);
var stats = CsvResult.statsFromCSV(headerOrder, copy);
nodes.put(node, stats);
builder.seed(currentSeed).addNode(node, stats);
}
results.add(new Result(seed, 0.0, 0.0, nodes));
results.add(builder.build());
}
return results;
}

View File

@@ -1,8 +1,7 @@
package net.berack.upo.valpre.sim.stats;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -14,7 +13,8 @@ import java.util.Map.Entry;
* length, the busy time, and the response time.
*/
public class Result implements Iterable<Entry<String, NodeStats>> {
public final Map<String, NodeStats> nodes;
public final String[] nodes;
public final NodeStats[] stats;
public final long seed;
public final double simulationTime;
public final double timeElapsedMS;
@@ -28,24 +28,59 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
* @param elapsed the real time elapsed while running the simulation in ms
* @param nodes all the stats collected by the simulation saved per node
*/
public Result(long seed, double time, double elapsed, Map<String, NodeStats> nodes) {
private Result(long seed, double time, double elapsed, String[] nodes, NodeStats[] stats) {
this.seed = seed;
this.simulationTime = time;
this.timeElapsedMS = elapsed;
this.nodes = nodes;
this.stats = stats;
}
public NodeStats getStat(String node) {
for (var i = 0; i < this.nodes.length; i++)
if (this.nodes[i].equals(node))
return this.stats[i];
throw new IllegalArgumentException("Node not found");
}
@Override
public String toString() {
return buildPrintable(this.seed, this.simulationTime, this.timeElapsedMS, this.nodes);
return buildPrintable(this.seed, this.simulationTime, this.timeElapsedMS, this.nodes, this.stats);
}
@Override
public java.util.Iterator<Entry<String, NodeStats>> iterator() {
return this.nodes.entrySet().iterator();
public Iterator<Entry<String, NodeStats>> iterator() {
return new Iterator<>() {
private int index = 0;
@Override
public boolean hasNext() {
return this.index < Result.this.nodes.length;
}
@Override
public Entry<String, NodeStats> next() {
var node = Result.this.nodes[this.index];
var stat = Result.this.stats[this.index];
this.index++;
return Map.entry(node, stat);
}
};
}
private static String buildPrintable(long seed, double simTime, double timeMS, Map<String, NodeStats> nodes) {
/**
* Create a string representation of the result. It includes the seed, the final
* time of the simulation, the real time elapsed while running the simulation in
* ms, and the stats of each node.
*
* @param seed the initial seed used by the simulation
* @param simTime the final time of the simulation
* @param timeMS the real time elapsed while running the simulation in ms
* @param nodes the names of the nodes
* @param stats the stats of each node
* @return a string representation of the result
*/
private static String buildPrintable(long seed, double simTime, double timeMS, String[] nodes, NodeStats[] stats) {
var size = (int) Math.ceil(Math.max(Math.log10(simTime), 1));
var iFormat = "%" + size + ".0f";
var fFormat = "%" + (size + 4) + ".3f";
@@ -59,18 +94,19 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
var table = new ConsoleTable("Node", "Departures", "Avg Queue", "Avg Wait", "Avg Response", "Throughput",
"Utilization %", "Unavailable %", "Last Event");
for (var entry : nodes.entrySet()) {
var stats = entry.getValue();
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
var stat = stats[i];
table.addRow(
entry.getKey(),
iFormat.formatted(stats.numDepartures),
fFormat.formatted(stats.avgQueueLength),
fFormat.formatted(stats.avgWaitTime),
fFormat.formatted(stats.avgResponse),
fFormat.formatted(stats.throughput),
fFormat.formatted(stats.utilization * 100),
fFormat.formatted(stats.unavailable * 100),
fFormat.formatted(stats.lastEventTime));
node,
iFormat.formatted(stat.numDepartures),
fFormat.formatted(stat.avgQueueLength),
fFormat.formatted(stat.avgWaitTime),
fFormat.formatted(stat.avgResponse),
fFormat.formatted(stat.throughput),
fFormat.formatted(stat.utilization * 100),
fFormat.formatted(stat.unavailable * 100),
fFormat.formatted(stat.lastEventTime));
}
builder.append(table);
@@ -87,7 +123,8 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
public final long seed;
private double avgSimulationTime = 0.0d;
private double avgTimeElapsedMS = 0.0d;
private Map<String, NodeStats.Summary> stats = new HashMap<>();
private String[] nodes;
private NodeStats.Summary[] stats;
private List<Result> runs = new ArrayList<>();
/**
@@ -95,8 +132,9 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
*
* @param seed the initial seed used by the simulation
*/
public Summary(long seed) {
public Summary(long seed, String[] nodes) {
this.seed = seed;
this.setup(nodes);
}
/**
@@ -105,31 +143,47 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
* @param results the results to summarize
*/
public Summary(List<Result> results) {
this(results.get(0).seed);
var first = results.get(0);
this.seed = first.seed;
this.setup(first.nodes);
for (var result : results)
this.add(result);
}
/**
* Sets up the summary with the nodes. It initializes the statistics of the
* nodes to 0. It is used by the constructors.
*/
private void setup(String[] nodes) {
this.nodes = nodes;
this.stats = new NodeStats.Summary[this.nodes.length];
for (var i = 0; i < this.nodes.length; i++)
this.stats[i] = new NodeStats.Summary();
}
/**
* Adds the result to the summary. It updates the average simulation time and
* the average time elapsed. It also updates the statistics of the nodes.
*
* @param result the result to add
* @throws IllegalArgumentException if the result is null or the nodes do not
* match
*/
public void add(Result result) {
if (result == null)
throw new IllegalArgumentException("Result cannot be null");
if (result.nodes.length != this.nodes.length)
throw new IllegalArgumentException("Nodes do not match");
var n = this.runs.size() + 1;
this.runs.add(result);
this.avgSimulationTime += (result.simulationTime - this.avgSimulationTime) / n;
this.avgTimeElapsedMS += (result.timeElapsedMS - this.avgTimeElapsedMS) / n;
for (var entry : result.nodes.entrySet()) {
var node = entry.getKey();
var stats = entry.getValue();
var summary = this.stats.computeIfAbsent(node, _ -> new NodeStats.Summary());
summary.update(stats);
for (var i = 0; i < this.nodes.length; i++) {
var stats = this.stats[i];
var summary = result.stats[i];
stats.update(summary);
}
}
@@ -156,8 +210,8 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
*
* @return the nodes of the summary
*/
public Collection<String> getNodes() {
return this.stats.keySet();
public List<String> getNodes() {
return List.of(this.nodes);
}
/**
@@ -168,10 +222,10 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
* @throws IllegalArgumentException if the node is not found
*/
public NodeStats.Summary getSummaryOf(String node) {
var stat = this.stats.get(node);
if (stat == null)
throw new IllegalArgumentException("Node not found");
return stat;
for (var i = 0; i < this.nodes.length; i++)
if (this.nodes[i].equals(node))
return this.stats[i];
throw new IllegalArgumentException("Node not found");
}
/**
@@ -185,11 +239,78 @@ public class Result implements Iterable<Entry<String, NodeStats>> {
@Override
public String toString() {
var stats = new HashMap<String, NodeStats>();
for (var entry : this.stats.entrySet())
stats.put(entry.getKey(), entry.getValue().average);
var stats = new NodeStats[this.nodes.length];
for (var i = 0; i < this.nodes.length; i++)
stats[i] = this.stats[i].average;
return buildPrintable(this.seed, this.avgSimulationTime, this.avgTimeElapsedMS, stats);
return buildPrintable(this.seed, this.avgSimulationTime, this.avgTimeElapsedMS, this.nodes, stats);
}
}
/**
* A builder class to create a new result object. It allows to set the seed, the
* simulation time, the time elapsed, the nodes, and the stats.
*/
public static class Builder {
public long seed;
public double simulationTime;
public double timeElapsedMS;
private List<String> nodes = new ArrayList<>();
private List<NodeStats> stats = new ArrayList<>();
/**
* Resets the builder to its initial state.
*/
public Builder reset() {
this.seed = 0;
this.simulationTime = 0.0d;
this.timeElapsedMS = 0.0d;
this.nodes.clear();
this.stats.clear();
return this;
}
/**
* Sets the seed of the result.
*
* @param seed the seed to set
* @return the builder
*/
public Builder seed(long seed) {
this.seed = seed;
return this;
}
/**
* Sets the simulation time and the time elapsed of the result.
*
* @param simulationTime the simulation time to set
* @param timeElapsedMS the time elapsed to set
* @return the builder
*/
public Builder times(double simulationTime, double timeElapsedMS) {
this.simulationTime = simulationTime;
this.timeElapsedMS = timeElapsedMS;
return this;
}
/**
* Adds a node and its stats to the result.
*
* @param node the node to add
* @param stat the stats of the node
* @return the builder
*/
public Builder addNode(String node, NodeStats stat) {
this.nodes.add(node);
this.stats.add(stat);
return this;
}
public Result build() {
var nodes = this.nodes.toArray(String[]::new);
var stats = this.stats.toArray(NodeStats[]::new);
return new Result(this.seed, this.simulationTime, this.timeElapsedMS, nodes, stats);
}
}
}