aivika-1.1: examples/SimpleWorkflow.hs
-- This is a model of the workflow with originally two work places. Also there are two finite queues.
--
-- It is described in different sources [1, 2]. So, this is chapter 7 of [2].
--
-- [1] { add a foreign source in English }
--
-- [2] Труб И.И., Объектно-ориентированное моделирование на C++: Учебный курс. - СПб.: Питер, 2006
import Prelude hiding (id, (.))
import Control.Monad
import Control.Monad.Trans
import Control.Arrow
import Control.Category (id, (.))
import Simulation.Aivika
import Simulation.Aivika.Queue
-- | The simulation specs.
specs = Specs { spcStartTime = 0.0,
spcStopTime = 300.0,
spcDT = 0.1,
spcMethod = RungeKutta4,
spcGeneratorType = SimpleGenerator }
-- the mean delay of the input arrivals distributed exponentially
meanOrderDelay = 0.4
-- the capacity of the queue before the first work places
queueMaxCount1 = 4
-- the capacity of the queue before the second work places
queueMaxCount2 = 2
-- the mean processing time distributed exponentially in
-- the first work places
meanProcessingTime1 = 0.25
-- the mean processing time distributed exponentially in
-- the second work places
meanProcessingTime2 = 0.5
-- the number of the first work places
-- (in parallel but the commented code allocates them sequentially)
workplaceCount1 = 1
-- the number of the second work places
-- (in parallel but the commented code allocates them sequentially)
workplaceCount2 = 1
-- create an accumulator to gather the queue size statistics
newQueueSizeAccumulator queue =
newTimingStatsAccumulator $
Signalable (queueCount queue) (queueCountChanged_ queue)
-- create a workflow with the exponential processing time
newWorkplaceExponential meanTime =
newServer $ \a ->
do holdProcess =<<
(liftParameter $
randomExponential meanTime)
return a
-- interpose the prefetch processor between two processors
interposePrefetchProcessor x y =
x >>> prefetchProcessor >>> y
model :: Simulation ()
model = do
-- it will gather the statistics of the processing time
arrivalTimer <- newArrivalTimer
-- define a stream of input events
let inputStream = randomExponentialStream meanOrderDelay
-- create a queue before the first work place
queue1 <- newFCFSQueue queueMaxCount1
-- create a queue before the second work place
queue2 <- newFCFSQueue queueMaxCount2
-- the first queue size statistics
queueSizeAcc1 <-
runEventInStartTime $
newQueueSizeAccumulator queue1
-- the second queue size statistics
queueSizeAcc2 <-
runEventInStartTime $
newQueueSizeAccumulator queue2
-- create the first work places, i.e. the "servers"
workplace1s <- forM [1 .. workplaceCount1] $ \_ ->
newWorkplaceExponential meanProcessingTime1
-- create the second work places, i.e. the "servers"
workplace2s <- forM [1 .. workplaceCount2] $ \_ ->
newWorkplaceExponential meanProcessingTime2
-- processor for the queue before the first work place
let queueProcessor1 =
queueProcessor
(\a -> liftEvent $ enqueueOrLost_ queue1 a)
(dequeue queue1)
-- processor for the queue before the second work place
let queueProcessor2 =
queueProcessor
(enqueue queue2)
(dequeue queue2)
-- the entire processor from input to output
let entireProcessor =
queueProcessor1 >>>
processorParallel (map serverProcessor workplace1s) >>>
-- foldr1 interposePrefetchProcessor (map serverProcessor workplace1s) >>>
queueProcessor2 >>>
processorParallel (map serverProcessor workplace2s) >>>
-- foldr1 interposePrefetchProcessor (map serverProcessor workplace2s) >>>
arrivalTimerProcessor arrivalTimer
-- start simulating the model
runProcessInStartTime $
sinkStream $ runProcessor entireProcessor inputStream
-- show the results in the final time
runEventInStopTime $
do queueSum1 <- queueSummary queue1 2
queueSum2 <- queueSummary queue2 2
workplaceSum1s <- forM workplace1s $ \x -> serverSummary x 2
workplaceSum2s <- forM workplace2s $ \x -> serverSummary x 2
processingTime <- arrivalProcessingTime arrivalTimer
queueSize1 <- timingStatsAccumulated queueSizeAcc1
queueSize2 <- timingStatsAccumulated queueSizeAcc2
liftIO $
do putStrLn ""
putStrLn "--- the first queue summary (in the final time) ---"
putStrLn ""
putStrLn $ queueSum1 []
putStrLn ""
forM_ (zip [1..] workplaceSum1s) $ \(i, x) ->
do putStrLn $ "--- the first work place no." ++ show i ++ " (in the final time) ---"
putStrLn ""
putStrLn $ x []
putStrLn ""
putStrLn "--- the second queue summary (in the final time) ---"
putStrLn ""
putStrLn $ queueSum2 []
putStrLn ""
forM_ (zip [1..] workplaceSum2s) $ \(i, x) ->
do putStrLn $ "--- the second work place no. " ++ show i ++ " (in the final time) ---"
putStrLn ""
putStrLn $ x []
putStrLn ""
putStrLn "--- the processing time summary ---"
putStrLn ""
putStrLn $ samplingStatsSummary processingTime 2 []
putStrLn ""
putStrLn "--- the first queue size summary ---"
putStrLn ""
putStrLn $ timingStatsSummary queueSize1 2 []
putStrLn ""
putStrLn "--- the second queue size summary ---"
putStrLn ""
putStrLn $ timingStatsSummary queueSize2 2 []
putStrLn ""
main = runSimulation model specs