-- It corresponds to model TimeOut described in document -- Advanced Features of the SimPy Language -- [http://heather.cs.ucdavis.edu/~matloff/156/PLN/AdvancedSimPy.pdf]. -- SimPy is available on [http://simpy.sourceforge.net/]. -- -- The model description is as follows. -- -- Introductory example to illustrate the modeling of "competing -- events" such as timeouts, especially using the cancelProcess function. A -- network node sends a message but also sets a timeout period; if the -- node times out, it assumes the message it had sent was lost, and it -- will send again. The time to get an acknowledgement for a message is -- exponentially distributed with mean 1.0, and the timeout period is -- 0.5. Immediately after receiving an acknowledgement, the node sends -- out a new message. -- -- We find the proportion of messages which timeout. The output should -- be about 0.61. import Control.Monad import Control.Monad.Trans import Simulation.Aivika ackRate = 1.0 / 1.0 -- reciprocal of the acknowledge mean time toPeriod = 0.5 -- timeout period specs = Specs { spcStartTime = 0.0, spcStopTime = 10000.0, spcDT = 1.0, spcMethod = RungeKutta4, spcGeneratorType = SimpleGenerator } model :: Simulation Double model = do -- number of messages sent nMsgs <- newRef 0 -- number of timeouts which have occured nTimeOuts <- newRef 0 -- reactivatedCode will 1 if timeout occurred, -- 2 ACK if received reactivatedCode <- newRef 0 nodePid <- newProcessId let node :: Process () node = do liftEvent $ modifyRef nMsgs $ (+) 1 -- create process IDs timeoutPid <- liftSimulation newProcessId ackPid <- liftSimulation newProcessId -- set up the timeout liftEvent $ runProcessUsingId timeoutPid (timeout ackPid) -- set up the message send/ACK liftEvent $ runProcessUsingId ackPid (acknowledge timeoutPid) passivateProcess liftEvent $ do code <- readRef reactivatedCode when (code == 1) $ modifyRef nTimeOuts $ (+) 1 writeRef reactivatedCode 0 node timeout :: ProcessId -> Process () timeout ackPid = do holdProcess toPeriod liftEvent $ do writeRef reactivatedCode 1 reactivateProcess nodePid cancelProcessWithId ackPid acknowledge :: ProcessId -> Process () acknowledge timeoutPid = do ackTime <- liftParameter $ randomExponential (1 / ackRate) holdProcess ackTime liftEvent $ do writeRef reactivatedCode 2 reactivateProcess nodePid cancelProcessWithId timeoutPid runProcessInStartTimeUsingId nodePid node runEventInStopTime $ do x <- readRef nTimeOuts y <- readRef nMsgs return $ x / y main = do putStr "The percentage of timeout was " runSimulation model specs >>= print