Learning Objectives

  • To understand how conveyors model entity movement over a fixed path
  • To distinguish accumulating from non-accumulating conveyors
  • To understand the cell-based representation of conveyor space and entity size
  • To build conveyors with the Conveyor class, ConveyorSegments/Segment, and the Conveyor.builder
  • To use the conveyor suspend functions (requestConveyor, rideConveyor, exitConveyor, convey, transferTo) to move entities

What is a Conveyor?

  • A conveyor is a track, belt, or other device that provides movement over a fixed path with fixed loading and discharge points
  • Used for high-volume items over short-to-medium distances; speeds from 20–80 up to ~500 feet per minute; gravity-based or powered
  • Each entity must wait for sufficient space on the conveyor before it can gain entry and begin its transfer

Conveyor modeling is roughly classified as:

  • Accumulating — items keep moving forward when there is a blockage
  • Non-accumulating — items stop when the conveyor stops
  • Fixed spacing — items keep a fixed space or ride in a bucket/bin (e.g. an escalator step)
  • Random spacing — items take up space in no particular position (e.g. an airport people mover)

When Do You Need Conveyor Constructs?

Performance measures to consider when designing conveyor systems:

  • Throughput capacity — loads processed per time
  • Delivery time — time to move an item from origin to destination
  • Queue lengths — queues to get on the conveyor and behind blockages
  • Carriers used / space utilized — the number of cells on the conveyor occupied

You do not always need specialized constructs:

  • A gravity conveyor can be modeled with a deterministic delay

  • The front of a conveyor can be modeled as a resource plus a small load delay

  • Use conveyor constructs when the space allocated to the conveyor is important to system operation

  • The KSL maps space/distance to resources called cells

Modeling a Conveyor as Cells

  • Space along the path is divided into equal units of resource called cells — the conveyor is a set of moving cells of equal length
  • The cell size is the smallest portion of a conveyor that an entity can occupy
  • Example: a 15-foot conveyor with 1-foot cells has 15 cells
  • Whenever an entity reaches an entry point it must wait for enough unoccupied, available, consecutive cells to hold its size
  • Think of an escalator with each cell being a step

A conveyor conceptualized as a set of contiguous cells

Entity Sizes and “Taking a Step”

  • Entity size matters: an item must acquire enough contiguous cells to hold its physical size (think of a rider with a suitcase taking two steps)
  • To “take a step,” an entity needs the next cell; as it crosses over it releases the previous cell — its step size is 1 cell at a time
  • This produces a repeated pattern of overlapping seize() and release() calls that depends on entity size and number of cells
  • Smaller cells let an entity “creep” on more continuously; larger cells force longer move delays and longer waits for space

Different entity sizes on a conveyor

Activity diagram for a non-accumulating conveyor

Non-Accumulating vs. Accumulating Conveyors

Non-accumulating — items stop when the conveyor stops

  • When an entity enters or reaches its destination, the entire conveyor disengages until instructions transfer or remove the entity
  • Spacing between entities stays the same; all entities experience every other entity’s loading/unloading delays

Accumulating — the conveyor is always running

  • When an entity stops (e.g. to be processed), entities behind it queue up on the conveyor; entities in front continue moving

  • Spacing decreases at the blockage until entities bump together; when the blockage ends, blocked entities move once cells free up

  • Analogy: wearing roller blades on an airport people mover, everyone piling up behind a bar placed across it — an accumulating conveyor

Segments, the Conveyor Class, and the Builder

  • A conveyor is a series of Segments; each has an origin and destination given as String locations (a conveyor uses no spatial model)
  • Segments are supplied via the ConveyorSegments collection of Segment data objects, or fluently via Conveyor.builder(...)
  • A conveyor has one cell size, one velocity (all segments move together), and a maxEntityCellsAllowed; each segment length is an integer multiple of the cell size
  • Cells are numbered 1..n; the conveyor is circular when the first entry location equals the last exit location

Conveyor segments

