Refatoring

- separated net topology from the simulation nodes
- moved files in a better hierarchy
This commit is contained in:
2025-01-22 17:04:53 +01:00
parent b9cd0ddeb7
commit 69b8e5710b
10 changed files with 511 additions and 408 deletions

View File

@@ -0,0 +1,89 @@
package net.berack.upo.valpre.sim;
/**
* Criteria to determine when to end the simulation.
*/
public interface EndCriteria {
/**
* Determines if the simulation should end based on the statistics of the nodes.
*
* @param run The current run of the network.
* @return True if the simulation should end, false otherwise.
*/
public boolean shouldEnd(Simulation run);
/**
* Ends the simulation when the given node has reached the specified number of
* arrivals.
*/
public static class MaxArrivals implements EndCriteria {
private final String nodeName;
private final int maxArrivals;
/**
* Creates a new criteria to end the simulation when the given node has reached
* the specified number of arrivals.
*
* @param nodeName The name of the node to check.
* @param maxArrivals The maximum number of arrivals to wait for.
*/
public MaxArrivals(String nodeName, int maxArrivals) {
this.nodeName = nodeName;
this.maxArrivals = maxArrivals;
}
@Override
public boolean shouldEnd(Simulation run) {
return run.getNode(nodeName).stats.numArrivals >= this.maxArrivals;
}
}
/**
* Ends the simulation when the given node has reached the specified number of
* departures.
*/
public static class MaxDepartures implements EndCriteria {
private final String nodeName;
private final int maxDepartures;
/**
* Creates a new criteria to end the simulation when the given node has reached
* the specified number of departures.
*
* @param nodeName The name of the node to check.
* @param maxDepartures The maximum number of departures to wait for.
*/
public MaxDepartures(String nodeName, int maxDepartures) {
this.nodeName = nodeName;
this.maxDepartures = maxDepartures;
}
@Override
public boolean shouldEnd(Simulation run) {
return run.getNode(nodeName).stats.numDepartures >= this.maxDepartures;
}
}
/**
* Ends the simulation when the given node has reached the specified number of
* departures.
*/
public static class MaxTime implements EndCriteria {
private final double maxTime;
/**
* Creates a new criteria to end the simulation when the given node has reached
* the specified number of departures.
*
* @param maxTime The maximum time to wait for.
*/
public MaxTime(double maxTime) {
this.maxTime = maxTime;
}
@Override
public boolean shouldEnd(Simulation run) {
return run.getTime() >= this.maxTime;
}
}
}

View File

@@ -0,0 +1,75 @@
package net.berack.upo.valpre.sim;
/**
* Represents an event in the simulation.
*/
public class Event implements Comparable<Event> {
public final double time;
public final Type type;
public final ServerNode node;
/**
* Create a new event.
*
* @param type The type of event.
* @param node The node that the event is associated with.
* @param time The time at which the event occurs.
*/
private Event(Type type, ServerNode node, double time) {
this.type = type;
this.time = time;
this.node = node;
}
@Override
public int compareTo(Event other) {
if (this.time < other.time)
return -1;
if (this.time == other.time)
return 0;
return 1;
}
/**
* Create a new event.
*
* @param node The node that the event is associated with.
* @param time The time at which the event occurs.
* @param type The type of event.
*
* @return The new event.
*/
public static Event newType(ServerNode node, double time, Type type) {
return new Event(type, node, time);
}
/**
* Create a new arrival event.
*
* @param node The node that the event is associated with.
* @param time The time at which the event occurs.
* @return The new event.
*/
public static Event newArrival(ServerNode node, double time) {
return new Event(Type.ARRIVAL, node, time);
}
/**
* Create a new departure event.
*
* @param node The node that the event is associated with.
* @param time The time at which the event occurs.
* @return The new event.
*/
public static Event newDeparture(ServerNode node, double time) {
return new Event(Type.DEPARTURE, node, time);
}
/**
* The type of event.
*/
public static enum Type {
ARRIVAL,
DEPARTURE,
}
}

