diff --git a/src/main/java/net/berack/upo/valpre/sim/Event.java b/src/main/java/net/berack/upo/valpre/sim/Event.java index b73f867..91d6fb2 100644 --- a/src/main/java/net/berack/upo/valpre/sim/Event.java +++ b/src/main/java/net/berack/upo/valpre/sim/Event.java @@ -6,7 +6,7 @@ package net.berack.upo.valpre.sim; public class Event implements Comparable { public final double time; public final Type type; - public final ServerNode node; + public final int nodeIndex; /** * Create a new event. @@ -15,10 +15,10 @@ public class Event implements Comparable { * @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) { + private Event(Type type, int node, double time) { this.type = type; this.time = time; - this.node = node; + this.nodeIndex = node; } @Override @@ -37,7 +37,7 @@ public class Event implements Comparable { * @param time The time at which the event occurs. * @return The new event. */ - public static Event newArrival(ServerNode node, double time) { + public static Event newArrival(int node, double time) { return new Event(Type.ARRIVAL, node, time); } @@ -48,7 +48,7 @@ public class Event implements Comparable { * @param time The time at which the event occurs. * @return The new event. */ - public static Event newDeparture(ServerNode node, double time) { + public static Event newDeparture(int node, double time) { return new Event(Type.DEPARTURE, node, time); } @@ -59,7 +59,7 @@ public class Event implements Comparable { * @param time The time at which the event occurs. * @return The new event. */ - public static Event newAvailable(ServerNode node, double time) { + public static Event newAvailable(int node, double time) { return new Event(Type.AVAILABLE, node, time); } diff --git a/src/main/java/net/berack/upo/valpre/sim/Net.java b/src/main/java/net/berack/upo/valpre/sim/Net.java index 40255f0..6794526 100644 --- a/src/main/java/net/berack/upo/valpre/sim/Net.java +++ b/src/main/java/net/berack/upo/valpre/sim/Net.java @@ -77,6 +77,7 @@ public final class Net implements Iterable { * @param weight The probability of the child node. * @throws IndexOutOfBoundsException if one of the two nodes are not in the net * @throws IllegalArgumentException if the weight is negative or zero + * @throws IllegalArgumentException if the child is a source node */ public void addConnection(int parent, int child, double weight) { if (weight <= 0) @@ -86,6 +87,9 @@ public final class Net implements Iterable { if (parent < 0 || child < 0 || parent > max || child > max) throw new IndexOutOfBoundsException("One of the nodes does not exist"); + if (this.servers.get(child).spawnArrivals > 0) + throw new IllegalArgumentException("Can't connect to a source node"); + var list = this.connections.get(parent); for (var conn : list) { if (conn.index == child) { @@ -136,6 +140,7 @@ public final class Net implements Iterable { * * @param index the index of the node * @return the node + * @throws IndexOutOfBoundsException if the index is not in the range */ public ServerNode getNode(int index) { return this.servers.get(index); @@ -151,28 +156,28 @@ public final class Net implements Iterable { */ public ServerNode getChildOf(ServerNode parent, Rng rng) { var index = this.indices.get(parent); - return this.getChildOf(index, rng); + index = this.getChildOf(index, rng); + return index < 0 ? null : this.servers.get(index); } /** * 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; + * bounds then an exception is thrown. If the node has no child then -1 is + * returned. * * @param parent the parent node * @param rng the random number generator used for getting one of the child * @throws IndexOutOfBoundsException If the index is not in the range * @return the resultig node */ - public ServerNode getChildOf(int parent, Rng rng) { + public int getChildOf(int parent, Rng rng) { var random = rng.random(); for (var conn : this.connections.get(parent)) { random -= conn.weight; - if (random <= 0) { - return this.servers.get(conn.index); - } + if (random <= 0) + return conn.index; } - return null; + return -1; } /** @@ -221,6 +226,20 @@ public final class Net implements Iterable { } } + /** + * Build the node states for the simulation. + * This method is used to create the state of each node in the network. + * Note that each call to this method will create a new state for each node. + * + * @return the array of node states + */ + public ServerNodeState[] buildNodeStates() { + var states = new ServerNodeState[this.servers.size()]; + for (var i = 0; i < states.length; i++) + states[i] = new ServerNodeState(i, this); + return states; + } + /** * Save the current net to a file. * The resulting file is saved with Kryo. @@ -237,6 +256,11 @@ public final class Net implements Iterable { } } + @Override + public Iterator iterator() { + return this.servers.iterator(); + } + /** * Load the net from the file passed as input. * The net will be the same as the one saved. @@ -295,9 +319,4 @@ public final class Net implements Iterable { this.weight = weight; } } - - @Override - public Iterator iterator() { - return this.servers.iterator(); - } } diff --git a/src/main/java/net/berack/upo/valpre/sim/ServerNode.java b/src/main/java/net/berack/upo/valpre/sim/ServerNode.java index 3ceff3c..9475b65 100644 --- a/src/main/java/net/berack/upo/valpre/sim/ServerNode.java +++ b/src/main/java/net/berack/upo/valpre/sim/ServerNode.java @@ -9,6 +9,7 @@ import net.berack.upo.valpre.rand.Rng; */ public class ServerNode { public final String name; + public final int maxQueue; public final int maxServers; public final int spawnArrivals; public final Distribution service; @@ -89,6 +90,7 @@ public class ServerNode { spawnArrivals = 0; this.name = name; + this.maxQueue = 100; // TODO change to runtime this.maxServers = maxServers; this.spawnArrivals = spawnArrivals; this.service = service; @@ -117,17 +119,6 @@ public class ServerNode { return Distribution.getPositiveSample(this.unavailable, rng); } - /** - * 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 > Math.max(0, numArrivals); - } - @Override public boolean equals(Object obj) { if (!(obj instanceof ServerNode)) diff --git a/src/main/java/net/berack/upo/valpre/sim/ServerNodeState.java b/src/main/java/net/berack/upo/valpre/sim/ServerNodeState.java new file mode 100644 index 0000000..f849f28 --- /dev/null +++ b/src/main/java/net/berack/upo/valpre/sim/ServerNodeState.java @@ -0,0 +1,174 @@ +package net.berack.upo.valpre.sim; + +import java.util.ArrayDeque; + +import net.berack.upo.valpre.rand.Rng; +import net.berack.upo.valpre.sim.stats.NodeStats; + +/** + * Represents a summary of the state 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. + * It also has a connection to the node and the net where it is. + */ +public class ServerNodeState { + public int numServerBusy = 0; + public int numServerUnavailable = 0; + public final ArrayDeque queue = new ArrayDeque<>(); + + public final int index; + public final Net net; + public final ServerNode node; + public final NodeStats stats = new NodeStats(); + + /** + * Create a new node state based on the index and the net passed as input + * + * @param index the index of the node + * @param net the net where the node is + */ + ServerNodeState(int index, Net net) { + this.index = index; + this.net = net; + this.node = net.getNode(index); + } + + /** + * Check if the queue is full based on the maximum queue length of the node + * + * @return true if the queue is full + */ + public boolean isQueueFull() { + return this.queue.size() >= this.node.maxQueue; + } + + /** + * Check if the node can serve a new request based on the number of servers + * + * @return true if the node can serve + */ + public boolean canServe() { + return this.node.maxServers > this.numServerBusy + this.numServerUnavailable; + } + + /** + * Check if the node has requests to serve based on the number of busy servers + * + * @return true if the node has requests + */ + public boolean hasRequests() { + return this.queue.size() > this.numServerBusy; + } + + /** + * Determines if the node should spawn an arrival based on the number of + * arrivals. + * + * @return True if the node should spawn an arrival, false otherwise. + */ + public boolean shouldSpawnArrival() { + return this.node.spawnArrivals > this.stats.numArrivals; + } + + /** + * Update stats and queue when an unavailability event finish. The + * unavailability + * time is the time of the event. + * + * @param time the time of the event + */ + public void updateAvailable(double time) { + this.stats.updateTimes(time, this.numServerBusy, this.numServerUnavailable, this.node.maxServers); + this.numServerUnavailable--; + } + + /** + * Update stats and queue when an arrival event occurs. The arrival time is the + * time of the event. + * + * @param time the time of the event + */ + public void updateArrival(double time) { + this.queue.add(time); + this.stats.updateArrival(time, this.queue.size()); + this.stats.updateTimes(time, this.numServerBusy, this.numServerUnavailable, node.maxServers); + } + + /** + * Update stats and queue when a departure event occurs. The departure time is + * the time of the event. + * + * @param time the time of the event + */ + public void updateDeparture(double time) { + var arrivalTime = this.queue.poll(); + this.stats.updateDeparture(time, arrivalTime); + this.stats.updateTimes(time, this.numServerBusy, this.numServerUnavailable, node.maxServers); + this.numServerBusy--; + } + + /** + * Create an arrival event based on the node and the time passed as input + * + * @param time the time of the event + * @return the arrival event + */ + public Event spawnArrivalIfPossilbe(double time) { + if (this.shouldSpawnArrival()) + return Event.newArrival(this.index, time); + return null; + } + + /** + * Create a departure event if the node can serve and has requests. The event is + * created based on the node and the delay is determined by the node's service + * time distribution. + * + * @param time the time of the event + * @param rng the random number generator + * @return the departure event if the node can serve and has requests, null + * otherwise + */ + public Event spawnDepartureIfPossible(double time, Rng rng) { + if (this.canServe() && this.hasRequests()) { + this.numServerBusy++; + var delay = node.getServiceTime(rng); + return Event.newDeparture(this.index, time + delay); + } + return null; + } + + /** + * Create an unavailable event if the node is unavailable. The event is created + * based on the given node, and the delay is determined by the node's + * unavailability distribution. + * + * @param time The time of the event + * @param rng The random number generator + * @return The event if the node is unavailable, null otherwise + */ + public Event spawnUnavailableIfPossible(double time, Rng rng) { + var delay = node.getUnavailableTime(rng); + if (delay > 0) { + this.numServerUnavailable++; + return Event.newAvailable(this.index, time + delay); + } + return null; + } + + /** + * Create an arrival event to a child node based on the node and the time passed + * as input + * + * @param time the time of the event + * @param rng the random number generator + * @return the arrival event to a child node if the node has children, null + * otherwise + */ + public Event spawnArrivalToChild(double time, Rng rng) { + var childIndex = this.net.getChildOf(this.index, rng); + if (childIndex >= 0) + return Event.newArrival(childIndex, time); + return null; + } +} \ No newline at end of file diff --git a/src/main/java/net/berack/upo/valpre/sim/Simulation.java b/src/main/java/net/berack/upo/valpre/sim/Simulation.java index dfeb9b7..b01ce0d 100644 --- a/src/main/java/net/berack/upo/valpre/sim/Simulation.java +++ b/src/main/java/net/berack/upo/valpre/sim/Simulation.java @@ -1,10 +1,8 @@ package net.berack.upo.valpre.sim; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.PriorityQueue; import net.berack.upo.valpre.rand.Rng; @@ -20,8 +18,7 @@ public final class Simulation { public final EndCriteria[] criterias; public final long seed; - private final Net net; - private final Map states; + private final ServerNodeState[] states; private final PriorityQueue fel; private double time = 0.0d; private long eventProcessed = 0; @@ -36,23 +33,22 @@ public final class Simulation { */ public Simulation(Net net, Rng rng, EndCriteria... criterias) { this.timeStartedNano = System.nanoTime(); - this.net = net; - this.states = new HashMap<>(); + this.states = net.buildNodeStates(); this.fel = new PriorityQueue<>(); this.criterias = criterias; this.seed = rng.getSeed(); this.rng = rng; boolean hasLimit = false; - for (var node : net) { + for (var state : this.states) { + var node = state.node; + // check for ending criteria in simulation if (node.spawnArrivals != Integer.MAX_VALUE) hasLimit = true; // Initial arrivals (if spawned) - this.states.put(node.name, new NodeState()); - if (node.shouldSpawnArrival(0)) - this.addArrival(node); + this.addToFel(state.spawnArrivalIfPossilbe(0.0d)); } if (!hasLimit && (criterias == null || criterias.length == 0)) @@ -83,38 +79,26 @@ public final class Simulation { if (event == null) throw new NullPointerException("No more events to process!"); - var node = event.node; - var state = this.states.get(node.name); + var state = this.states[event.nodeIndex]; this.time = event.time; this.eventProcessed += 1; switch (event.type) { case AVAILABLE -> { - state.stats.updateTimes(this.time, state.numServerBusy, state.numServerUnavailable, node.maxServers); - state.numServerUnavailable--; - this.addDepartureIfPossible(node, state); + state.updateAvailable(time); + this.addToFel(state.spawnDepartureIfPossible(time, this.rng)); } case ARRIVAL -> { - state.queue.add(this.time); - state.stats.updateArrival(this.time, state.queue.size()); - state.stats.updateTimes(this.time, state.numServerBusy, state.numServerUnavailable, node.maxServers); - this.addDepartureIfPossible(node, state); + state.updateArrival(time); + this.addToFel(state.spawnDepartureIfPossible(time, this.rng)); } case DEPARTURE -> { - var arrivalTime = state.queue.poll(); - state.stats.updateDeparture(this.time, arrivalTime); - state.stats.updateTimes(this.time, state.numServerBusy, state.numServerUnavailable, node.maxServers); - state.numServerBusy--; + state.updateDeparture(time); - this.addUnavailableIfPossible(node, state); - this.addDepartureIfPossible(node, state); - - var next = this.net.getChildOf(node, this.rng); - if (next != null) - this.addArrival(next); - - if (node.shouldSpawnArrival(state.stats.numArrivals)) - this.addArrival(node); + this.addToFel(state.spawnUnavailableIfPossible(time, this.rng)); + this.addToFel(state.spawnDepartureIfPossible(time, this.rng)); + this.addToFel(state.spawnArrivalToChild(time, rng)); + this.addToFel(state.spawnArrivalIfPossilbe(time)); } } } @@ -127,8 +111,8 @@ public final class Simulation { public Result endSimulation() { var elapsed = System.nanoTime() - this.timeStartedNano; var nodes = new HashMap(); - for (var entry : this.states.entrySet()) - nodes.put(entry.getKey(), entry.getValue().stats); + for (var state : this.states) + nodes.put(state.node.name, state.stats); return new Result(this.seed, this.time, elapsed * 1e-6, nodes); } @@ -166,9 +150,10 @@ public final class Simulation { * * @param node the name of the node * @return the node + * @throws NullPointerException if the node does not exist. */ public ServerNode getNode(String node) { - return this.net.getNode(node); + return this.getNodeState(node).node; } /** @@ -176,55 +161,26 @@ public final class Simulation { * * @param node the name of the node * @return the current state of the node + * @throws NullPointerException if the node does not exist. */ - public NodeState getNodeState(String node) { - return this.states.get(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 service - * distribution. - * - * @param node The node to create the event for. - * @param state The current state of the node - */ - public void addDepartureIfPossible(ServerNode node, NodeState state) { - var canServe = node.maxServers > state.numServerBusy + state.numServerUnavailable; - var hasRequests = state.queue.size() > state.numServerBusy; - - if (canServe && hasRequests) { - state.numServerBusy++; - var delay = node.getServiceTime(this.rng); - var event = Event.newDeparture(node, this.time + delay); - fel.add(event); + public ServerNodeState getNodeState(String node) { + for (var state : this.states) { + if (state.node.name.equals(node)) + return state; } + + throw new NullPointerException("Node not found: " + node); } /** - * Add an AVAILABLE event in the case that the node has an unavailability time. + * Add an arrival event to the future event list if the event is not null, + * otherwise do nothing. * - * @param node The node to create the event for - * @param state The current state of the node + * @param e the event to add */ - public void addUnavailableIfPossible(ServerNode node, NodeState state) { - var delay = node.getUnavailableTime(rng); - if (delay > 0) { - state.numServerUnavailable++; - var event = Event.newAvailable(node, time + delay); - this.fel.add(event); - } + public void addToFel(Event e) { + if (e != null) + this.fel.add(e); } /** @@ -243,16 +199,4 @@ public final class Simulation { } return false; } - - /** - * Represents a summary of the state 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 NodeState { - public int numServerBusy = 0; - public int numServerUnavailable = 0; - public final NodeStats stats = new NodeStats(); - public final ArrayDeque queue = new ArrayDeque<>(); - } } \ No newline at end of file diff --git a/src/main/resources/example1.net b/src/main/resources/example1.net index f8e0fd8..7e87705 100644 Binary files a/src/main/resources/example1.net and b/src/main/resources/example1.net differ diff --git a/src/main/resources/example2.net b/src/main/resources/example2.net index 0428cd6..b75bba1 100644 Binary files a/src/main/resources/example2.net and b/src/main/resources/example2.net differ diff --git a/src/main/resources/example3.net b/src/main/resources/example3.net index e3a788b..3785f02 100644 Binary files a/src/main/resources/example3.net and b/src/main/resources/example3.net differ diff --git a/src/test/java/net/berack/upo/valpre/sim/TestSaveExamplesNet.java b/src/test/java/net/berack/upo/valpre/sim/TestSaveExamplesNet.java new file mode 100644 index 0000000..f96dfff --- /dev/null +++ b/src/test/java/net/berack/upo/valpre/sim/TestSaveExamplesNet.java @@ -0,0 +1,61 @@ +package net.berack.upo.valpre.sim; + +import java.io.IOException; + +import org.junit.Test; + +import com.esotericsoftware.kryo.KryoException; + +import net.berack.upo.valpre.rand.Distribution; + +public class TestSaveExamplesNet { + + private static final Distribution exp0_22 = new Distribution.Exponential(1.0 / 4.5); + private static final Distribution exp2 = new Distribution.Exponential(2.0); + private static final Distribution exp1_5 = new Distribution.Exponential(1.5); + private static final Distribution exp3_5 = new Distribution.Exponential(3.5); + private static final Distribution exp10 = new Distribution.Exponential(10.0); + + private static final Distribution norm3_2 = new Distribution.NormalBoxMuller(3.2, 0.6); + private static final Distribution norm4_2 = new Distribution.NormalBoxMuller(4.2, 0.6); + + private static final Distribution unNorm = new Distribution.UnavailableTime(0.2, norm4_2); + private static final Distribution unExp = new Distribution.UnavailableTime(0.1, exp10); + + @Test + public void testSaveExample1() throws KryoException, IOException { + var net = new Net(); + net.addNode(ServerNode.createLimitedSource("Source", exp0_22, 1000)); + net.addNode(ServerNode.createQueue("Queue", 1, norm3_2)); + net.addConnection(0, 1, 1.0); + + net.save("src/main/resources/example1.net"); + net = Net.load("src/main/resources/example1.net"); + } + + @Test + public void testSaveExample2() throws KryoException, IOException { + var net = new Net(); + net.addNode(ServerNode.createLimitedSource("Source", exp0_22, 1000)); + net.addNode(ServerNode.createQueue("Queue", 1, norm3_2)); + net.addNode(ServerNode.createQueue("Queue Wait", 1, norm3_2, unNorm)); + net.addConnection(0, 1, 1.0); + net.addConnection(1, 2, 1.0); + + net.save("src/main/resources/example2.net"); + net = Net.load("src/main/resources/example2.net"); + } + + @Test + public void testSaveExample3() throws KryoException, IOException { + var net = new Net(); + net.addNode(ServerNode.createLimitedSource("Source", exp1_5, 1000)); + net.addNode(ServerNode.createQueue("Service1", 1, exp2)); + net.addNode(ServerNode.createQueue("Service2", 1, exp3_5, unExp)); + net.addConnection(0, 1, 1.0); + net.addConnection(1, 2, 1.0); + + net.save("src/main/resources/example3.net"); + net = Net.load("src/main/resources/example3.net"); + } +} diff --git a/src/test/java/net/berack/upo/valpre/sim/TestSimulation.java b/src/test/java/net/berack/upo/valpre/sim/TestSimulation.java index ec6b150..9e66bd1 100644 --- a/src/test/java/net/berack/upo/valpre/sim/TestSimulation.java +++ b/src/test/java/net/berack/upo/valpre/sim/TestSimulation.java @@ -42,61 +42,42 @@ public class TestSimulation { var node = ServerNode.createQueue("Nodo", 0, const1); assertEquals("Nodo", node.name); assertEquals(1, node.maxServers); - assertFalse(node.shouldSpawnArrival(0)); - assertFalse(node.shouldSpawnArrival(50)); - assertFalse(node.shouldSpawnArrival(1000)); - assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE)); - assertFalse(node.shouldSpawnArrival(-1)); + assertEquals(0, node.spawnArrivals); assertEquals(1.0, node.getServiceTime(null), DELTA); node = ServerNode.createQueue("Queue", 50, const1); assertEquals("Queue", node.name); assertEquals(50, node.maxServers); - assertFalse(node.shouldSpawnArrival(0)); - assertFalse(node.shouldSpawnArrival(50)); - assertFalse(node.shouldSpawnArrival(1000)); - assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE)); - assertFalse(node.shouldSpawnArrival(-1)); + assertEquals(0, node.spawnArrivals); assertEquals(1.0, node.getServiceTime(null), DELTA); node = ServerNode.createSource("Source", const1); assertEquals("Source", node.name); assertEquals(Integer.MAX_VALUE, node.maxServers); - assertTrue(node.shouldSpawnArrival(0)); - assertTrue(node.shouldSpawnArrival(50)); - assertTrue(node.shouldSpawnArrival(1000)); - assertTrue(node.shouldSpawnArrival(Integer.MAX_VALUE - 1)); - assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE)); - assertTrue(node.shouldSpawnArrival(-1)); + assertEquals(Integer.MAX_VALUE, node.spawnArrivals); assertEquals(1.0, node.getServiceTime(null), DELTA); node = ServerNode.createLimitedSource("Source", const1, 50); assertEquals("Source", node.name); assertEquals(Integer.MAX_VALUE, node.maxServers); - assertTrue(node.shouldSpawnArrival(0)); - assertTrue(node.shouldSpawnArrival(49)); - assertFalse(node.shouldSpawnArrival(50)); - assertFalse(node.shouldSpawnArrival(1000)); - assertFalse(node.shouldSpawnArrival(Integer.MAX_VALUE)); - assertTrue(node.shouldSpawnArrival(-1)); + assertEquals(50, node.spawnArrivals); assertEquals(1.0, node.getServiceTime(null), DELTA); } @Test public void event() { - var node = ServerNode.createSource("Source", const0); - var event = Event.newAvailable(node, 1.0); - assertEquals(node, event.node); + var event = Event.newAvailable(0, 1.0); + assertEquals(0, event.nodeIndex); assertEquals(1.0, event.time, 0.000000000001); assertEquals(Event.Type.AVAILABLE, event.type); - var event2 = Event.newArrival(node, 5.0); - assertEquals(node, event2.node); + var event2 = Event.newArrival(0, 5.0); + assertEquals(0, event2.nodeIndex); assertEquals(5.0, event2.time, 0.000000000001); assertEquals(Event.Type.ARRIVAL, event2.type); - var event3 = Event.newDeparture(node, 8.0); - assertEquals(node, event3.node); + var event3 = Event.newDeparture(1, 8.0); + assertEquals(1, event3.nodeIndex); assertEquals(8.0, event3.time, 0.000000000001); assertEquals(Event.Type.DEPARTURE, event3.type); @@ -170,9 +151,30 @@ public class TestSimulation { assertEquals(0, conn.size()); var sample = net.getChildOf(0, rigged); - assertEquals(node1, sample); - sample = net.getChildOf(node, rigged); - assertEquals(node1, sample); + assertEquals(1, sample); + var sample2 = net.getChildOf(node, rigged); + assertEquals(node1, sample2); + } + + @Test + public void nodeState() { + var net = new Net(); + var node = ServerNode.createQueue("First", 1, const0); + net.addNode(node); + var state = new ServerNodeState(0, net); + + assertEquals(0, state.index); + assertEquals(net, state.net); + assertEquals(node, state.node); + assertEquals(0, state.numServerBusy); + assertEquals(0, state.numServerUnavailable); + assertEquals(0, state.queue.size()); + assertFalse(state.isQueueFull()); + assertTrue(state.canServe()); + assertFalse(state.hasRequests()); + assertFalse(state.shouldSpawnArrival()); + + // TODO better test } @Test @@ -183,7 +185,7 @@ public class TestSimulation { assertTrue(sim.hasEnded()); assertFalse(criteria.shouldEnd(sim)); - sim.addArrival(node0); + sim.addToFel(Event.newArrival(0, sim.getTime())); assertFalse(sim.hasEnded()); assertFalse(criteria.shouldEnd(sim)); sim.processNextEvent(); // Arrival @@ -204,7 +206,7 @@ public class TestSimulation { assertTrue(sim.hasEnded()); // No more events assertFalse(criteria.shouldEnd(sim)); - sim.addArrival(node0); + sim.addToFel(Event.newArrival(0, sim.getTime())); assertFalse(sim.hasEnded()); assertFalse(criteria.shouldEnd(sim)); sim.processNextEvent(); // Arrival @@ -225,7 +227,7 @@ public class TestSimulation { assertTrue(sim.hasEnded()); assertFalse(criteria.shouldEnd(sim)); - sim.addArrival(node0); + sim.addToFel(Event.newArrival(0, sim.getTime())); assertFalse(sim.hasEnded()); assertFalse(criteria.shouldEnd(sim)); @@ -247,7 +249,7 @@ public class TestSimulation { assertTrue(sim.hasEnded()); assertFalse(criteria.shouldEnd(sim)); - sim.addArrival(node0); + sim.addToFel(Event.newArrival(0, sim.getTime())); assertFalse(sim.hasEnded()); assertFalse(criteria.shouldEnd(sim)); sim.processNextEvent(); // Arrival @@ -264,7 +266,7 @@ public class TestSimulation { assertTrue(sim.hasEnded()); assertFalse(criteria.shouldEnd(sim)); - sim.addArrival(node0); + sim.addToFel(Event.newArrival(0, sim.getTime())); assertFalse(sim.hasEnded()); assertFalse(criteria.shouldEnd(sim)); @@ -286,7 +288,7 @@ public class TestSimulation { assertTrue(sim.hasEnded()); assertFalse(criteria.shouldEnd(sim)); - sim.addArrival(node0); + sim.addToFel(Event.newArrival(0, sim.getTime())); assertFalse(sim.hasEnded()); assertFalse(criteria.shouldEnd(sim)); sim.processNextEvent(); // Arrival @@ -321,7 +323,7 @@ public class TestSimulation { var fel = sim.getFutureEventList(); assertEquals(0, fel.size()); - sim.addArrival(node0); + sim.addToFel(Event.newArrival(0, sim.getTime())); assertFalse(sim.hasEnded()); assertEquals(0, sim.getEventsProcessed()); assertEquals(0.0, sim.getTime(), DELTA); @@ -334,7 +336,7 @@ public class TestSimulation { fel = sim.getFutureEventList(); assertEquals(1, fel.size()); assertEquals(Event.Type.ARRIVAL, fel.get(0).type); - assertEquals(node0, fel.get(0).node); + assertEquals(0, fel.get(0).nodeIndex); assertEquals(0.0, fel.get(0).time, DELTA); sim.processNextEvent(); // Arrival @@ -350,7 +352,7 @@ public class TestSimulation { fel = sim.getFutureEventList(); assertEquals(1, fel.size()); assertEquals(Event.Type.DEPARTURE, fel.get(0).type); - assertEquals(node0, fel.get(0).node); + assertEquals(0, fel.get(0).nodeIndex); assertEquals(1.0, fel.get(0).time, DELTA); sim.processNextEvent(); // Departure Source @@ -366,7 +368,7 @@ public class TestSimulation { fel = sim.getFutureEventList(); assertEquals(1, fel.size()); assertEquals(Event.Type.ARRIVAL, fel.get(0).type); - assertEquals(node1, fel.get(0).node); + assertEquals(1, fel.get(0).nodeIndex); assertEquals(1.0, fel.get(0).time, DELTA); sim.processNextEvent(); // Arrival Queue @@ -382,7 +384,7 @@ public class TestSimulation { fel = sim.getFutureEventList(); assertEquals(1, fel.size()); assertEquals(Event.Type.DEPARTURE, fel.get(0).type); - assertEquals(node1, fel.get(0).node); + assertEquals(1, fel.get(0).nodeIndex); assertEquals(2.0, fel.get(0).time, DELTA); sim.processNextEvent(); // Departure Queue @@ -402,7 +404,7 @@ public class TestSimulation { var result = sim.endSimulation(); assertEquals(2.0, result.simulationTime, DELTA); assertEquals(sim.seed, result.seed); - assertEquals(elapsed * 1e-6, result.timeElapsedMS * 1e-6, diff); + assertEquals(elapsed * 1e-6, result.timeElapsedMS, diff); assertEquals(2, result.nodes.size()); assertEquals(1, result.nodes.get(node0.name).numArrivals, DELTA); assertEquals(1, result.nodes.get(node0.name).numDepartures, DELTA); @@ -414,12 +416,12 @@ public class TestSimulation { public void endSim() { var criteria = new EndCriteria.MaxDepartures(node0.name, 5); var sim = new Simulation(simpleNet, rigged, criteria); - sim.addArrival(node0); - sim.addArrival(node0); - sim.addArrival(node0); - sim.addArrival(node0); - sim.addArrival(node0); - sim.addArrival(node0); + sim.addToFel(Event.newArrival(0, sim.getTime())); + sim.addToFel(Event.newArrival(0, sim.getTime())); + sim.addToFel(Event.newArrival(0, sim.getTime())); + sim.addToFel(Event.newArrival(0, sim.getTime())); + sim.addToFel(Event.newArrival(0, sim.getTime())); + sim.addToFel(Event.newArrival(0, sim.getTime())); while (!criteria.shouldEnd(sim)) { sim.processNextEvent();