The Conveyor.builder Fluent API

  • The builder constructs a conveyor segment-by-segment; the first location of firstSegment reused as the last nextSegment makes the conveyor circular
  • Locations are Strings — using a model element’s name gives a unique location string
loopConveyor = Conveyor.builder(this, "LoopConveyor")
    .conveyorType(Conveyor.Type.NON_ACCUMULATING)
    .velocity(30.0)
    .cellSize(10)
    .maxCellsAllowed(2)
    .firstSegment(myDrillingResource.name, myMillingResource.name, 70)
    .nextSegment(myPlaningResource.name, 90)
    .nextSegment(myGrindingResource.name, 50)
    .nextSegment(myInspectionResource.name, 180)
    .nextSegment(myDrillingResource.name, 250)
    .build()

Conveyor Requests and the Suspend Functions

  • Holding cells on a conveyor requires a ConveyorRequestIfc — like an Allocation, it is the “ticket” to ride and is required when exiting
  • requestConveyor(conveyor, entryLocation, numCellsNeeded) — returns a ConveyorRequestIfc; waits for the cells, then holds them (not yet riding)
  • rideConveyor(conveyorRequest, destination) — moves the item to the destination cell
  • exitConveyor(conveyorRequest) — moves through and releases the cells
  • convey(conveyor, entryLocation, destination, numCellsNeeded) — combines request + ride + exit
  • transferTo(conveyorRequest, nextConveyor, entryLocation) — transfers to another conveyor
val cr = requestConveyor(conveyor = conveyor, entryLocation = enter, numCellsNeeded = 1)
rideConveyor(conveyorRequest = cr, destination = station1)
exitConveyor(conveyorRequest = cr)

The ConveyorRequestIfc Interface

Conveyor requests

Tandem Queue with Conveyors: Delay Baseline

  • One conveyor at 30 fpm with three segments (70, 40, 60 feet): enter → station1 → station2 → exit
  • Baseline: represent conveyor travel with deterministic delays (distance / velocity)
  • Because the delay is deterministic, parts exit in the same order they enter — a useful baseline for the detailed models

Tandem queue with conveyors

private inner class Part : Entity() {
    val tandemQProcess: KSLProcess = process(isDefaultProcess = true) {
        wip.increment()
        timeStamp = time
        delay(delayDuration = 70.0/30.0) // 30 fpm for 70 ft
        use(resource = worker1, delayDuration = st1)
        delay(delayDuration = 40.0/30.0) // 30 fpm for 40 ft
        use(resource = worker2, delayDuration = st2)
        delay(delayDuration = 60.0/30.0) // 30 fpm for 60 ft
        timeInSystem.value = time - timeStamp
        wip.decrement()
    }
}

Tandem Queue: Work Performed Off the Conveyor

  • The conveyor is built with Conveyor.builder: velocity 30, cellSize(1), maxCellsAllowed(1), segments firstSegment(enter, station1, 70), nextSegment(station2, 40), nextSegment(exit, 60)
  • The part exits the conveyor at each station, is processed, then re-requests to convey to the next station
  • convey() combines requestConveyor + rideConveyor + exitConveyor into one call, shortening the process
private inner class Part : Entity() {
    val tandemQProcess: KSLProcess = process(isDefaultProcess = true) {
        wip.increment()
        timeStamp = time
        convey(conveyor = conveyor, entryLocation = enter, destination = station1)
        use(resource = worker1, delayDuration = st1)
        convey(conveyor = conveyor, entryLocation = station1, destination = station2)
        use(resource = worker2, delayDuration = st2)
        convey(conveyor = conveyor, entryLocation = station2, destination = exit)
        timeInSystem.value = time - timeStamp
        wip.decrement()
    }
}
  • Both accumulating and non-accumulating perform well here; time in system is ~1 minute above the delay baseline (time to move through the entry/exit cells)