View File

@@ -0,0 +1,131 @@
package net.berack.upo.valpre.sim;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;
import net.berack.upo.valpre.rand.Rng;
/**
* TODO
*/
public final class Net {
private final HashMap<String, Integer> indices = new HashMap<>();
private final List<ServerNode> servers = new ArrayList<>();
private final List<List<Connection>> connections = new ArrayList<>();
private final List<Double> sum = new ArrayList<>();
/**
* Adds a new server node to the network.
* The unique identifier for the nodes is the name and, if you try to add a node
* that has the same name of another, then the method will return an exception
*
* @param node The server node to add.
* @throws IllegalArgumentException if the node already exist
*/
public void addNode(ServerNode node) {
if (this.indices.containsKey(node.name))
throw new IllegalArgumentException("Node already exist");
this.servers.add(node);
this.indices.put(node.name, this.servers.size() - 1);
this.connections.add(new ArrayList<>());
this.sum.add(0.0);
}
/**
* Adds a connection between the nodes with the given weight to select it.
* The weight must be > 0 and the nodes must be already added to the net.
* If the connection is already present then the new weight is used.
*
* @param parent The parent node.
* @param child The child node to add.
* @param weight The probability of the child node.
* @throws NullPointerException if one of the two nodes are not in the net
* @throws IllegalArgumentException if the weight is negative or zero
*/
public void addConnection(String parent, String child, double weight) {
var nodeP = this.indices.get(parent);
var nodeC = this.indices.get(child);
if (weight <= 0)
throw new IllegalArgumentException("Weight must be > 0");
if (nodeP == nodeC && nodeP == null)
throw new NullPointerException("One of the nodes does not exist");
var list = this.connections.get(nodeP);
for (var conn : list) {
if (conn.index == nodeC) {
conn.weight = weight;
return;
}
}
list.add(new Connection(nodeC, weight));
}
/**
* Get the total number of the nodes in the net
*
* @return the size of the net
*/
public int size() {
return this.servers.size();
}
/**
* Get one of the child nodes from the parent specified. If the index is out of
* bounds then an
* exception is thrown. If the node has no child then null is returned;
*
* @param parent the parent node
* @param rng the random number generator used for getting one of the child
* @return the resultig node
*/
public ServerNode getChildOf(ServerNode parent, Rng rng) {
var index = this.indices.get(parent.name);
var random = rng.random();
for (var conn : this.connections.get(index)) {
random -= conn.weight / 1.0;
if (random <= 0) {
return this.servers.get(conn.index);
}
}
return null;
}
/**
* Normalizes the weights in each connections so that their sum equals 1.
* This method should be called by the user if they have inserted weights that
* are not summing to 1 or are unsure.
*/
public void normalizeWeights() {
for (var list : this.connections) {
var sum = 0.0d;
for (var conn : list)
sum += conn.weight;
for (var conn : list)
conn.weight /= sum;
}
}
/**
* Apply a consumer to all the nodes. The implementation uses a stream and for
* this reason you should consider to make thread safe the consumer.
*
* @param consumer a function that takes in input a ServerNode
*/
public void forEachNode(Consumer<ServerNode> consumer) {
this.servers.stream().forEach(consumer);
}
public static class Connection {
public final int index;
public double weight;
private Connection(int index, double weight) {
this.index = index;
this.weight = weight;
}
}
}

View File

