diff --git a/MV3302ClassCode/src/mv3302/Customer.java b/MV3302ClassCode/src/mv3302/Customer.java new file mode 100644 index 0000000000000000000000000000000000000000..6a4b4db1d986d39c76faad4b08ea9bfd06873fc7 --- /dev/null +++ b/MV3302ClassCode/src/mv3302/Customer.java @@ -0,0 +1,50 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package mv3302; + +import simkit.Entity; + +/** + *Class represents a customer in a simulation. Assigns the customer with a respective + * serviceTime and renegeTime + */ +public class Customer extends Entity { + + private double serviceTime; + + private double renegeTime; + + /** + * constructor method that assigns values of serviceTime and renegeTime to each customer + */ + public Customer(double serviceTime, double renegeTime) { + super("Customer"); + this.serviceTime = serviceTime; + this.renegeTime = renegeTime; + } + + /** + * @return the serviceTime + */ + public double getServiceTime() { + return serviceTime; + } + + /** + * @return the renegeTime + */ + public double getRenegeTime() { + return renegeTime; + } + + /** + * returns toString method with serviceTime and renegeTime + */ + @Override + public String toString() { + return String.format("%s %,.4f %,.4f", super.toString(), serviceTime, renegeTime); + } + +} \ No newline at end of file diff --git a/MV3302ClassCode/src/mv3302/CustomerArrivalProcess.java b/MV3302ClassCode/src/mv3302/CustomerArrivalProcess.java new file mode 100644 index 0000000000000000000000000000000000000000..6d53bebe48b985e7e5e4708953a970a9aa0b322b --- /dev/null +++ b/MV3302ClassCode/src/mv3302/CustomerArrivalProcess.java @@ -0,0 +1,87 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package mv3302; + +import simkit.random.RandomVariate; + +/** + * CustomerArrivalProcess class acts as the arrival process for the simulation + * extending the ArrivalProcess class. The class instantiates variables for the + * ServiceTime, RenegeTime, InterarrivalTime and numberArrivals + */ +public final class CustomerArrivalProcess extends ArrivalProcess { + + private RandomVariate serviceTimeGenerator; + + private RandomVariate renegeTimeGenerator; + + private RandomVariate interarrivalTimeGenerator; + + private int numberofArrivals; + + /** + * Constructor method that instantiates renege times and service times for + * each customer arrival + * @param renegeTimeGenerator + * @param interarrivalTimeGenerator + * @param serviceTimeGenerator + */ + public CustomerArrivalProcess(RandomVariate renegeTimeGenerator, RandomVariate interarrivalTimeGenerator, RandomVariate serviceTimeGenerator) { + super(interarrivalTimeGenerator); + setServiceTimeGenerator(serviceTimeGenerator); + setRenegeTimeGenerator(renegeTimeGenerator); + } + + /** + * Generates a customers serviceTime, renegeTime, iterates the total number + * of arrivals and schedules the customers arrival event + */ + @Override + public void doArrival() { + super.doArrival(); + double serviceTime = serviceTimeGenerator.generate(); + double renegeTime = renegeTimeGenerator.generate(); + Customer customer = new Customer(serviceTime, renegeTime); + waitDelay("Arrival", 0.0, customer); + numberofArrivals += 1; + } + + /** + * @return the serviceTimeGenerator + */ + public RandomVariate getServiceTimeGenerator() { + return serviceTimeGenerator; + } + + /** + * @param serviceTimeGenerator the serviceTimeGenerator to set + */ + public void setServiceTimeGenerator(RandomVariate serviceTimeGenerator) { + this.serviceTimeGenerator = serviceTimeGenerator; + } + + /** + * @return the renegeTimeGenerator + */ + public RandomVariate getRenegeTimeGenerator() { + return renegeTimeGenerator; + } + + /** + * @param renegeTimeGenerator the renegeTimeGenerator to set + */ + public void setRenegeTimeGenerator(RandomVariate renegeTimeGenerator) { + this.renegeTimeGenerator = renegeTimeGenerator; + } + + /** + * @return the numberArrivals + */ + @Override + public int getNumberArrivals() { + return numberofArrivals; + } + +} diff --git a/MV3302ClassCode/src/mv3302/ServerWithReneges.java b/MV3302ClassCode/src/mv3302/ServerWithReneges.java new file mode 100644 index 0000000000000000000000000000000000000000..b097e6c223d24e6f49c178e95de09c667d354d0e --- /dev/null +++ b/MV3302ClassCode/src/mv3302/ServerWithReneges.java @@ -0,0 +1,260 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package mv3302; + +import static java.lang.Double.NaN; +import java.util.SortedSet; +import java.util.TreeSet; +import static simkit.Priority.HIGH; +import simkit.SimEntityBase; +import simkit.random.RandomVariate; + +/** + * Implementation of the multiple server queue model using<code>Customer</code> + * objects to represent individual customers. This allows the model to + * explicitly tally measures such as delay in queue and time in system. + */ +public final class ServerWithReneges extends SimEntityBase { + + /** + * Number of servers (k) + */ + private int totalNumberServers; + + /** + * # of available servers (S) + */ + protected int numberAvailableServers; + + /** + * Container of waiting customers (q) + */ + protected SortedSet<Customer> queue; + + /** + * time a given customer spends in queue (D - a transient state) + */ + protected double delayInQueueServed = NaN; + + /** + * time a given customer will spend in the queue before reneging + */ + private double delayInQueueReneged; + + /** + * time a given customer spends in the system (W - a transient state) + */ + protected double timeInSystem = NaN; + + private int numberReneges; + + private int numberServed; + + /** + * Generates service times ({t<sub>S</sub>}) + */ + private RandomVariate serviceTimeGenerator; + + /** + * Instantiate a ServerWithReneges with the given totalNumberServers and + * serviceTimeGenerator. Note: should call <code>this()<</code> to ensure + * that <code>queue</code> is instantiated + * + * @param totalNumberServers + * @param serviceTimeGenerator + */ + public ServerWithReneges(int totalNumberServers, RandomVariate serviceTimeGenerator) { + this(); + setTotalNumberServers(totalNumberServers); + setServiceTimeGenerator(serviceTimeGenerator); + } + + /** + * Instantiate the <code>queue</code>state variable + */ + public ServerWithReneges() { + this.queue = new TreeSet<>(); + } + + /** + * Initialize state variables:<ul> + * <li>Empty <code>queue</code> + * <li>set numberAvailableServers to totalNumberServers + * <li>Set numberServed to 0 + * <li>Set delayInQueue to NaN + * <li>Set timeInSystem to NaN </ul> + */ + @Override + public void reset() { + super.reset(); + queue.clear(); + numberAvailableServers = getTotalNumberServers(); + delayInQueueServed = NaN; + delayInQueueReneged = 0.0; + timeInSystem = NaN; + numberReneges = 0; + numberServed = 0; + } + + /** + * Fire property changes for time-varying states + */ + public void doRun() { + firePropertyChange("queue", getQueue()); + firePropertyChange("numberAvailableServers", getNumberAvailableServers()); + firePropertyChange("numberReneges", getNumberReneges()); + } + + /** + * Stamp time and add customer to <code>queue</code><br> + * if available server, schedule StartService with delay of 0.0 + * + * @param customer Arriving customer + */ + public void doArrival(Customer customer) { + customer.stampTime(); + SortedSet<Customer> oldQueue = getQueue(); + queue.add(customer); + firePropertyChange("queue", oldQueue, getQueue()); + waitDelay("Renege", 0.0, customer); + if (numberAvailableServers > 0) { + waitDelay("StartService", 0.0, HIGH); + } + } + + /** + * Decrement numberAvailableServers<br> + * Remove first customer from <code>queue</code><br> + * Compute delayInQueueServed as elapsed time<br> + * Schedule EndService with delay generated by serviceTimeGenerator + */ + public void doStartService() { + Customer customer = queue.first(); + SortedSet<Customer> oldQueue = getQueue(); + firePropertyChange("queue", oldQueue, getQueue()); + int oldNumberAvailableServers = getNumberAvailableServers(); + numberAvailableServers -= 1; + firePropertyChange("numberAvailableServers", oldNumberAvailableServers, getNumberAvailableServers()); + interrupt("renege", customer); + delayInQueueServed = customer.getElapsedTime(); + firePropertyChange("delayInQueueServed", getDelayInQueueServed()); + waitDelay("EndService", serviceTimeGenerator, customer); + } + + /** + * Remove customer from the queue, increment the numberReneges, fires + * property changes for the total number of reneges, and captures + * delayInQueueReneged values as elapsedTime + */ + public void doRenege(Customer customer) { + queue.remove(customer); + double oldNumberReneges = getNumberReneges(); + numberReneges += 1; + firePropertyChange("numberReneges", oldNumberReneges, getNumberReneges()); + delayInQueueReneged = customer.getElapsedTime(); + firePropertyChange("delayInQueueReneged", getDelayInQueueReneged()); + } + + /** + * Increment numberAvailableServers<br> + * Compute timeInSystem as customer's elapsedTime<br> + * Increment numberServed<br> + * If customer(s) in <code>queue</code>, schedule StartService with delay of + * 0.0 + * + * @param customer Given customer completing service + */ + public void doEndService(Customer customer) { + int oldNumberAvailableServers = getNumberAvailableServers(); + numberAvailableServers += 1; + firePropertyChange("numberAvailableServers", oldNumberAvailableServers, getNumberAvailableServers()); + + timeInSystem = customer.getElapsedTime(); + firePropertyChange("timeInSystem", getTimeInSystem()); + numberServed += 1; + if (!queue.isEmpty()) { + waitDelay("StartService", 0.0, HIGH); + } + } + + /** + * @return the totalNumberServers + */ + public int getTotalNumberServers() { + return totalNumberServers; + } + + /** + * @param totalNumberServers the totalNumberServers to set + * @throws IllegalArgumentException if totalNumberServers ≤ 0 + */ + public void setTotalNumberServers(int totalNumberServers) { + if (totalNumberServers <= 0) { + throw new IllegalArgumentException("totalNumberServers must be > 0: " + totalNumberServers); + } + this.totalNumberServers = totalNumberServers; + } + + /** + * @return the serviceTimeGenerator + */ + public RandomVariate getServiceTimeGenerator() { + return serviceTimeGenerator; + } + + /** + * @param serviceTimeGenerator the serviceTimeGenerator to set + */ + public void setServiceTimeGenerator(RandomVariate serviceTimeGenerator) { + this.serviceTimeGenerator = serviceTimeGenerator; + } + + /** + * @return the numberAvailableServers + */ + public int getNumberAvailableServers() { + return numberAvailableServers; + } + + /** + * @return the queue + */ + public SortedSet<Customer> getQueue() { + return new TreeSet<>(queue); + } + + /** + * @return the delayInQueue + */ + public double getDelayInQueueServed() { + return delayInQueueServed; + } + + /** + * @return the timeInSystem + */ + public double getTimeInSystem() { + return timeInSystem; + } + + /** + * @return the delayInQueueReneged + */ + public double getDelayInQueueReneged() { + return delayInQueueReneged; + } + + public double getNumberReneges() { + return numberReneges; + } + + /** + * @return the numberServed + */ + public int getNumberServed() { + return numberServed; + } + +} diff --git a/MV3302ClassCode/src/mv3302/run/RunServerWithReneges.java b/MV3302ClassCode/src/mv3302/run/RunServerWithReneges.java new file mode 100644 index 0000000000000000000000000000000000000000..555f1341315466d515b7aa19a7bcea8f977067da --- /dev/null +++ b/MV3302ClassCode/src/mv3302/run/RunServerWithReneges.java @@ -0,0 +1,97 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Main.java to edit this template + */ +package mv3302.run; + +import mv3302.CustomerArrivalProcess; +import mv3302.ServerWithReneges; +import simkit.Schedule; +import simkit.random.RandomVariate; +import simkit.random.RandomVariateFactory; +import simkit.stat.CollectionSizeTimeVaryingStats; +import simkit.stat.SimpleStatsTally; +import simkit.stat.SimpleStatsTimeVarying; +import simkit.util.SimplePropertyDumper; + +/** + * Simple test of ServerWithReneges calculating for Number of Arrivals, Number + * Served, Number Reneges, Percent Reneges Average number in queue, Average + * Utilization, Avg Delay in Queue Served, Avg Delay in Queue Reneged, and Avg + * Time in System + */ +public class RunServerWithReneges { + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { +// Instantiate CustomerArrivalProcess + RandomVariate interarrivalTimeGenerator = RandomVariateFactory.getInstance("Exponential", 1.5); + RandomVariate serviceTimeGenerator = RandomVariateFactory.getInstance("Gamma", 2.5, 1.2); + RandomVariate renegeTimeGenerator = RandomVariateFactory.getInstance("Uniform", 4.0, 6.0); + CustomerArrivalProcess renegingCustomerArrivalProcess = new CustomerArrivalProcess(renegeTimeGenerator, interarrivalTimeGenerator, serviceTimeGenerator); + System.out.println(renegingCustomerArrivalProcess); +// Instantiate ServerWithReneges + int totalNumberServers = 2; + + ServerWithReneges serverWithReneges = new ServerWithReneges(totalNumberServers, serviceTimeGenerator); + System.out.println(serverWithReneges); + +// The ServerWithReneges will "hear" Arrival(Customer) events + renegingCustomerArrivalProcess.addSimEventListener(serverWithReneges); + +// This was used for debugging model + SimplePropertyDumper simplePropertyDumper = new SimplePropertyDumper(true); + +// This listener computes time-varying statictics for Collection states + CollectionSizeTimeVaryingStats numberInQueueStat = new CollectionSizeTimeVaryingStats("queue"); + serverWithReneges.addPropertyChangeListener(numberInQueueStat); + +// Same as SimpleServer model + SimpleStatsTimeVarying numberAvailableServersStat = new SimpleStatsTimeVarying("numberAvailableServers"); + serverWithReneges.addPropertyChangeListener(numberAvailableServersStat); + +// Tally statistics are computed in the same way by "listening" + SimpleStatsTally timeInSystemStat = new SimpleStatsTally("timeInSystem"); + serverWithReneges.addPropertyChangeListener(timeInSystemStat); + + SimpleStatsTally delayInQueueServedStat = new SimpleStatsTally("delayInQueueServed"); + serverWithReneges.addPropertyChangeListener(delayInQueueServedStat); + + SimpleStatsTally delayInQueueRenegedStat = new SimpleStatsTally("delayInQueueReneged"); + serverWithReneges.addPropertyChangeListener(delayInQueueRenegedStat); + +// Commented out after model debugged +// renegingCustomerArrivalProcess.addPropertyChangeListener(simplePropertyDumper); +// serverWithReneges.addPropertyChangeListener(simplePropertyDumper); +// Was "true" when debugging model + Schedule.setVerbose(false); +// Schedule.stopOnEvent(5, "EndService", Entity.class); + +// Run for 100,000 time units + double stopTime = 100000.0; + Schedule.stopAtTime(stopTime); + +// Initialized & run + Schedule.reset(); + Schedule.startSimulation(); + + System.out.printf("%nSimulation ended at time %,.2f%n%n", Schedule.getSimTime()); + + System.out.printf("Number Arrivals: %,d%n", renegingCustomerArrivalProcess.getNumberArrivals()); + System.out.printf("Number Served: %,d%n", serverWithReneges.getNumberServed()); + System.out.printf("Number Reneges: %,d%n", renegingCustomerArrivalProcess.getNumberArrivals() - serverWithReneges.getNumberServed()); + double percentReneges = (double) (renegingCustomerArrivalProcess.getNumberArrivals() - serverWithReneges.getNumberServed()) / renegingCustomerArrivalProcess.getNumberArrivals() * 100.00; + System.out.printf("Percent Reneges: %,.2f%%%n", percentReneges); + + System.out.printf("Avg # in Queue: %,.3f%n", numberInQueueStat.getMean()); + System.out.printf("Avg Utilization: %,.3f%n%n", numberAvailableServersStat.getMean()); + +// From directly computing delay in queue, delay in reneged and time in system + System.out.printf("Avg Delay in Queue Served: %,.3f%n", delayInQueueServedStat.getMean()); + System.out.printf("Avg Delay in Queue Reneged: %,.3f%n", delayInQueueRenegedStat.getMean()); + System.out.printf("Avg Time in System (Served): %,.3f%n", timeInSystemStat.getMean()); + } + +}