Tandem Queue: Work Performed On the Conveyor

  • Keep the same conveyorRequest, rideConveyor between stations, and exitConveyor only at the end — the part stays on the conveyor while being processed
  • Cannot use convey() (it exits every time); the use() calls happen while the part still occupies its cells
val conveyorRequest = requestConveyor(conveyor = conveyor, entryLocation = enter, numCellsNeeded = 1)
rideConveyor(conveyorRequest = conveyorRequest, destination = station1)
use(resource = worker1, delayDuration = st1)
rideConveyor(conveyorRequest = conveyorRequest, destination = station2)
use(resource = worker2, delayDuration = st2)
rideConveyor(conveyorRequest = conveyorRequest, destination = exit)
exitConveyor(conveyorRequest = conveyorRequest)

Working on the conveyor

  • Accumulating: parts pile up behind the blockage; station queues read 0 while occupied cells absorb the waiting — the key lesson
  • Non-accumulating: processing stops the whole conveyor, so the entry queue explodes (time in system ~2530 vs ~17) — inappropriate unless the work is perfectly balanced

Test and Repair via a Circular Conveyor

  • A circular, accumulating loop conveyor (velocity 10 fpm, cellSize(1), 5 segments totaling 130 feet) links diagnostics, the three test stations, and repair
  • Parts have different sizes — test plans 1 & 2 need 1 cell, plans 3 & 4 need 2 cells — so maxCellsAllowed(2)
  • Locations are the resources’ names; loopConveyor.accessQueueAt(myRepair.name).defaultReportingOption = false turns off unused access-queue stats
// parts following different test plans need different cell counts
private val cellSizes = mapOf(testPlan1 to 1, testPlan2 to 1, testPlan3 to 2, testPlan4 to 2)
val cellsNeeded = cellSizes[plan]!!

val testAndRepairProcess: KSLProcess = process(isDefaultProcess = true) {
    use(resource = myDiagnostics, delayDuration = diagnosticTime)
    var entryLocation = myDiagnostics.name
    for (tp in plan) {
        convey(conveyor = loopConveyor, entryLocation = entryLocation,
            destination = tp.resource.name, numCellsNeeded = cellsNeeded)
        use(resource = tp.resource, delayDuration = tp.processTime)
        entryLocation = tp.resource.name
    }
}

Merging, Diverging, and Recirculating

Two conveyors merge into one

One conveyor diverges into two
  • Merge: separate Conveyor instances share a location; transferTo() moves the entity onto the next conveyor and returns a new ConveyorRequestIfc for the ride (a feeder can also merge into the mid-point of a main conveyor)
  • Diverge: the reverse — at the sorting point an if/when picks which conveyor to transferTo
  • Recirculate: on a circular conveyor, an entity with no room at its station keeps riding around, using the conveyor itself as waiting space
val cr = requestConveyor(conveyor = conveyor1, entryLocation = areaA, numCellsNeeded = 1)
rideConveyor(conveyorRequest = cr, destination = sorting)
val tr = transferTo(conveyorRequest = cr, nextConveyor = conveyor3, entryLocation = sorting)
rideConveyor(conveyorRequest = tr, destination = areaC)
exitConveyor(conveyorRequest = tr)

Summary of New Concepts

  • Conveyors model movement over a fixed path by mapping space to resources called cells; entity size sets how many contiguous cells are needed and step size is 1 cell
  • Non-accumulating conveyors stop entirely at a blockage; accumulating conveyors keep running while entities queue up on the conveyor behind the blockage
  • Build conveyors with Conveyor.builder or ConveyorSegments + Segment; a conveyor is circular when its start and end locations coincide
  • Suspend functions: requestConveyor, rideConveyor, exitConveyor, convey (request + ride + exit), and transferTo (move between conveyors)
  • Working off vs on the conveyor changes where you exitConveyor; non-accumulating conveyors are a poor fit for work-on-conveyor unless perfectly balanced
  • Merge, diverge, and recirculate patterns are built from multiple conveyors joined with transferTo and from circular conveyors
⌂ Index