@@ -0,0 +1,96 @@
package net.berack.upo.valpre.sim;
import net.berack.upo.valpre.rand.Distribution;
import net.berack.upo.valpre.rand.Rng;
/**
* Represents a node in the network. It can be a source, a queue, or a sink
* based on the configuration passed as parameters.
*/
public class ServerNode {
public final String name;
public final int maxServers;
public final int spawnArrivals;
public final Distribution distribution;
/**
* Creates a source node with the given name and distribution.
* It swpawns infinite arrivals (Integer.MAX_VALUE) that are served by infinite
* servers (Integer.MAX_VALUE).
*
* @param name The name of the node.
* @param distribution The distribution of the inter-arrival times.
* @return The created source node.
*/
public static ServerNode createSource(String name, Distribution distribution) {
return new ServerNode(name, Integer.MAX_VALUE, distribution, Integer.MAX_VALUE);
}
/**
* Creates a source node with the given name, distribution, and number of
* arrivals to spawn that are served by infinite servers (Integer.MAX_VALUE).
*
* @param name The name of the node.
* @param distribution The distribution of the inter-arrival times.
* @param spawnArrivals The number of arrivals to spawn.
* @return The created source node.
*/
public static ServerNode createLimitedSource(String name, Distribution distribution, int spawnArrivals) {
return new ServerNode(name, Integer.MAX_VALUE, distribution, spawnArrivals);
}
/**
* Creates a queue node with the given name, maximum number of servers, and
* distribution.
*
* @param name The name of the node.
* @param maxServers The maximum number of servers in the queue.
* @param distribution The distribution of the service times.
* @return The created queue node.
*/
public static ServerNode createQueue(String name, int maxServers, Distribution distribution) {
return new ServerNode(name, maxServers, distribution, 0);
}
/**
* Creates a generic node with the given name and distribution.
*
* @param name The name of the node.
* @param maxServers The maximum number of servers in the queue.
* @param distribution The distribution of the service times.
* @param spawnArrivals The number of arrivals to spawn.
*/
public ServerNode(String name, int maxServers, Distribution distribution, int spawnArrivals) {
this.name = name;
this.maxServers = maxServers;
this.distribution = distribution;
this.spawnArrivals = spawnArrivals;
}
/**
* Gets a positive sample from the distribution.
* This is useful if you need to generate a positive value from a distribution
* that can generate negative values. For example, the normal distribution.
*
* @param rng The random number generator to use.
* @return A positive sample from the distribution.
*/
public double getPositiveSample(Rng rng) {
double sample;
do {
sample = this.distribution.sample(rng);
} while (sample < 0);
return sample;
}
/**
* Determines if the node should spawn an arrival based on the number of
* arrivals.
*
* @param numArrivals The number of arrivals to check against.
* @return True if the node should spawn an arrival, false otherwise.
*/
public boolean shouldSpawnArrival(double numArrivals) {
return this.spawnArrivals > numArrivals;
}
}

View File

