{-# LANGUAGE Arrows #-}
-- Example: In this example, the operations of a quarry are modeled.
--
-- It is described in different sources [1, 2]. So, this is chapter 10 of [2] and section 5.16 of [1].
--
-- [1] A. Alan B. Pritsker, Simulation with Visual SLAM and AweSim, 2nd ed.
-- [2] Труб И.И., Объектно-ориентированное моделирование на C++: Учебный курс. - СПб.: Питер, 2006
--
-- In this example, the operations of a quarry are modeled. In the quarry,
-- trucks deliver ore from three shovels to a crusher. A truck always
-- returns to its assigned shovel after dumping a load at the crusher.
-- There are two different truck sizes in use, twenty-ton and fifty-ton.
-- The size of the truck affects its loading time at the shovel, travel
-- time to the crusher, dumping time at the crusher and return trip time
-- from the crusher back to the appropriate shovel. For the twenty-ton
-- trucks, there loading, travel, dumping and return trip times are:
-- exponentially distributed with a mean 5; a constant 2.5; exponentially
-- distributed with mean 2; and a constant 1.5. The corresponding times
-- for the fifty-ton trucks are: exponentially distributed with mean 10;
-- a constant 3; exponentially distributed with mean 4; and a constant 2.
-- To each shovel is assigned two twenty-ton trucks are one fifty-ton truck.
-- The shovel queues are all ranked on a first-in, first-out basis.
-- The crusher queue is ranked on truck size, largest trucks first.
-- It is desired to analyze this system over 480 time units to determine
-- the utilization and queue lengths associated with the shovels and crusher.
module Model(model) where
import Control.Monad
import Control.Monad.Trans
import Control.Category
import Simulation.Aivika
import qualified Simulation.Aivika.Queue.Infinite as IQ
-- | The average loading time for twenty-ton truck
avgLoadingTime20 = 5
-- | A constant travel time for twenty-ton truck
travelTime20 = 2.5
-- | The average dumping time for twenty-ton truck
avgDumpingTime20 = 2
-- | A constant return trip time for twenty-ton truck
returnTripTime20 = 1.5
-- | A priority of the twenty-ton truck (less is higher)
crushingPriority20 = 2
-- | The average loading time for fifty-ton truck
avgLoadingTime50 = 10
-- | A constant travel time for fifty-ton truck
travelTime50 = 3
-- | The average dumping time for fifty-ton truck
avgDumpingTime50 = 4
-- | A constant return trip time for fifty-ton truck
returnTripTime50 = 2
-- | A priority of the fifty-ton truck (less is higher)
crushingPriority50 = 1
-- | It models a truck assigned to some queue.
data Truck =
Truck { truckQueue :: TruckQueue,
-- ^ a queue to which the truck is assigned
truckTonSize :: TruckTonSize,
-- ^ the truck ton size
truckAvgLoadingTime :: Double,
-- ^ the average loading time
truckTravelTime :: Double,
-- ^ a constant travel time
truckCrushingPriority :: Double,
-- ^ a priority for crushing (less is higher)
truckAvgDumpingTime :: Double,
-- ^ the average dumping time
truckReturnTripTime :: Double
-- ^ a constant return trip time
}
-- | It defines the truck ton size
data TruckTonSize = TwentyTonSize | FiftyTonSize
-- | Specifies a queue to which the truck is assigned
data TruckQueue = TruckQueue1 | TruckQueue2 | TruckQueue3
-- | Return a truck assigned to the specified queue with the given ton size.
truck :: TruckQueue -> TruckTonSize -> Truck
truck tq TwentyTonSize =
Truck { truckQueue = tq,
truckTonSize = TwentyTonSize,
truckAvgLoadingTime = avgLoadingTime20,
truckTravelTime = travelTime20,
truckCrushingPriority = crushingPriority20,
truckAvgDumpingTime = avgDumpingTime20,
truckReturnTripTime = returnTripTime20 }
truck tq FiftyTonSize =
Truck { truckQueue = tq,
truckTonSize = FiftyTonSize,
truckAvgLoadingTime = avgLoadingTime50,
truckTravelTime = travelTime50,
truckCrushingPriority = crushingPriority50,
truckAvgDumpingTime = avgDumpingTime50,
truckReturnTripTime = returnTripTime50 }
model :: Simulation Results
model = do
-- create a queue for the first shovel
shovelQueue1 <-
runEventInStartTime IQ.newFCFSQueue
-- create another queue for the second shovel
shovelQueue2 <-
runEventInStartTime IQ.newFCFSQueue
-- create a queue for the thrid shovel
shovelQueue3 <-
runEventInStartTime IQ.newFCFSQueue
-- add initial trucks to the queue
let initShovelQueue q tq =
do IQ.enqueue q $ truck tq TwentyTonSize
IQ.enqueue q $ truck tq TwentyTonSize
IQ.enqueue q $ truck tq FiftyTonSize
-- initiate the three shovel queues
runEventInStartTime $
do initShovelQueue shovelQueue1 TruckQueue1
initShovelQueue shovelQueue2 TruckQueue2
initShovelQueue shovelQueue3 TruckQueue3
-- create a priority queue for the crusher
crusherQueue <-
runEventInStartTime IQ.newPriorityQueue
-- define how the specified truck travels from the shovel to the crusher
let truckTravel t =
spawnProcess $
do holdProcess (truckTravelTime t)
liftEvent $
IQ.enqueueWithStoringPriority crusherQueue (truckCrushingPriority t) t
-- define how the specified truck returns to the queue
let truckReturnTrip t =
spawnProcess $
do holdProcess (truckReturnTripTime t)
let q = case truckQueue t of
TruckQueue1 -> shovelQueue1
TruckQueue2 -> shovelQueue2
TruckQueue3 -> shovelQueue3
liftEvent $
IQ.enqueue q t
-- utilise the crusher's activity
let utiliseCrusher q t =
do randomExponentialProcess_ $
truckAvgDumpingTime t
return t
-- utilise the shovel's activity
let utiliseShovel q t =
do randomExponentialProcess_ $
truckAvgLoadingTime t
return t
-- create shovel activities
shovelAct1 <-
newActivity $ utiliseShovel shovelQueue1
shovelAct2 <-
newActivity $ utiliseShovel shovelQueue2
shovelAct3 <-
newActivity $ utiliseShovel shovelQueue3
-- create the crusher's activity
crusherAct <-
newActivity $ utiliseCrusher crusherQueue
-- define how we should iterate the crusher
let crusherNet act q =
proc () ->
do t <- arrNet (const $ IQ.dequeue q) -< ()
t' <- activityNet act -< t
arrNet truckReturnTrip -< t'
let shovelNet act q =
proc () ->
do t <- arrNet (const $ IQ.dequeue q) -< ()
t' <- activityNet act -< t
arrNet truckTravel -< t'
-- start processing the cursher's queue
runProcessInStartTime $
iterateNet (crusherNet crusherAct crusherQueue) ()
-- start processing the shovel queues
runProcessInStartTime $
iterateNet (shovelNet shovelAct1 shovelQueue1) ()
runProcessInStartTime $
iterateNet (shovelNet shovelAct2 shovelQueue2) ()
runProcessInStartTime $
iterateNet (shovelNet shovelAct3 shovelQueue3) ()
-- return the simulation results in start time
return $
results
[resultSource
"shovelQueue" "the shovel's queue"
[shovelQueue1, shovelQueue2, shovelQueue3],
--
resultSource
"crusherQueue" "the crusher's queue"
crusherQueue,
--
resultSource
"shovelActvty" "the shovel's activity"
[shovelAct1, shovelAct2, shovelAct3],
--
resultSource
"crusherActvty" "the crusher's activity"
crusherAct]