-- Example: Machine Tool with Breakdowns
--
-- It is described in different sources [1, 2]. So, this is chapter 13 of [2] and section 6.12 of [1].
--
-- [1] A. Alan B. Pritsker, Simulation with Visual SLAM and AweSim, 2nd ed.
--
-- [2] Труб И.И., Объектно-ориентированное моделирование на C++: Учебный курс. - СПб.: Питер, 2006
--
-- Jobs arrive to a machine tool on the average of one per hour. The distribution of
-- these interarrival times is exponential. During normal operation, the jobs are
-- processed on a first-in, first-out basis. The time to process a job in hours is
-- normally distributed with a mean of 0.5 and a standard deviation of 0.1. In addition
-- to the processing time, there is a set up time that is uniformly distributed between
-- 0.2 and 0.5 of an hour. Jobs that have been processed by the machine tool are routed
-- to a different section of the shop and are considered to have left the machine tool
-- area.
--
-- The machine tool experiences breakdowns during which time it can no longer process
-- jobs. The time between breakdowns is normally distributed with a mean of 20 hours
-- and a standard deviation of 2 hours. When a breakdown occurs, the job being processed
-- is removed from the machine tool and is placed at the head of the queue of jobs
-- waiting to be processed. Jobs preempted restart from the point at which they were
-- interrupted.
--
-- When the machine tool breaks down, a repair process is initiated which is
-- accomplished in three phases. Each phase is exponentially distributed with a mean of
-- 3/4 of an hour. Since the repair time is the sum of independent and identically
-- distributed exponential random variables, the repair time is Erlang distributed.
-- The machine tool is to be analyzed for 500 hours to obtain information on
-- the utilization of the machine tool and the time required to process a job.
-- Statistics are to be collected for thousand simulation runs.
import Control.Monad
import Control.Monad.Trans
import Control.Category
import Data.Monoid
import Data.List
import Simulation.Aivika
import qualified Simulation.Aivika.Queue.Infinite as IQ
import qualified Simulation.Aivika.Resource.Preemption as PR
-- | The simulation specs.
specs = Specs { spcStartTime = 0.0,
spcStopTime = 500.0,
spcDT = 0.1,
spcMethod = RungeKutta4,
spcGeneratorType = SimpleGenerator }
-- | How often do jobs arrive to a machine tool (exponential)?
jobArrivingMu = 1
-- | A mean of time to process a job (normal).
jobProcessingMu = 0.5
-- | The standard deviation of time to process a job (normal).
jobProcessingSigma = 0.1
-- | The minimum set-up time (uniform).
minSetUpTime = 0.2
-- | The maximum set-up time (uniform).
maxSetUpTime = 0.5
-- | A mean of time between breakdowns (normal).
breakdownMu = 20
-- | The standard deviation of time between breakdowns (normal).
breakdownSigma = 2
-- | A mean of each of the three repair phases (Erlang).
repairMu = 3/4
-- | A priority of the job (less is higher)
jobPriority = 1
-- | A priority of the breakdown (less is higher)
breakdownPriority = 0
-- | The simulation model.
model :: Simulation Results
model = do
-- create an input queue
inputQueue <- runEventInStartTime IQ.newFCFSQueue
-- a counter of jobs completed
jobsCompleted <- newArrivalTimer
-- a counter of interrupted jobs
jobsInterrupted <- newRef (0 :: Int)
-- create an input stream
let inputStream =
randomExponentialStream jobArrivingMu
-- create a preemptible resource
tool <- PR.newResource 1
-- the machine setting up
machineSettingUp <-
newPreemptibleRandomUniformServer True minSetUpTime maxSetUpTime
-- the machine processing
machineProcessing <-
newPreemptibleRandomNormalServer True jobProcessingMu jobProcessingSigma
-- the machine breakdown
let machineBreakdown =
do randomNormalProcess_ breakdownMu breakdownSigma
PR.usingResourceWithPriority tool breakdownPriority $
randomErlangProcess_ repairMu 3
machineBreakdown
-- start the process of breakdowns
runProcessInStartTime machineBreakdown
-- update a counter of job interruptions
runEventInStartTime $
handleSignal_ (serverTaskPreemptionBeginning machineProcessing) $ \a ->
modifyRef jobsInterrupted (+ 1)
-- define the queue network
let network =
queueProcessor
(\a -> liftEvent $ IQ.enqueue inputQueue a)
(IQ.dequeue inputQueue) >>>
(withinProcessor $ PR.requestResourceWithPriority tool jobPriority) >>>
serverProcessor machineSettingUp >>>
serverProcessor machineProcessing >>>
(withinProcessor $ PR.releaseResource tool) >>>
arrivalTimerProcessor jobsCompleted
-- start the machine tool
runProcessInStartTime $
sinkStream $ runProcessor network inputStream
-- return the simulation results in start time
return $
results
[resultSource
"inputQueue" "the queue of jobs"
inputQueue,
--
resultSource
"machineSettingUp" "the machine setting up"
machineSettingUp,
--
resultSource
"machineProcessing" "the machine processing"
machineProcessing,
--
resultSource
"jobsInterrupted" "a counter of the interrupted jobs"
jobsInterrupted,
--
resultSource
"jobsCompleted" "a counter of the completed jobs"
jobsCompleted]
main =
printSimulationResultsInStopTime
printResultSourceInEnglish
(fmap resultSummary model) specs