@@ -0,0 +1,226 @@
package net.berack.upo.valpre.sim;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import net.berack.upo.valpre.rand.Rng;
import net.berack.upo.valpre.sim.stats.NetStatistics;
import net.berack.upo.valpre.sim.stats.NetStatistics.Statistics;
/**
* Process an entire run of the simulation.
*/
public final class Simulation {
private final Net net;
private final Map<String, NodeBehavior> nodes;
private final PriorityQueue<Event> fel;
private final EndCriteria[] criterias;
private final long timeStartedNano;
private final long seed;
private final Rng rng;
private double time;
/**
* Creates a new run of the simulation with the given nodes and random number
* generator.
*
* @param nodes The nodes in the network.
* @param rng The random number generator to use.
* @param criterias when the simulation has to end.
*/
public Simulation(Net net, Rng rng, EndCriteria... criterias) {
this.net = net;
this.nodes = new HashMap<>();
this.fel = new PriorityQueue<>();
this.criterias = criterias;
this.timeStartedNano = System.nanoTime();
this.seed = rng.getSeed();
this.rng = rng;
this.time = 0.0d;
// Initial arrivals (if spawned)
net.forEachNode(node -> {
this.nodes.put(node.name, new NodeBehavior());
if (node.shouldSpawnArrival(0))
this.addArrival(node);
});
}
/**
* Runs the simulation until a given criteria is met.
*
* @return The final statistics the network.
*/
public NetStatistics.RunResult run() {
while (!this.hasEnded())
this.processNextEvent();
return this.endSimulation();
}
/**
* Processes the next event in the future event list.
* This method will throw NullPointerException if there are no more events.
* You should check if the simulation has ended before calling this method.
*
* @see #hasEnded()
*/
public void processNextEvent() {
var event = fel.poll();
var node = event.node;
var behaviour = this.nodes.get(node.name);
this.time = event.time;
switch (event.type) {
case ARRIVAL -> {
if (behaviour.updateArrival(event.time, node.maxServers))
this.addDeparture(node);
}
case DEPARTURE -> {
if (behaviour.updateDeparture(event.time))
this.addDeparture(node);
var next = this.net.getChildOf(node, this.rng);
if (next != null) {
this.addArrival(next);
}
if (node.shouldSpawnArrival(behaviour.stats.numArrivals)) {
this.addArrival(node);
}
}
}
}
/**
* Ends the simulation and returns the statistics of the network.
*
* @return The statistics of the network.
*/
public NetStatistics.RunResult endSimulation() {
var elapsed = System.nanoTime() - this.timeStartedNano;
var nodes = new HashMap<String, Statistics>();
for (var entry : this.nodes.entrySet())
nodes.put(entry.getKey(), entry.getValue().stats);
return new NetStatistics.RunResult(this.seed, this.time, elapsed, nodes);
}
/**
* Get the current time.
*
* @return a double representing the current time of the simulation.
*/
public double getTime() {
return this.time;
}
/**
* Get the node requested by the name passed as a string.
*
* @param node the name of the node
* @return the node
*/
public NodeBehavior getNode(String node) {
return this.getNode(node);
}
/**
* Adds an arrival event to the future event list. The event is created based
* on the given node, and no delay is added.
*
* @param node The node to create the event for.
*/
public void addArrival(ServerNode node) {
var event = Event.newArrival(node, this.time);
fel.add(event);
}
/**
* Adds a departure event to the future event list. The event is created based
* on the given node, and the delay is determined by the node's distribution.
*
* @param node The node to create the event for.
*/
public void addDeparture(ServerNode node) {
var delay = node.getPositiveSample(this.rng);
var event = Event.newDeparture(node, this.time + delay);
fel.add(event);
}
/**
* Determines if the simulation has finshed based on the given criteria.
*
* @return True if the simulation should end, false otherwise.
*/
public boolean hasEnded() {
if (fel.isEmpty()) {
return true;
}
for (var c : this.criterias) {
if (c.shouldEnd(this)) {
return true;
}
}
return false;
}
/**
* Represents a summary of the behavior of a server node in the network.
* It is used by the simulation to track the number of arrivals and departures,
* the maximum queue length, the busy time, and the response time.
*/
public static class NodeBehavior {
public int numServerBusy = 0;
public final Statistics stats = new Statistics();
private final ArrayDeque<Double> queue = new ArrayDeque<>();
/**
* TODO
*
* @param time
* @param maxServers
* @return
*/
public boolean updateArrival(double time, int maxServers) {
var total = this.stats.averageQueueLength * this.stats.numArrivals;
this.queue.add(time);
this.stats.numArrivals++;
this.stats.averageQueueLength = (total + this.queue.size()) / this.stats.numArrivals;
this.stats.maxQueueLength = Math.max(this.stats.maxQueueLength, this.queue.size());
var startDeparture = maxServers > this.numServerBusy;
if (startDeparture) {
this.numServerBusy++;
} else {
this.stats.busyTime += time - this.stats.lastEventTime;
}
this.stats.lastEventTime = time;
return startDeparture;
}
/**
* TODO
*
* @param time
* @return
*/
public boolean updateDeparture(double time) {
var startService = this.queue.poll();
var response = time - startService;
var startDeparture = this.queue.size() >= this.numServerBusy;
if (!startDeparture) {
this.numServerBusy--;
}
this.stats.numDepartures++;
this.stats.responseTime += response;
this.stats.busyTime += time - this.stats.lastEventTime;
this.stats.lastEventTime = time;
return startDeparture;
}
}
}

