Learning Objectives

  • To be able to model a tandem queue
  • To be able to use the station package within the KSL

Tandem Queue

A tandem queue is a sequence of queues that must be visited (in order) to receive service from resources. The following example presents an illustrative situation.

Remarks for Modeling a Tandem Queue

  • The first thing to note about this situation is that the system consists of two very similar components: station 1 and station 2.
  • The second thing to note is that the same types of events occur for each of the two components.
  • Since the same logic will need to be implemented for each station, it makes sense from an object-oriented perspective, to conceptualize a class that encapsulates the data and behavior to represent this situation.
    • We should build a class that models a single queue that holds objects that must wait for a server to be available. We are going to call this thing a SingleQStation.

A Simple Resource

Resource Concepts

  • A resource has a capacity that represents the maximum number of units that it can have available at any time.
  • When a resource is seized some amount of units become busy (or allocated). When the units are no longer needed, they are released.
  • If we let \(A(t)\) be the number of available units, \(B(t)\) be the number of busy units and \(c\) be the capacity of the resource, we have that \(c = A(t) + B(t)\) or \(A(t) = c - B(t)\).
  • A resource is considered busy if \(B(t) > 0\). That is, a resource is busy if some units are allocated. A resource is considered idle if no units are busy. That is, \(B(t) = 0\), which implies that \(A(t) = c\).

A Single Queue Station (SingleQStation)

  • The SingleQStation class uses an instance of the SResource class to represent its resource.
  • In addition, the SingleQStation class uses an instance of the Queue class.

Processing an Arrival

  • The logic of the process() function should look very familiar.
  • The arriving customer immediately enters the queue. Then, if the resource is available, the next customer is placed into service.
    /**
     *  Receives the qObject instance for processing. Handle the queuing
     *  if the resource is not available and begins service for the next customer.
     */
    override fun process(arrivingQObject: QObject) {
        // enqueue the newly arriving qObject
        myWaitingQ.enqueue(arrivingQObject)
        if (isResourceAvailable) {
            serveNext()
        }
    }

Serving the Next Customer

The serveNext() function removes the next customer from the queue, seizes the resource, and schedules the end of processing event for the customer.

    /**
     * Called to determine which waiting QObject will be served next Determines
     * the next customer, seizes the resource, and schedules the end of the
     * service.
     */
    protected fun serveNext() {
        //remove the next customer
        val nextCustomer = myWaitingQ.removeNext()!!
        myResource.seize()
        // schedule end of service, if the customer can supply a value,
        // use it otherwise use the processing time RV
        schedule(this::endOfProcessing, delayTime(nextCustomer), nextCustomer)
    }

    /**
     *  Could be overridden to supply different approach for determining the service delay
     */
    protected fun delayTime(qObject: QObject) : Double {
        return qObject.valueObject?.value ?: myActivityTimeRV.value
    }

Completing the Service of the Customer

  • As mentioned, the endOfProcessing() function represents the logic that should occur after the customer completes its use of the resource for the processing time.
  • The function sendToNextReceiver() will be discussed further within the following slides.
    /**
     *  The end of processing event actions. Collect departing statistics and send the qObject
     *  to its next receiver. If the queue is not empty, continue processing the next qObject.
     */
    private fun endOfProcessing(event: KSLEvent<QObject>) {
        val leaving: QObject = event.message!!
        myResource.release()
        if (isQueueNotEmpty) { // queue is not empty
            serveNext()
        }
        sendToNextReceiver(leaving)
    }

Receiving Customers

  • An important interface is the QObjectReceiverIfc interface that promises to allow the receiving of QObject
fun interface QObjectReceiverIfc {
    fun receive(qObject: ModelElement.QObject)
}
  • Note that the QObjectReceiverIfc interface is SAM functional interface.
  • Classes that implement the QObjectReceiverIfc interface promise to have a receive() function.

The SingleQStation Class

  • As shown in the following code, the SingleQStation class extends the Station class, which implements the QObjectReceiverIfc interface.
  • The activityTime parameter determines the service time.
  • The resource parameter allows the specification of a possibly shared resource. If it is not supplied, one will be created.
  • The nextReceiver parameter indicates what object will receive the completed customer.
open class SingleQStation(
    parent: ModelElement,
    activityTime: RVariableIfc = ConstantRV.ZERO,
    resource: SResource? = null,
    nextReceiver: QObjectReceiverIfc = NotImplementedReceiver,
    name: String? = null
) : Station(parent, nextReceiver, name = name), SingleQStationCIfc {
...
}

The Station Class

  • The function departuerCollection() is used to collect statistics.
  • The optional exitAction can be used to define additional actions upon departure.
  • If the sender property is not null, it is used to send the completed object.
  • If the completed object has is own sender it is used. Otherwise, the next receiver property is used to receive the completed customer.
