/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2003-2011 Ausenco Engineering Canada Inc.
* Copyright (C) 2016 JaamSim Software Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jaamsim.ProcessFlow;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.TreeSet;
import com.jaamsim.Graphics.DisplayEntity;
import com.jaamsim.Samples.SampleConstant;
import com.jaamsim.Samples.SampleInput;
import com.jaamsim.basicsim.Entity;
import com.jaamsim.basicsim.EntityTarget;
import com.jaamsim.datatypes.DoubleVector;
import com.jaamsim.datatypes.IntegerVector;
import com.jaamsim.events.EventHandle;
import com.jaamsim.events.EventManager;
import com.jaamsim.events.ProcessTarget;
import com.jaamsim.input.BooleanInput;
import com.jaamsim.input.Input;
import com.jaamsim.input.IntegerInput;
import com.jaamsim.input.InterfaceEntityInput;
import com.jaamsim.input.Keyword;
import com.jaamsim.input.Output;
import com.jaamsim.input.ValueInput;
import com.jaamsim.math.Vec3d;
import com.jaamsim.units.DimensionlessUnit;
import com.jaamsim.units.DistanceUnit;
import com.jaamsim.units.TimeUnit;
public class Queue extends LinkedComponent {
@Keyword(description = "The priority for positioning the received entity in the queue.\n" +
"Priority is integer valued and a lower numerical value indicates a higher priority.\n" +
"For example, priority 3 is higher than 4, and priorities 3, 3.2, and 3.8 are equivalent.",
exampleList = {"this.obj.Attrib1"})
private final SampleInput priority;
@Keyword(description = "An expression that returns a dimensionless integer value that can be used to "
+ "match entities in separate queues. The expression is evaluated when the entity "
+ "first arrives at the queue. Since Match is integer valued, a value of 3.2 for one "
+ "queue and 3.6 for another queue are considered to be equal.",
exampleList = {"this.obj.Attrib1"})
private final SampleInput match;
@Keyword(description = "Determines the order in which entities are placed in the queue (FIFO or LIFO):\n" +
"TRUE = first in first out (FIFO) order (the default setting)," +
"FALSE = last in first out (LIFO) order.",
exampleList = {"FALSE"})
private final BooleanInput fifo;
@Keyword(description = "The time an entity will wait in the queue before deciding whether or "
+ "not to renege. Evaluated when the entity first enters the queue.\n"
+ "A constant value, a distribution to be sampled, a time series, or an "
+ "expression can be entered.",
exampleList = { "3.0 h", "NormalDistribution1", "'1[s] + 0.5*[TimeSeries1].PresentValue'" })
private final SampleInput renegeTime;
@Keyword(description = "A logical condition that determines whether an entity will renege "
+ "after waiting for its RenegeTime value. Note that TRUE and FALSE are "
+ "entered as 1 and 0, respectively.\n"
+ "A constant value, a distribution to be sampled, a time series, or an "
+ "expression can be entered.",
exampleList = { "1", "'this.QueuePosition > 1'", "'this.QueuePostion > [Queue2].QueueLength'" })
private final SampleInput renegeCondition;
@Keyword(description = "The object to which an entity will be sent if it reneges.",
exampleList = {"Branch1"})
protected final InterfaceEntityInput<Linkable> renegeDestination;
@Keyword(description = "The amount of graphical space shown between DisplayEntity objects in the queue.",
exampleList = {"1 m"})
private final ValueInput spacing;
@Keyword(description = "The number of queuing entities in each row.",
exampleList = {"4"})
protected final IntegerInput maxPerLine; // maximum items per sub line-up of queue
private final TreeSet<QueueEntry> itemSet; // contains all the entities in queue order
private final HashMap<Integer, TreeSet<QueueEntry>> matchMap; // each TreeSet contains the queued entities for a given match value
private Integer matchForMaxCount; // match value with the largest number of entities
private int maxCount; // largest number of entities for a given match value
private final ArrayList<QueueUser> userList; // other objects that use this queue
// Statistics
protected double timeOfLastUpdate; // time at which the statistics were last updated
protected double startOfStatisticsCollection; // time at which statistics collection was started
protected int minElements; // minimum observed number of entities in the queue
protected int maxElements; // maximum observed number of entities in the queue
protected double elementSeconds; // total time that entities have spent in the queue
protected double squaredElementSeconds; // total time for the square of the number of elements in the queue
protected DoubleVector queueLengthDist; // entry at position n is the total time the queue has had length n
protected long numberReneged; // number of entities that reneged from the queue
{
defaultEntity.setHidden(true);
nextComponent.setHidden(true);
priority = new SampleInput("Priority", "Key Inputs", new SampleConstant(0));
priority.setUnitType(DimensionlessUnit.class);
priority.setEntity(this);
priority.setValidRange(0.0d, Double.POSITIVE_INFINITY);
this.addInput(priority);
match = new SampleInput("Match", "Key Inputs", null);
match.setUnitType(DimensionlessUnit.class);
match.setEntity(this);
this.addInput(match);
fifo = new BooleanInput("FIFO", "Key Inputs", true);
this.addInput(fifo);
renegeTime = new SampleInput("RenegeTime", "Key Inputs", null);
renegeTime.setUnitType(TimeUnit.class);
renegeTime.setEntity(this);
renegeTime.setValidRange(0.0d, Double.POSITIVE_INFINITY);
this.addInput(renegeTime);
renegeCondition = new SampleInput("RenegeCondition", "Key Inputs", new SampleConstant(1));
renegeCondition.setUnitType(DimensionlessUnit.class);
renegeCondition.setEntity(this);
renegeCondition.setValidRange(0.0d, 1.0d);
this.addInput(renegeCondition);
renegeDestination = new InterfaceEntityInput<>(Linkable.class, "RenegeDestination", "Key Inputs", null);
this.addInput(renegeDestination);
spacing = new ValueInput("Spacing", "Key Inputs", 0.0d);
spacing.setUnitType(DistanceUnit.class);
spacing.setValidRange(0.0d, Double.POSITIVE_INFINITY);
this.addInput(spacing);
maxPerLine = new IntegerInput("MaxPerLine", "Key Inputs", Integer.MAX_VALUE);
maxPerLine.setValidRange(1, Integer.MAX_VALUE);
this.addInput(maxPerLine);
}
public Queue() {
itemSet = new TreeSet<>();
queueLengthDist = new DoubleVector(10,10);
userList = new ArrayList<>();
matchMap = new HashMap<>();
}
@Override
public void updateForInput(Input<?> in) {
super.updateForInput(in);
if (in == renegeTime) {
boolean bool = renegeTime.getValue() != null;
renegeDestination.setRequired(bool);
return;
}
}
@Override
public void earlyInit() {
super.earlyInit();
// Clear the entries in the queue
itemSet.clear();
matchMap.clear();
matchForMaxCount = null;
maxCount = -1;
// Clear statistics
startOfStatisticsCollection = 0.0;
timeOfLastUpdate = 0.0;
minElements = 0;
maxElements = 0;
elementSeconds = 0.0;
squaredElementSeconds = 0.0;
queueLengthDist.clear();
numberReneged = 0;
// Identify the objects that use this queue
userList.clear();
for (Entity each : Entity.getClonesOfIterator(Entity.class)) {
if (each instanceof QueueUser) {
QueueUser u = (QueueUser)each;
if (u.getQueues().contains(this))
userList.add(u);
}
}
}
private static class QueueEntry implements Comparable<QueueEntry> {
final DisplayEntity entity;
final long entNum;
final int priority;
final Integer match;
final double timeAdded;
final Vec3d orientation;
final EventHandle renegeHandle;
public QueueEntry(DisplayEntity ent, long n, int pri, Integer m, double t, Vec3d orient, EventHandle rh) {
entity = ent;
entNum = n;
priority = pri;
match = m;
timeAdded = t;
orientation = orient;
renegeHandle = rh;
}
@Override
public int compareTo(QueueEntry entry) {
if (this.priority > entry.priority)
return 1;
else if (this.priority < entry.priority)
return -1;
else {
if (this.entNum > entry.entNum)
return 1;
else if (this.entNum < entry.entNum)
return -1;
return 0;
}
}
}
private final DoQueueChanged userUpdate = new DoQueueChanged(this);
private final EventHandle userUpdateHandle = new EventHandle();
private static class DoQueueChanged extends ProcessTarget {
private final Queue queue;
public DoQueueChanged(Queue q) {
queue = q;
}
@Override
public void process() {
for (QueueUser each : queue.userList)
each.queueChanged();
}
@Override
public String getDescription() {
return queue.getName() + ".UpdateAllQueueUsers";
}
}
// ******************************************************************************************************
// QUEUE HANDLING METHODS
// ******************************************************************************************************
@Override
public void addEntity(DisplayEntity ent) {
super.addEntity(ent);
// Update the queue statistics
int queueSize = itemSet.size(); // present number of entities in the queue
this.updateStatistics(queueSize, queueSize+1);
// Build the entry for the entity
long n = this.getTotalNumberAdded();
if (!fifo.getValue())
n *= -1;
int pri = (int) priority.getValue().getNextSample(getSimTime());
Integer m = null;
if (match.getValue() != null)
m = Integer.valueOf((int) match.getValue().getNextSample(getSimTime()));
EventHandle rh = null;
if (renegeTime.getValue() != null)
rh = new EventHandle();
QueueEntry entry = new QueueEntry(ent, n, pri, m, getSimTime(), ent.getOrientation(), rh);
// Add the entity to the TreeSet of all the entities in the queue
boolean bool = itemSet.add(entry);
if (!bool)
error("Entity %s is already present in the queue.", ent);
// Does the entry have a match value?
if (entry.match != null) {
// Add the entity to the TreeSet of all the entities with this match value
TreeSet<QueueEntry> matchSet = matchMap.get(entry.match);
if (matchSet == null) {
matchSet = new TreeSet<>();
matchSet.add(entry);
matchMap.put(entry.match, matchSet);
}
else {
matchSet.add(entry);
}
// Update the maximum count
if (entry.match.equals(matchForMaxCount)) {
maxCount++;
}
else {
if (matchSet.size() > maxCount) {
matchForMaxCount = entry.match;
maxCount = matchSet.size();
}
}
}
// Notify the users of this queue
if (!userUpdateHandle.isScheduled())
EventManager.scheduleTicks(0, 2, false, userUpdate, userUpdateHandle);
// Schedule the time to check the renege condition
if (renegeTime.getValue() != null) {
double dur = renegeTime.getValue().getNextSample(getSimTime());
// Schedule the renege tests in FIFO order so that if two or more entities are added to
// the queue at the same time, the one nearest the front of the queue is tested first
EventManager.scheduleSeconds(dur, 5, true, new RenegeActionTarget(this, entry), rh);
}
}
private static class RenegeActionTarget extends EntityTarget<Queue> {
private final QueueEntry entry;
RenegeActionTarget(Queue q, QueueEntry e) {
super(q, "renegeAction");
entry = e;
}
@Override
public void process() {
ent.renegeAction(entry);
}
}
public void renegeAction(QueueEntry entry) {
// Temporarily set the obj entity to the one that might renege
double simTime = this.getSimTime();
DisplayEntity oldEnt = this.getReceivedEntity(simTime);
this.setReceivedEntity(entry.entity);
// Check the condition for reneging
boolean bool = (renegeCondition.getValue().getNextSample(simTime) == 0.0d);
this.setReceivedEntity(oldEnt);
if (bool) {
return;
}
// Remove the entity from the queue and send it to the renege destination
this.remove(entry);
numberReneged++;
renegeDestination.getValue().addEntity(entry.entity);
}
public DisplayEntity removeEntity(DisplayEntity ent) {
return this.remove(getQueueEntry(ent));
}
/**
* Removes a specified entity from the queue
*/
private DisplayEntity remove(QueueEntry entry) {
int queueSize = itemSet.size(); // present number of entities in the queue
this.updateStatistics(queueSize, queueSize-1);
// Remove the entity from the TreeSet of all entities in the queue
boolean found = itemSet.remove(entry);
if (!found)
error("Cannot find the entry in itemSet.");
// Kill the renege event
if (entry.renegeHandle != null)
EventManager.killEvent(entry.renegeHandle);
// Does the entry have a match value?
if (entry.match != null) {
// Remove the entity from the TreeSet for that match value
TreeSet<QueueEntry> matchSet = matchMap.get(entry.match);
if (matchSet == null)
error("Cannot find an entry in matchMap for match value: %s", entry.match);
found = matchSet.remove(entry);
if (!found)
error("Cannot find the entry in matchMap.");
// If there are no more entities for this match value, remove it from the HashMap of match values
if (matchSet.isEmpty())
matchMap.remove(entry.match);
// Update the maximum count
if (entry.match.equals(matchForMaxCount)) {
matchForMaxCount = null;
maxCount = -1;
}
}
// Reset the entity's orientation to its original value
entry.entity.setOrientation(entry.orientation);
this.incrementNumberProcessed();
return entry.entity;
}
private QueueEntry getQueueEntry(DisplayEntity ent) {
Iterator<QueueEntry> itr = itemSet.iterator();
while (itr.hasNext()) {
QueueEntry entry = itr.next();
if (entry.entity == ent)
return entry;
}
return null;
}
/**
* Returns the position of the specified entity in the queue.
* Returns -1 if the entity is not found.
* @param ent - entity in question
* @return index of the entity in the queue.
*/
public int getPosition(DisplayEntity ent) {
int ret = 0;
Iterator<QueueEntry> itr = itemSet.iterator();
while (itr.hasNext()) {
if (itr.next().entity == ent)
return ret;
ret++;
}
return -1;
}
/**
* Removes the first entity from the queue
*/
public DisplayEntity removeFirst() {
if (itemSet.isEmpty())
error("Cannot remove an entity from an empty queue");
return this.remove(itemSet.first());
}
/**
* Returns the first entity in the queue.
* @return first entity in the queue.
*/
public DisplayEntity getFirst() {
return itemSet.first().entity;
}
/**
* Returns the number of entities in the queue
*/
public int getCount() {
return itemSet.size();
}
/**
* Returns true if the queue is empty
*/
public boolean isEmpty() {
return itemSet.isEmpty();
}
/**
* Returns the number of seconds spent by the first object in the queue
*/
public double getQueueTime() {
return this.getSimTime() - itemSet.first().timeAdded;
}
/**
* Returns the priority value for the first object in the queue
*/
public int getFirstPriority() {
return itemSet.first().priority;
}
/**
* Returns the number of times that the specified match value appears in
* the queue. If the match value is null, then every entity is counted.
* @param m - value to be matched.
* @return number of entities that have this match value.
*/
public int getMatchCount(Integer m) {
if (m == null)
return itemSet.size();
TreeSet<QueueEntry> matchSet = matchMap.get(m);
if (matchSet == null)
return 0;
return matchSet.size();
}
/**
* Returns the first entity in the queue whose match value is equal to the
* specified value. The returned entity is removed from the queue.
* If the match value is null, the first entity is removed.
* @param m - value to be matched.
* @return entity whose match value equals the specified value.
*/
public DisplayEntity removeFirstForMatch(Integer m) {
if (m == null)
return this.removeFirst();
TreeSet<QueueEntry> matchSet = matchMap.get(m);
if (matchSet == null)
return null;
return this.remove(matchSet.first());
}
public ArrayList<Integer> getUniqueMatchValues() {
ArrayList<Integer> ret = new ArrayList<>(matchMap.size());
Iterator<Integer> itr = matchMap.keySet().iterator();
while (itr.hasNext()) {
ret.add(itr.next());
}
return ret;
}
/**
* Returns the match value that has the largest number of entities in the queue.
* @return match value with the most entities.
*/
public int getMatchForMax() {
if (matchForMaxCount == null)
this.setMaxCount();
return matchForMaxCount;
}
/**
* Returns the number of entities in the longest match value queue.
* @return number of entities in the longest match value queue.
*/
public int getMaxCount() {
if (matchForMaxCount == null)
this.setMaxCount();
return maxCount;
}
/**
* Determines the longest queue for a give match value.
*/
private void setMaxCount() {
maxCount = -1;
for (Entry<Integer, TreeSet<QueueEntry>> each : matchMap.entrySet()) {
int count = each.getValue().size();
if (count > maxCount) {
maxCount = count;
matchForMaxCount = each.getKey();
}
}
}
/**
* Returns a match value that has sufficient numbers of entities in each
* queue. The first match value that satisfies the criterion is selected.
* If the numberList is too short, then the last value is used.
* @param queueList - list of queues to check.
* @param numberList - number of matches required for each queue.
* @return match value.
*/
public static Integer selectMatchValue(ArrayList<Queue> queueList, IntegerVector numberList) {
// Check whether each queue has sufficient entities for any match value
int number;
for (int i=0; i<queueList.size(); i++) {
if (numberList == null) {
number = 1;
}
else {
int ind = Math.min(i, numberList.size()-1);
number = numberList.get(ind);
}
if (queueList.get(i).getMaxCount() < number)
return null;
}
// Find the queue with the fewest match values
Queue shortest = null;
int count = -1;
for (Queue que : queueList) {
int n = que.getMatchValueCount(0.0);
if (n > count) {
count = n;
shortest = que;
}
}
// Return the first match value that has sufficient entities in each queue
for (int m : shortest.getUniqueMatchValues()) {
if (Queue.sufficientEntities(queueList, numberList, m))
return m;
}
return null;
}
/**
* Returns true if each of the queues contains sufficient entities with
* the specified match value for processing to begin.
* If the numberList is too short, then the last value is used.
* If the numberList is null, then one entity per queue is required.
* If the match value m is null, then all the entities in each queue are counted.
* @param queueList - list of queues to check.
* @param numberList - number of matches required for each queue.
* @param m - match value.
* @return true if there are sufficient entities in each queue.
*/
public static boolean sufficientEntities(ArrayList<Queue> queueList, IntegerVector numberList, Integer m) {
int number;
for (int i=0; i<queueList.size(); i++) {
if (numberList == null) {
number = 1;
}
else {
int ind = Math.min(i, numberList.size()-1);
number = numberList.get(ind);
}
if (queueList.get(i).getMatchCount(m) < number)
return false;
}
return true;
}
/**
* Update the position of all entities in the queue. ASSUME that entities
* will line up according to the orientation of the queue.
*/
@Override
public void updateGraphics(double simTime) {
Vec3d queueOrientation = getOrientation();
Vec3d qSize = this.getSize();
Vec3d tmp = new Vec3d();
double distanceX = 0.5d * qSize.x;
double distanceY = 0;
double maxWidth = 0;
// Copy the item set to avoid some concurrent modification exceptions
TreeSet<QueueEntry> itemSetCopy = new TreeSet<>(itemSet);
// find widest vessel
if (itemSetCopy.size() > maxPerLine.getValue()){
Iterator<QueueEntry> itr = itemSetCopy.iterator();
while (itr.hasNext()) {
maxWidth = Math.max(maxWidth, itr.next().entity.getSize().y);
}
}
// update item locations
int i = 0;
Iterator<QueueEntry> itr = itemSetCopy.iterator();
while (itr.hasNext()) {
DisplayEntity item = itr.next().entity;
// if new row is required, set reset distanceX and move distanceY up one row
if( i > 0 && i % maxPerLine.getValue() == 0 ){
distanceX = 0.5d * qSize.x;
distanceY += spacing.getValue() + maxWidth;
}
i++;
// Rotate each transporter about its center so it points to the right direction
item.setOrientation(queueOrientation);
Vec3d itemSize = item.getSize();
distanceX += spacing.getValue() + 0.5d * itemSize.x;
tmp.set3(-distanceX / qSize.x, distanceY/qSize.y, 0.0d);
// increment total distance
distanceX += 0.5d * itemSize.x;
// Set Position
Vec3d itemCenter = this.getGlobalPositionForAlignment(tmp);
item.setGlobalPositionForAlignment(item.getAlignment(), itemCenter);
}
}
// *******************************************************************************************************
// STATISTICS
// *******************************************************************************************************
@Override
public void clearStatistics() {
super.clearStatistics();
double simTime = this.getSimTime();
startOfStatisticsCollection = simTime;
timeOfLastUpdate = simTime;
minElements = itemSet.size();
maxElements = itemSet.size();
elementSeconds = 0.0;
squaredElementSeconds = 0.0;
for (int i=0; i<queueLengthDist.size(); i++) {
queueLengthDist.set(i, 0.0d);
}
numberReneged = 0;
}
private void updateStatistics(int oldValue, int newValue) {
minElements = Math.min(newValue, minElements);
maxElements = Math.max(newValue, maxElements);
// Add the necessary number of additional bins to the queue length distribution
int n = newValue + 1 - queueLengthDist.size();
for (int i = 0; i < n; i++) {
queueLengthDist.add(0.0);
}
double simTime = this.getSimTime();
double dt = simTime - timeOfLastUpdate;
if (dt > 0.0) {
elementSeconds += dt * oldValue;
squaredElementSeconds += dt * oldValue * oldValue;
queueLengthDist.addAt(dt,oldValue); // add dt to the entry at index queueSize
timeOfLastUpdate = simTime;
}
}
@Override
public void linkTo(DisplayEntity nextEnt) {
if (!(nextEnt instanceof LinkedService))
return;
LinkedService serv = (LinkedService)nextEnt;
serv.addQueue(this);
}
// LinkDisplayable
@Override
public ArrayList<Entity> getDestinationEntities() {
ArrayList<Entity> ret = super.getDestinationEntities();
Linkable l = renegeDestination.getValue();
if (l != null && (l instanceof Entity)) {
ret.add((Entity)l);
}
return ret;
}
// ******************************************************************************************************
// OUTPUT METHODS
// ******************************************************************************************************
@Output(name = "QueueLength",
description = "The present number of entities in the queue.",
unitType = DimensionlessUnit.class,
sequence = 0)
public int getQueueLength(double simTime) {
return itemSet.size();
}
@Output(name = "QueueList",
description = "The entities in the queue.",
sequence = 1)
public ArrayList<DisplayEntity> getQueueList(double simTime) {
ArrayList<DisplayEntity> ret = new ArrayList<>(itemSet.size());
Iterator<QueueEntry> itr = itemSet.iterator();
while (itr.hasNext()) {
ret.add(itr.next().entity);
}
return ret;
}
@Output(name = "QueueTimes",
description = "The waiting time for each entity in the queue.",
unitType = TimeUnit.class,
sequence = 2)
public ArrayList<Double> getQueueTimes(double simTime) {
ArrayList<Double> ret = new ArrayList<>(itemSet.size());
Iterator<QueueEntry> itr = itemSet.iterator();
while (itr.hasNext()) {
ret.add(simTime - itr.next().timeAdded);
}
return ret;
}
@Output(name = "PriorityValues",
description = "The Priority expression value for each entity in the queue.",
unitType = DimensionlessUnit.class,
sequence = 3)
public IntegerVector getPriorityValues(double simTime) {
IntegerVector ret = new IntegerVector(itemSet.size());
Iterator<QueueEntry> itr = itemSet.iterator();
while (itr.hasNext()) {
ret.add(itr.next().priority);
}
return ret;
}
@Output(name = "MatchValues",
description = "The Match expression value for each entity in the queue.",
unitType = DimensionlessUnit.class,
sequence = 4)
public IntegerVector getMatchValues(double simTime) {
IntegerVector ret = new IntegerVector(itemSet.size());
Iterator<QueueEntry> itr = itemSet.iterator();
while (itr.hasNext()) {
Integer val = itr.next().match;
if (val != null) {
ret.add(val);
}
}
return ret;
}
@Output(name = "QueueLengthAverage",
description = "The average number of entities in the queue.",
unitType = DimensionlessUnit.class,
reportable = true,
sequence = 5)
public double getQueueLengthAverage(double simTime) {
double dt = simTime - timeOfLastUpdate;
int queueSize = itemSet.size();
double totalTime = simTime - startOfStatisticsCollection;
if (totalTime > 0.0) {
return (elementSeconds + dt*queueSize)/totalTime;
}
return 0.0;
}
@Output(name = "QueueLengthStandardDeviation",
description = "The standard deviation of the number of entities in the queue.",
unitType = DimensionlessUnit.class,
reportable = true,
sequence = 6)
public double getQueueLengthStandardDeviation(double simTime) {
double dt = simTime - timeOfLastUpdate;
int queueSize = itemSet.size();
double mean = this.getQueueLengthAverage(simTime);
double totalTime = simTime - startOfStatisticsCollection;
if (totalTime > 0.0) {
return Math.sqrt( (squaredElementSeconds + dt*queueSize*queueSize)/totalTime - mean*mean );
}
return 0.0;
}
@Output(name = "QueueLengthMinimum",
description = "The minimum number of entities in the queue.",
unitType = DimensionlessUnit.class,
reportable = true,
sequence = 7)
public int getQueueLengthMinimum(double simTime) {
return minElements;
}
@Output(name = "QueueLengthMaximum",
description = "The maximum number of entities in the queue.",
unitType = DimensionlessUnit.class,
reportable = true,
sequence = 8)
public int getQueueLengthMaximum(double simTime) {
// An entity that is added to an empty queue and removed immediately
// does not count as a non-zero queue length
if (maxElements == 1 && queueLengthDist.get(1) == 0.0)
return 0;
return maxElements;
}
@Output(name = "QueueLengthTimes",
description = "The total time that the queue has length 0, 1, 2, etc.",
unitType = TimeUnit.class,
reportable = true,
sequence = 9)
public DoubleVector getQueueLengthDistribution(double simTime) {
DoubleVector ret = new DoubleVector(queueLengthDist);
double dt = simTime - timeOfLastUpdate;
int queueSize = itemSet.size();
if (ret.size() == 0)
ret.add(0.0);
ret.addAt(dt, queueSize);
return ret;
}
@Output(name = "AverageQueueTime",
description = "The average time each entity waits in the queue. Calculated as total queue time to date divided " +
"by the total number of entities added to the queue.",
unitType = TimeUnit.class,
reportable = true,
sequence = 10)
public double getAverageQueueTime(double simTime) {
long n = this.getNumberAdded();
if (n == 0)
return 0.0;
double dt = simTime - timeOfLastUpdate;
int queueSize = itemSet.size();
return (elementSeconds + dt*queueSize)/n;
}
@Output(name = "MatchValueCount",
description = "The present number of unique match values in the queue.",
unitType = DimensionlessUnit.class,
sequence = 11)
public int getMatchValueCount(double simTime) {
return matchMap.size();
}
@Output(name = "NumberReneged",
description = "The number of entities that reneged from the queue.",
unitType = DimensionlessUnit.class,
reportable = true,
sequence = 12)
public long getNumberReneged(double simTime) {
return numberReneged;
}
@Output(name = "QueuePosition",
description = "The position in the queue for an entity undergoing the RenegeCondition test.\n"
+ "First in queue = 1, second in queue = 2, etc.",
unitType = DimensionlessUnit.class,
reportable = false,
sequence = 13)
public long getQueuePosition(double simTime) {
DisplayEntity objEnt = this.getReceivedEntity(simTime);
if (objEnt == null)
return -1;
int pos = this.getPosition(objEnt);
if (pos >= 0)
pos++;
return pos;
}
}