View File

@@ -0,0 +1,85 @@
package net.berack.upo.valpre.sim;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import net.berack.upo.valpre.rand.Rng;
import net.berack.upo.valpre.rand.Rngs;
import net.berack.upo.valpre.sim.stats.NetStatistics;
import net.berack.upo.valpre.sim.stats.NetStatistics.RunResult;
/**
* A network simulation that uses a discrete event simulation to model the
* behavior of a network of servers.
*/
public class SimulationMultiple {
private final Net net;
public SimulationMultiple(Net net) {
this.net = net;
}
/**
* Run the simualtion multiple times with the given seed and number of runs.
* The runs are calculated one after the other. For a parallel run see
* {@link #runParallel(long, int, EndCriteria...)}.
*
* @param seed The seed to use for the random number generator.
* @param runs The number of runs to perform.
* @param criterias The criteria to determine when to end the simulation. If
* null then the simulation will run until there are no more
* events.
* @return The statistics the network.
*/
public NetStatistics run(long seed, int runs, EndCriteria... criterias) {
var rng = new Rng(seed);
var stats = new RunResult[runs];
for (int i = 0; i < runs; i++) {
var sim = new Simulation(this.net, rng, criterias);
stats[i] = sim.run();
}
return new NetStatistics(stats);
}
/**
* Runs the simulation multiple times with the given seed and number of runs.
* The runs are calculated in parallel using the given number of threads.
* The maximum number of threads are determined by the available processors
* and the number of runs.
*
* @param seed The seed to use for the random number generator.
* @param runs The number of runs to perform.
* @param criterias The criteria to determine when to end the simulation. If
* null then the simulation will run until there are no more
* events.
* @return The statistics the network.
* @throws InterruptedException If the threads are interrupted.
* @throws ExecutionException If the one of the threads has been aborted.
*/
public NetStatistics runParallel(long seed, int runs, EndCriteria... criterias)
throws InterruptedException, ExecutionException {
var rngs = new Rngs(seed);
var results = new NetStatistics.RunResult[runs];
var futures = new Future[runs];
var numThreads = Math.min(runs, Runtime.getRuntime().availableProcessors());
try (var threads = Executors.newFixedThreadPool(numThreads)) {
for (int i = 0; i < runs; i++) {
final var id = i;
futures[i] = threads.submit(() -> {
var sim = new Simulation(this.net, rngs.getRng(id), criterias);
results[id] = sim.run();
});
}
for (var i = 0; i < runs; i++) {
futures[i].get();
}
return new NetStatistics(results);
}
}
}

View File

@@ -0,0 +1,50 @@
package net.berack.upo.valpre.sim.stats;
/**
* TODO
*/
public class ConsoleTable {
private StringBuilder builder = new StringBuilder();
private final int maxLen;
private final String border;
/**
* TODO
*
* @param header
*/
public ConsoleTable(String... header) {
var max = 0;
for (var name : header)
max = Math.max(max, name.length());
this.maxLen = max + 2;
this.border = ("+" + "".repeat(maxLen)).repeat(header.length) + "+\n";
this.builder.append(border);
this.addRow(header);
}
/**
* TODO
*
* @param values
*/
public void addRow(String... values) {
for (var val : values) {
var diff = maxLen - val.length();
var first = (int) Math.ceil(diff / 2.0);
builder.append('║');
builder.append(" ".repeat(first));
builder.append(val);
builder.append(" ".repeat(diff - first));
}
builder.append("\n");
builder.append(border);
}
@Override
public String toString() {
return builder.toString();
}
}