abstract class Station(parent: ModelElement,
    private var nextReceiver: QObjectReceiverIfc = NotImplementedReceiver, name: String? = null
) : ModelElement(parent, name), QObjectReceiverIfc, StationCIfc {
...
    protected fun sendToNextReceiver(completedQObject: QObject) {
        departureCollection(completedQObject)
        exitAction?.onExit(completedQObject)
        onExit(completedQObject)
        if (sender != null) {
            sender!!.send(completedQObject)
        } else {
            if (completedQObject.sender != null) {
                completedQObject.sender!!.send(completedQObject)
            } else {
                nextReceiver.receive(completedQObject)
            }
        }
    }

Modeling the Tandem Queue

  • Standard approach to defining statistics to be collected.
class TandemQueue(
    parent: ModelElement,
    name: String? = null
) : ModelElement(parent, name) {

    private val myNS: TWResponse = TWResponse(parent = this, name = "${this.name}:NS")
    val numInSystem: TWResponseCIfc
        get() = myNS

    private val mySysTime: Response = Response(parent = this, name = "${this.name}:TotalSystemTime")
    val totalSystemTime: ResponseCIfc
        get() = mySysTime

    private val myNumProcessed: Counter = Counter(parent = this, name = "${this.name}:TotalProcessed")
    val totalProcessed: CounterCIfc
        get() = myNumProcessed
...

Modeling the Tandem Queue

  • An event generator is used to generate the arrivals.
  • Two instances of the SingleQStation class are used to model the queues.
  • Station 2 is the receiver for station 1. An instance of ExitSystem() is the receiver for station 2.
    private val ad = ExponentialRV(6.0, 1)
    private val myArrivalGenerator: EventGenerator = EventGenerator(parent = this,
        generateAction = this::arrivalEvent, timeUntilFirstRV = ad, timeBtwEventsRV = ad)

    private val myStation1: SingleQStation = SingleQStation(parent = this,
        activityTime = ExponentialRV(4.0, 2),name = "${this.name}:Station1")
    val station1: SingleQStationCIfc
        get() = myStation1

    private val myStation2: SingleQStation = SingleQStation(parent = this,
        activityTime = ExponentialRV(3.0, 3),name = "${this.name}:Station2")
    val station2: SingleQStationCIfc
        get() = myStation2

    init {
        myStation1.nextReceiver(myStation2)
        myStation2.nextReceiver(ExitSystem())
    }

Modeling the Arrival to and Exit from the System

  • The event generator creates the customer and tells station 1 to receive it.
  • The exit system implementation collects statistics upon exit.
    private fun arrivalEvent(generator: EventGenerator){
        val customer = QObject()
        myNS.increment()
        myStation1.receive(customer)
    }

    private inner class ExitSystem : QObjectReceiverIfc {
        override fun receive(qObject: QObject) {
            mySysTime.value = time - qObject.createTime
            myNumProcessed.increment()
            myNS.decrement()
        }
    }
}

Running the Model

fun main(){
    val sim = Model("TandemQ Model")
    sim.numberOfReplications = 30
    sim.lengthOfReplication = 20000.0
    sim.lengthOfReplicationWarmUp = 5000.0
    val tq = TandemQueue(sim, name = "TandemQ")
    sim.simulate()
    sim.print()
}

Model Output

Name Count Average Half-Width
TandemQ:NS 30 3.04 0.093
TandemQ:TotalSystemTime 30 18.132 0.5
TandemQ:Station1:R:NumBusy 30 0.671 0.008
TandemQ:Station1:R:Util 30 0.671 0.008
TandemQ:Station1:NS 30 2.031 0.088
TandemQ:Station1:StationTime 30 12.107 0.483
TandemQ:Station1:Q:NumInQ 30 1.359 0.081
TandemQ:Station1:Q:TimeInQ 30 8.101 0.457
TandemQ:Station2:R:NumBusy 30 0.5 0.004
TandemQ:Station2:R:Util 30 0.5 0.004
TandemQ:Station2:NS 30 1.01 0.023
TandemQ:Station2:StationTime 30 6.029 0.135
TandemQ:Station2:Q:NumInQ 30 0.51 0.02
TandemQ:Station2:Q:TimeInQ 30 3.042 0.12
TandemQ:TotalProcessed 30 2512.667 17.41
TandemQ:Station1:NumProcessed 30 2512.7 17.535
TandemQ:Station2:NumProcessed 30 2512.667 17.41

The Station Package

Summary of New Constructs

SResource: A simple resource that has 1 or more units that represent its capacity. The units can be seized and released. Statistics on the utilization and number of busy units are automatically reported.

SingleQStation: A station that has a single waiting line for customers to wait in when its associated resource does not have available units. Statistics on time spent at the station, number of customers at the station, and number of customers processed are automatically reported.

⌂ Index