View File

@@ -0,0 +1,230 @@
package net.berack.upo.valpre.sim.stats;
import java.util.HashMap;
import java.util.Map;
/**
* TODO
*/
public class NetStatistics {
public final RunResult[] runs;
public final RunResult average;
public final RunResult variance;
/**
* TODO
*
* @param runs
*/
public NetStatistics(RunResult... runs) {
this.runs = runs;
this.average = calcAvg(runs);
this.variance = calcVar(this.average, runs);
}
/**
* TODO
*
* @param runs
* @return
*/
public static RunResult calcAvg(RunResult... runs) {
var avgTime = 0.0d;
var avgElapsed = 0L;
var nodes = new HashMap<String, Statistics>();
for (var run : runs) {
avgTime += run.simulationTime;
avgElapsed += run.timeElapsedNano;
for (var entry : run.nodes.entrySet()) {
var stat = nodes.computeIfAbsent(entry.getKey(), _ -> new Statistics());
var other = entry.getValue();
stat.numDepartures += other.numDepartures;
stat.numArrivals += other.numArrivals;
stat.busyTime += other.busyTime;
stat.responseTime += other.responseTime;
stat.lastEventTime += other.lastEventTime;
stat.averageQueueLength += other.averageQueueLength;
stat.maxQueueLength = Math.max(stat.maxQueueLength, other.maxQueueLength);
}
}
avgTime /= runs.length;
avgElapsed /= runs.length;
for (var stat : nodes.values()) {
stat.numDepartures /= runs.length;
stat.numArrivals /= runs.length;
stat.busyTime /= runs.length;
stat.responseTime /= runs.length;
stat.lastEventTime /= runs.length;
stat.averageQueueLength /= runs.length;
}
return new RunResult(runs[0].seed, avgTime, avgElapsed, nodes);
}
/**
* TODO
*
* @param avg
* @param runs
* @return
*/
public static RunResult calcVar(RunResult avg, RunResult... runs) {
var varTime = 0.0d;
var varElapsed = 0L;
var nodes = new HashMap<String, Statistics>();
for (var run : runs) {
varTime += Math.pow(run.simulationTime - avg.simulationTime, 2);
varElapsed += Math.pow(run.timeElapsedNano - avg.simulationTime, 2);
for (var entry : run.nodes.entrySet()) {
var stat = nodes.computeIfAbsent(entry.getKey(), _ -> new Statistics());
var average = avg.nodes.get(entry.getKey());
var other = entry.getValue();
stat.numDepartures += Math.pow(other.numDepartures - average.numDepartures, 2);
stat.numArrivals += Math.pow(other.numArrivals - average.numArrivals, 2);
stat.busyTime += Math.pow(other.busyTime - average.busyTime, 2);
stat.responseTime += Math.pow(other.responseTime - average.responseTime, 2);
stat.lastEventTime += Math.pow(other.lastEventTime - average.lastEventTime, 2);
stat.averageQueueLength += Math.pow(other.averageQueueLength - average.averageQueueLength, 2);
}
}
varTime /= runs.length - 1;
varElapsed /= runs.length - 1;
for (var stat : nodes.values()) {
stat.numDepartures /= runs.length - 1;
stat.numArrivals /= runs.length - 1;
stat.busyTime /= runs.length - 1;
stat.responseTime /= runs.length - 1;
stat.lastEventTime /= runs.length - 1;
stat.averageQueueLength /= runs.length - 1;
}
return new RunResult(runs[0].seed, varTime, varElapsed, nodes);
}
/**
* Represents the statistics of a network simulation.
* It is used by the simulation to track the behavior of the network and its
* nodes, including the number of arrivals and departures, the maximum queue
* length, the busy time, and the response time.
*/
public static class RunResult {
public final Map<String, Statistics> nodes;
public final long seed;
public final double simulationTime;
public final long timeElapsedNano;
/**
* Creates a new statistics object for the given collection of server nodes and
* random number generator.
*
* @param nodes The collection of server nodes to track.
* @param rng The random number generator to use.
*/
public RunResult(long seed, double time, long elapsed, Map<String, Statistics> nodes) {
this.seed = seed;
this.simulationTime = time;
this.timeElapsedNano = elapsed;
this.nodes = nodes;
}
/**
* Print a summary of the statistics to the console.
* The summary includes the seed, the simulation time, the elapsed time, and
* the statistics for each node in the network.
*/
public String getSummary() {
var size = (int) Math.ceil(Math.log10(this.simulationTime));
var iFormat = "%" + size + ".0f";
var fFormat = "%" + (size + 4) + ".3f";
var builder = new StringBuilder();
for (var entry : this.nodes.entrySet()) {
var stats = entry.getValue();
var busy = stats.busyTime * 100 / stats.lastEventTime;
var avgResp = stats.responseTime / stats.numDepartures;
builder.append("===== " + entry.getKey() + " =====\n");
builder.append(String.format(" Arrivals: \t" + iFormat + "\n", stats.numArrivals));
builder.append(String.format(" Departures:\t" + iFormat + "\n", stats.numDepartures));
builder.append(String.format(" Max Queue: \t" + iFormat + "\n", stats.maxQueueLength));
builder.append(String.format(" Avg Queue: \t" + fFormat + "\n", stats.averageQueueLength));
builder.append(String.format(" Response: \t" + fFormat + "\n", avgResp));
builder.append(String.format(" Busy %%: \t" + fFormat + "\n", busy));
builder.append(String.format(" Last Event:\t" + fFormat + "\n", stats.lastEventTime));
}
return builder.toString();
}
/**
* TODO
*/
public String getSummaryAsTable() {
var size = (int) Math.ceil(Math.log10(this.simulationTime));
var iFormat = "%" + size + ".0f";
var fFormat = "%" + (size + 4) + ".3f";
String[] h = { "Node", "Arrivals", "Departures", "Max Queue", "Avg Queue", "Response", "Busy %",
"Last Event" };
var table = new ConsoleTable(h);
for (var entry : this.nodes.entrySet()) {
var stats = entry.getValue();
table.addRow(
entry.getKey(),
String.format(iFormat, stats.numArrivals),
String.format(iFormat, stats.numDepartures),
String.format(iFormat, stats.maxQueueLength),
String.format(fFormat, stats.averageQueueLength),
String.format(fFormat, stats.responseTime / stats.numDepartures),
String.format(fFormat, stats.busyTime * 100 / stats.lastEventTime),
String.format(fFormat, stats.lastEventTime));
}
return table.toString();
}
/**
* TODO
*/
public String getHeader() {
var size = (int) Math.ceil(Math.log10(this.simulationTime));
var format = "%" + (size + 4) + ".3f";
var builder = new StringBuilder();
builder.append("===== Net Stats =====\n");
builder.append(String.format("Seed: \t%d\n", this.seed));
builder.append(String.format("Simulation: \t" + format + "\n", this.simulationTime));
builder.append(String.format("Elapsed: \t" + format + "ms\n", this.timeElapsedNano / 1e6));
return builder.toString();
}
}
/**
* TODO
*/
public static class Statistics {
public double numArrivals = 0;
public double numDepartures = 0;
public double maxQueueLength = 0;
public double averageQueueLength = 0.0d;
public double busyTime = 0.0d;
public double responseTime = 0.0d;
public double lastEventTime = 0.0d;
/**
* Resets the statistics to their initial values.
*/
public void reset() {
this.numArrivals = 0;
this.numDepartures = 0;
this.maxQueueLength = 0;
this.averageQueueLength = 0.0d;
this.busyTime = 0.0d;
this.responseTime = 0.0d;
this.lastEventTime = 0.0d;
}
}
}