packages feed

aivika-distributed 0.1.1 → 0.1.3

raw patch · 10 files changed

+823/−50 lines, 10 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Simulation.Aivika.Distributed.Optimistic.Message: enqueueMessage :: Serializable a => ProcessId -> Double -> a -> Event DIO ()
+ Simulation.Aivika.Distributed.Optimistic.Message: enqueueMessage :: forall a. Serializable a => ProcessId -> Double -> a -> Event DIO ()
- Simulation.Aivika.Distributed.Optimistic.Message: messageReceived :: Serializable a => Signal DIO a
+ Simulation.Aivika.Distributed.Optimistic.Message: messageReceived :: forall a. Serializable a => Signal DIO a
- Simulation.Aivika.Distributed.Optimistic.Message: sendMessage :: Serializable a => ProcessId -> a -> Event DIO ()
+ Simulation.Aivika.Distributed.Optimistic.Message: sendMessage :: forall a. Serializable a => ProcessId -> a -> Event DIO ()

Files

Simulation/Aivika/Distributed/Optimistic/Internal/DIO.hs view
@@ -127,7 +127,7 @@ -- | The default parameters for the 'DIO' computation defaultDIOParams :: DIOParams defaultDIOParams =-  DIOParams { dioLoggingPriority = DEBUG,+  DIOParams { dioLoggingPriority = WARNING,               dioUndoableLogSizeThreshold = 500000,               dioOutputMessageQueueSizeThreshold = 10000,               dioSyncTimeout = 5000000,
Simulation/Aivika/Distributed/Optimistic/Internal/TimeServer.hs view
@@ -68,7 +68,7 @@ -- | The default time server parameters. defaultTimeServerParams :: TimeServerParams defaultTimeServerParams =-  TimeServerParams { tsLoggingPriority = DEBUG,+  TimeServerParams { tsLoggingPriority = WARNING,                      tsExpectTimeout = 1000,                      tsTimeSyncDelay = 1000                    }
aivika-distributed.cabal view
@@ -1,5 +1,5 @@ name:            aivika-distributed-version:         0.1.1+version:         0.1.3 synopsis:        Parallel distributed simulation library description:     This package extends the Aivika library with facilities for running parallel distributed simulations.@@ -17,8 +17,12 @@ tested-with:     GHC == 7.10.1  extra-source-files:  tests/MachRep1.hs+                     tests/MachRep1Simple.hs                      tests/MachRep2.hs+                     tests/MachRep2Distributed.hs                      tests/MachRep2Reproducible.hs+                     tests/MachRep2Sync.hs+                     tests/MachRep2SyncIO.hs  library 
tests/MachRep1.hs view
@@ -18,19 +18,6 @@ -- Output is long-run proportion of up time. Should get value of about -- 0.66. -{--ghc -threaded MachRep1Distributed.hs--Fire up some slave nodes (for the example, we run them on a single machine):--./MachRep1Distributed slave localhost 8080 &-./MachRep1Distributed slave localhost 8081 &--And start the master node:--./MachRep1Distributed master localhost 8084--}- import System.Environment (getArgs)  import Data.Typeable@@ -115,8 +102,6 @@     m  = do runSimulation (slaveModel masterId) specs             unregisterDIO --- remotable ['runSlaveModel, 'timeServer]- runMasterModel :: DP.ProcessId -> Int -> DP.Process (DP.ProcessId, DP.Process Double) runMasterModel timeServerId n =   runDIO m ps timeServerId@@ -128,12 +113,9 @@  master = \backend nodes ->   do liftIO . putStrLn $ "Slaves: " ++ show nodes-     timeServerId <- DP.spawnLocal $ timeServer defaultTimeServerParams-     -- timeServerId <- DP.spawn node1 ($(mkClosure 'timeServer) defaultTimeServerParams)+     let timeServerParams = defaultTimeServerParams { tsLoggingPriority = DEBUG }+     timeServerId <- DP.spawnLocal $ timeServer timeServerParams      (masterId, masterProcess) <- runMasterModel timeServerId 2-     -- (masterId, masterProcess) <- runMasterModel timeServerId (length nodes)-     -- forM_ nodes $ \node ->-     --   DP.spawn node ($(mkClosure 'runSlaveModel) (timeServerId, masterId))      forM_ [1..2] $ \i ->        do (slaveId, slaveProcess) <- runSlaveModel (timeServerId, masterId)           DP.spawnLocal slaveProcess@@ -145,14 +127,3 @@ main = do   backend <- initializeBackend "localhost" "8080" initRemoteTable   startMaster backend (master backend)---- main :: IO ()--- main = do---   args <- getArgs---   case args of---     ["master", host, port] -> do---       backend <- initializeBackend host port initRemoteTable---       startMaster backend (master backend)---     ["slave", host, port] -> do---       backend <- initializeBackend host port initRemoteTable---       startSlave backend
+ tests/MachRep1Simple.hs view
@@ -0,0 +1,90 @@++-- It corresponds to model MachRep1 described in document +-- Introduction to Discrete-Event Simulation and the SimPy Language+-- [http://heather.cs.ucdavis.edu/~matloff/156/PLN/DESimIntro.pdf]. +-- SimPy is available on [http://simpy.sourceforge.net/].+--   +-- The model description is as follows.+--+-- Two machines, which sometimes break down.+-- Up time is exponentially distributed with mean 1.0, and repair time is+-- exponentially distributed with mean 0.5. There are two repairpersons,+-- so the two machines can be repaired simultaneously if they are down+-- at the same time.+--+-- Output is long-run proportion of up time. Should get value of about+-- 0.66.++import Control.Monad+import Control.Monad.Trans+import Control.Concurrent+import qualified Control.Distributed.Process as DP+import Control.Distributed.Process.Node (initRemoteTable)+import Control.Distributed.Process.Backend.SimpleLocalnet++import Simulation.Aivika.Trans+import Simulation.Aivika.Distributed++meanUpTime = 1.0+meanRepairTime = 0.5++specs = Specs { spcStartTime = 0.0,+                spcStopTime = 10000.0,+                spcDT = 1.0,+                spcMethod = RungeKutta4,+                spcGeneratorType = SimpleGenerator }+        +model :: Simulation DIO Double+model =+  do totalUpTime <- newRef 0.0+     +     let machine =+           do upTime <-+                liftParameter $+                randomExponential meanUpTime+              holdProcess upTime+              liftEvent $ +                modifyRef totalUpTime (+ upTime)+              repairTime <-+                liftParameter $+                randomExponential meanRepairTime+              holdProcess repairTime+              machine++     runProcessInStartTime machine+     runProcessInStartTime machine++     let upTimeProp =+           do x <- readRef totalUpTime+              y <- liftDynamics time+              return $ x / (2 * y)++     syncEventInStopTime $+       liftIO $ putStrLn "Test IO"+     +     runEventInStopTime upTimeProp++runModel :: DP.ProcessId -> DP.Process ()+runModel timeServerId =+  do DP.say "Started simulating..."+     let ps = defaultDIOParams { dioLoggingPriority = NOTICE }+         m =+           do a <- runSimulation model specs+              terminateDIO+              return a+     (modelId, modelProcess) <- runDIO m ps timeServerId+     a <- modelProcess+     DP.say $ "The result is " ++ show a++master = \backend nodes ->+  do liftIO . putStrLn $ "Slaves: " ++ show nodes+     let timeServerParams = defaultTimeServerParams { tsLoggingPriority = DEBUG }+     timeServerId  <- DP.spawnLocal $ timeServer timeServerParams+     runModel timeServerId+     liftIO $+       threadDelay 1000000+  +main :: IO ()+main = do+  backend <- initializeBackend "localhost" "8080" initRemoteTable+  startMaster backend (master backend)
tests/MachRep2.hs view
@@ -20,8 +20,6 @@ -- that a given machine does not have immediate access to the repairperson  -- when the machine breaks down. Output values should be about 0.6 and 0.67.  --- import System.Environment (getArgs)- import Data.Typeable import Data.Binary @@ -197,8 +195,6 @@     m  = do runSimulation (slaveModel masterId) specs             unregisterDIO --- remotable ['runSlaveModel, 'timeServer]- runMasterModel :: DP.ProcessId -> Int -> DP.Process (DP.ProcessId, DP.Process (Double, Double)) runMasterModel timeServerId n =   runDIO m ps timeServerId@@ -211,12 +207,9 @@ master = \backend nodes ->   do liftIO . putStrLn $ "Slaves: " ++ show nodes      let n = 2-     timeServerId <- DP.spawnLocal $ timeServer defaultTimeServerParams-     -- timeServerId <- DP.spawn node1 ($(mkClosure 'timeServer) defaultTimeServerParams)+         timeServerParams = defaultTimeServerParams { tsLoggingPriority = DEBUG }+     timeServerId <- DP.spawnLocal $ timeServer timeServerParams      (masterId, masterProcess) <- runMasterModel timeServerId n-     -- (masterId, masterProcess) <- runMasterModel timeServerId (length nodes)-     -- forM_ nodes $ \node ->-     --   DP.spawn node ($(mkClosure 'runSlaveModel) (timeServerId, masterId))      forM_ [1..n] $ \i ->        do (slaveId, slaveProcess) <- runSlaveModel (timeServerId, masterId)           DP.spawnLocal slaveProcess
+ tests/MachRep2Distributed.hs view
@@ -0,0 +1,262 @@++{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE DeriveGeneric #-}++-- It corresponds to model MachRep2 described in document +-- Introduction to Discrete-Event Simulation and the SimPy Language+-- [http://heather.cs.ucdavis.edu/~matloff/156/PLN/DESimIntro.pdf]. +-- SimPy is available on [http://simpy.sourceforge.net/].+--   +-- The model description is as follows.+--   +-- Two machines, but sometimes break down. Up time is exponentially +-- distributed with mean 1.0, and repair time is exponentially distributed +-- with mean 0.5. In this example, there is only one repairperson, so +-- the two machines cannot be repaired simultaneously if they are down +-- at the same time.+--+-- In addition to finding the long-run proportion of up time as in+-- model MachRep1, let’s also find the long-run proportion of the time +-- that a given machine does not have immediate access to the repairperson +-- when the machine breaks down. Output values should be about 0.6 and 0.67. ++-- Compiling and Running+---+-- Compile using+--+-- ghc -threaded MachRep2Distributed.hs+--+-- Fire up some slave nodes (for the example, we run them on a single machine):+--+-- ./MachRep2Distributed slave localhost 8080 &+-- ./MachRep2Distributed slave localhost 8081 &+-- ./MachRep2Distributed slave localhost 8082 &+--+-- And start the master node:+--+-- ./MachRep2Distributed master localhost 8088+--++import System.Environment (getArgs)++import Data.Typeable+import Data.Binary++import GHC.Generics++import Control.Monad+import Control.Monad.Trans+import Control.Concurrent+import qualified Control.Distributed.Process as DP+import Control.Distributed.Process.Closure+import Control.Distributed.Process.Node (initRemoteTable)+import Control.Distributed.Process.Backend.SimpleLocalnet++import Simulation.Aivika.Trans+import Simulation.Aivika.Distributed++meanUpTime = 1.0+meanRepairTime = 0.5++specs = Specs { spcStartTime = 0.0,+                spcStopTime = 1000.0,+                spcDT = 1.0,+                spcMethod = RungeKutta4,+                spcGeneratorType = SimpleGenerator }++-- | The time shift when replying the messages.+delta = 0.01++data TotalUpTimeChange = TotalUpTimeChange (DP.ProcessId, Double) deriving (Eq, Ord, Show, Typeable, Generic)+data TotalUpTimeChangeResp = TotalUpTimeChangeResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++data NRepChange = NRepChange (DP.ProcessId, Int) deriving (Eq, Ord, Show, Typeable, Generic)+data NRepChangeResp = NRepChangeResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++data NImmedRepChange = NImmedRepChange (DP.ProcessId, Int) deriving (Eq, Ord, Show, Typeable, Generic)+data NImmedRepChangeResp = NImmedRepChangeResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++data RepairPersonCount = RepairPersonCount DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)+data RepairPersonCountResp = RepairPersonCountResp (DP.ProcessId, Int) deriving (Eq, Ord, Show, Typeable, Generic)++data RequestRepairPerson = RequestRepairPerson DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)+data RequestRepairPersonResp = RequestRepairPersonResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++data ReleaseRepairPerson = ReleaseRepairPerson DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)+data ReleaseRepairPersonResp = ReleaseRepairPersonResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++instance Binary TotalUpTimeChange+instance Binary TotalUpTimeChangeResp++instance Binary NRepChange+instance Binary NRepChangeResp++instance Binary NImmedRepChange+instance Binary NImmedRepChangeResp++instance Binary RepairPersonCount+instance Binary RepairPersonCountResp++instance Binary RequestRepairPerson+instance Binary RequestRepairPersonResp++instance Binary ReleaseRepairPerson+instance Binary ReleaseRepairPersonResp++-- | A sub-model.+slaveModel :: DP.ProcessId -> Simulation DIO ()+slaveModel masterId =+  do inboxId <- liftComp messageInboxId++     let machine =+           do t <- liftDynamics time+              upTime <-+                liftParameter $ randomExponential meanUpTime+              enqueueMessage masterId (t + delta + upTime) (TotalUpTimeChange (inboxId, upTime))+              enqueueMessage masterId (t + delta + upTime) (NRepChange (inboxId, 1))+              enqueueMessage masterId (t + delta + upTime) (RepairPersonCount inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(RepairPersonCountResp (senderId, n)) ->+       do t <- liftDynamics time+          when (n == 1) $+            enqueueMessage masterId (t + delta) (NImmedRepChange (inboxId, 1))+          enqueueMessage masterId (t + delta) (RequestRepairPerson inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(RequestRepairPersonResp senderId) ->+       do t <- liftDynamics time+          repairTime <-+            liftParameter $ randomExponential meanRepairTime+          enqueueMessage masterId (t + delta + repairTime) (ReleaseRepairPerson inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(ReleaseRepairPersonResp senderId) ->+       machine+  +     runEventInStartTime machine++     syncEventInStopTime $+       liftIO $ putStrLn "The sub-model finished"++-- | The main model.       +masterModel :: Int -> Simulation DIO (Double, Double)+masterModel count =+  do totalUpTime <- newRef 0.0+     nRep <- newRef 0+     nImmedRep <- newRef 0++     let maxRepairPersonCount = 1+     repairPerson <- newFCFSResource maxRepairPersonCount++     inboxId <- liftComp messageInboxId++     runEventInStartTime $+       handleSignal messageReceived $ \(TotalUpTimeChange (senderId, x)) ->+       do modifyRef totalUpTime (+ x)+          t <- liftDynamics time+          enqueueMessage senderId (t + delta) (TotalUpTimeChangeResp inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(NRepChange (senderId, x)) ->+       do modifyRef nRep (+ x)+          t <- liftDynamics time+          enqueueMessage senderId (t + delta) (NRepChangeResp inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(NImmedRepChange (senderId, x)) ->+       do modifyRef nImmedRep (+ x)+          t <- liftDynamics time+          enqueueMessage senderId (t + delta) (NImmedRepChangeResp inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(RepairPersonCount senderId) ->+       do n <- resourceCount repairPerson+          t <- liftDynamics time+          enqueueMessage senderId (t + delta) (RepairPersonCountResp (inboxId, n))++     runEventInStartTime $+       handleSignal messageReceived $ \(RequestRepairPerson senderId) ->+       runProcess $+       do requestResource repairPerson+          t <- liftDynamics time+          liftEvent $+            enqueueMessage senderId (t + delta) (RequestRepairPersonResp inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(ReleaseRepairPerson senderId) ->+       do t <- liftDynamics time+          releaseResourceWithinEvent repairPerson+          enqueueMessage senderId (t + delta) (ReleaseRepairPersonResp inboxId)+          +     let upTimeProp =+           do x <- readRef totalUpTime+              y <- liftDynamics time+              return $ x / (fromIntegral count * y)++         immedProp =+           do n <- readRef nRep+              nImmed <- readRef nImmedRep+              return $+                fromIntegral nImmed /+                fromIntegral n++     runEventInStopTime $+       do x <- upTimeProp+          y <- immedProp+          return (x, y)++runSlaveModel :: (DP.ProcessId, DP.ProcessId) -> DP.Process (DP.ProcessId, DP.Process ())+runSlaveModel (timeServerId, masterId) =+  runDIO m ps timeServerId+  where+    ps = defaultDIOParams { dioLoggingPriority = WARNING }+    m  = do runSimulation (slaveModel masterId) specs+            unregisterDIO++startSlaveModel :: (DP.ProcessId, DP.ProcessId) -> DP.Process ()+startSlaveModel x@(timeServerId, masterId) =+  do (slaveId, slaveProcess) <- runSlaveModel x+     slaveProcess++remotable ['startSlaveModel, 'timeServer]++runMasterModel :: DP.ProcessId -> Int -> DP.Process (DP.ProcessId, DP.Process (Double, Double))+runMasterModel timeServerId n =+  runDIO m ps timeServerId+  where+    ps = defaultDIOParams { dioLoggingPriority = WARNING }+    m  = do a <- runSimulation (masterModel n) specs+            terminateDIO+            return a++master = \backend nodes ->+  do liftIO . putStrLn $ "Slaves: " ++ show nodes+     let [n0, n1, n2] = nodes+         timeServerParams = defaultTimeServerParams { tsLoggingPriority = DEBUG }+     -- timeServerId <- DP.spawnLocal $ timeServer timeServerParams+     timeServerId <- DP.spawn n0 ($(mkClosure 'timeServer) timeServerParams)+     -- (masterId, masterProcess) <- runMasterModel timeServerId 2+     -- forM_ [1..2] $ \i ->+     --   do (slaveId, slaveProcess) <- runSlaveModel (timeServerId, masterId)+     --      DP.spawnLocal slaveProcess+     (masterId, masterProcess) <- runMasterModel timeServerId 2+     forM_ [n1, n2] $ \node ->+        DP.spawn node ($(mkClosure 'startSlaveModel) (timeServerId, masterId))+     a <- masterProcess+     DP.say $+       "The result is " ++ show a++main :: IO ()+main = do+  args <- getArgs+  case args of+    ["master", host, port] -> do+      backend <- initializeBackend host port rtable+      startMaster backend (master backend)+    ["slave", host, port] -> do+      backend <- initializeBackend host port rtable+      startSlave backend+  where+    rtable :: DP.RemoteTable+    rtable = __remoteTable initRemoteTable
tests/MachRep2Reproducible.hs view
@@ -20,8 +20,6 @@ -- that a given machine does not have immediate access to the repairperson  -- when the machine breaks down. Output values should be about 0.6 and 0.67.  --- import System.Environment (getArgs)- import Data.Typeable import Data.Binary @@ -227,12 +225,9 @@ master = \backend nodes ->   do liftIO . putStrLn $ "Slaves: " ++ show nodes      let n = length seeds-     timeServerId <- DP.spawnLocal $ timeServer defaultTimeServerParams-     -- timeServerId <- DP.spawn node1 ($(mkClosure 'timeServer) defaultTimeServerParams)+         timeServerParams = defaultTimeServerParams { tsLoggingPriority = DEBUG }+     timeServerId <- DP.spawnLocal $ timeServer timeServerParams      (masterId, masterProcess) <- runMasterModel timeServerId n-     -- (masterId, masterProcess) <- runMasterModel timeServerId (length nodes)-     -- forM_ nodes $ \node ->-     --   DP.spawn node ($(mkClosure 'runSlaveModel) (timeServerId, masterId))      forM_ [1..n] $ \i ->        do (slaveId, slaveProcess) <- runSlaveModel (timeServerId, masterId, i)           DP.spawnLocal slaveProcess
+ tests/MachRep2Sync.hs view
@@ -0,0 +1,228 @@++{--# LANGUAGE TemplateHaskell #--}+{-# LANGUAGE DeriveGeneric #-}++-- It corresponds to model MachRep2 described in document +-- Introduction to Discrete-Event Simulation and the SimPy Language+-- [http://heather.cs.ucdavis.edu/~matloff/156/PLN/DESimIntro.pdf]. +-- SimPy is available on [http://simpy.sourceforge.net/].+--   +-- The model description is as follows.+--   +-- Two machines, but sometimes break down. Up time is exponentially +-- distributed with mean 1.0, and repair time is exponentially distributed +-- with mean 0.5. In this example, there is only one repairperson, so +-- the two machines cannot be repaired simultaneously if they are down +-- at the same time.+--+-- In addition to finding the long-run proportion of up time as in+-- model MachRep1, let’s also find the long-run proportion of the time +-- that a given machine does not have immediate access to the repairperson +-- when the machine breaks down. Output values should be about 0.6 and 0.67. ++import Data.Typeable+import Data.Binary++import GHC.Generics++import Control.Monad+import Control.Monad.Trans+import Control.Concurrent+import qualified Control.Distributed.Process as DP+import Control.Distributed.Process.Closure+import Control.Distributed.Process.Node (initRemoteTable)+import Control.Distributed.Process.Backend.SimpleLocalnet++import Simulation.Aivika.Trans+import Simulation.Aivika.Distributed++meanUpTime = 1.0+meanRepairTime = 0.5++specs = Specs { spcStartTime = 0.0,+                spcStopTime = 1000.0,+                spcDT = 1.0,+                spcMethod = RungeKutta4,+                spcGeneratorType = SimpleGenerator }++-- | The time shift when replying the messages.+delta = 0.01++data TotalUpTimeChange = TotalUpTimeChange (DP.ProcessId, Double) deriving (Eq, Ord, Show, Typeable, Generic)+data TotalUpTimeChangeResp = TotalUpTimeChangeResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++data NRepChange = NRepChange (DP.ProcessId, Int) deriving (Eq, Ord, Show, Typeable, Generic)+data NRepChangeResp = NRepChangeResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++data NImmedRepChange = NImmedRepChange (DP.ProcessId, Int) deriving (Eq, Ord, Show, Typeable, Generic)+data NImmedRepChangeResp = NImmedRepChangeResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++data RepairPersonCount = RepairPersonCount DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)+data RepairPersonCountResp = RepairPersonCountResp (DP.ProcessId, Int) deriving (Eq, Ord, Show, Typeable, Generic)++data RequestRepairPerson = RequestRepairPerson DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)+data RequestRepairPersonResp = RequestRepairPersonResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++data ReleaseRepairPerson = ReleaseRepairPerson DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)+data ReleaseRepairPersonResp = ReleaseRepairPersonResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++instance Binary TotalUpTimeChange+instance Binary TotalUpTimeChangeResp++instance Binary NRepChange+instance Binary NRepChangeResp++instance Binary NImmedRepChange+instance Binary NImmedRepChangeResp++instance Binary RepairPersonCount+instance Binary RepairPersonCountResp++instance Binary RequestRepairPerson+instance Binary RequestRepairPersonResp++instance Binary ReleaseRepairPerson+instance Binary ReleaseRepairPersonResp++-- | A sub-model.+slaveModel :: DP.ProcessId -> Simulation DIO ()+slaveModel masterId =+  do inboxId <- liftComp messageInboxId++     let machine =+           do t <- liftDynamics time+              upTime <-+                liftParameter $ randomExponential meanUpTime+              enqueueMessage masterId (t + delta + upTime) (TotalUpTimeChange (inboxId, upTime))+              enqueueMessage masterId (t + delta + upTime) (NRepChange (inboxId, 1))+              enqueueMessage masterId (t + delta + upTime) (RepairPersonCount inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(RepairPersonCountResp (senderId, n)) ->+       do t <- liftDynamics time+          when (n == 1) $+            enqueueMessage masterId (t + delta) (NImmedRepChange (inboxId, 1))+          enqueueMessage masterId (t + delta) (RequestRepairPerson inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(RequestRepairPersonResp senderId) ->+       do t <- liftDynamics time+          repairTime <-+            liftParameter $ randomExponential meanRepairTime+          enqueueMessage masterId (t + delta + repairTime) (ReleaseRepairPerson inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(ReleaseRepairPersonResp senderId) ->+       machine+  +     runEventInStartTime machine++     syncEventInStopTime $+       liftIO $ putStrLn "The sub-model finished"++-- | The main model.       +masterModel :: Int -> Simulation DIO (Double, Double)+masterModel count =+  do totalUpTime <- newRef 0.0+     nRep <- newRef 0+     nImmedRep <- newRef 0++     let maxRepairPersonCount = 1+     repairPerson <- newFCFSResource maxRepairPersonCount++     inboxId <- liftComp messageInboxId++     runEventInStartTime $+       handleSignal messageReceived $ \(TotalUpTimeChange (senderId, x)) ->+       do modifyRef totalUpTime (+ x)+          t <- liftDynamics time+          enqueueMessage senderId (t + delta) (TotalUpTimeChangeResp inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(NRepChange (senderId, x)) ->+       do modifyRef nRep (+ x)+          t <- liftDynamics time+          enqueueMessage senderId (t + delta) (NRepChangeResp inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(NImmedRepChange (senderId, x)) ->+       do modifyRef nImmedRep (+ x)+          t <- liftDynamics time+          enqueueMessage senderId (t + delta) (NImmedRepChangeResp inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(RepairPersonCount senderId) ->+       do n <- resourceCount repairPerson+          t <- liftDynamics time+          enqueueMessage senderId (t + delta) (RepairPersonCountResp (inboxId, n))++     runEventInStartTime $+       handleSignal messageReceived $ \(RequestRepairPerson senderId) ->+       runProcess $+       do requestResource repairPerson+          t <- liftDynamics time+          liftEvent $+            enqueueMessage senderId (t + delta) (RequestRepairPersonResp inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(ReleaseRepairPerson senderId) ->+       do t <- liftDynamics time+          releaseResourceWithinEvent repairPerson+          enqueueMessage senderId (t + delta) (ReleaseRepairPersonResp inboxId)++     runEventInStartTime $+       forM_ [100, 200.. 1000] $ \t ->+       syncEvent t $+       return ()+          +     let upTimeProp =+           do x <- readRef totalUpTime+              y <- liftDynamics time+              return $ x / (fromIntegral count * y)++         immedProp =+           do n <- readRef nRep+              nImmed <- readRef nImmedRep+              return $+                fromIntegral nImmed /+                fromIntegral n++     runEventInStopTime $+       do x <- upTimeProp+          y <- immedProp+          return (x, y)++runSlaveModel :: (DP.ProcessId, DP.ProcessId) -> DP.Process (DP.ProcessId, DP.Process ())+runSlaveModel (timeServerId, masterId) =+  runDIO m ps timeServerId+  where+    ps = defaultDIOParams { dioLoggingPriority = WARNING }+    m  = do runSimulation (slaveModel masterId) specs+            unregisterDIO++runMasterModel :: DP.ProcessId -> Int -> DP.Process (DP.ProcessId, DP.Process (Double, Double))+runMasterModel timeServerId n =+  runDIO m ps timeServerId+  where+    ps = defaultDIOParams { dioLoggingPriority = WARNING }+    m  = do a <- runSimulation (masterModel n) specs+            terminateDIO+            return a++master = \backend nodes ->+  do liftIO . putStrLn $ "Slaves: " ++ show nodes+     let n = 2+         timeServerParams = defaultTimeServerParams { tsLoggingPriority = DEBUG }+     timeServerId <- DP.spawnLocal $ timeServer timeServerParams+     (masterId, masterProcess) <- runMasterModel timeServerId n+     forM_ [1..n] $ \i ->+       do (slaveId, slaveProcess) <- runSlaveModel (timeServerId, masterId)+          DP.spawnLocal slaveProcess+     a <- masterProcess+     DP.say $+       "The result is " ++ show a+  +main :: IO ()+main = do+  backend <- initializeBackend "localhost" "8080" initRemoteTable+  startMaster backend (master backend)
+ tests/MachRep2SyncIO.hs view
@@ -0,0 +1,230 @@++{--# LANGUAGE TemplateHaskell #--}+{-# LANGUAGE DeriveGeneric #-}++-- It corresponds to model MachRep2 described in document +-- Introduction to Discrete-Event Simulation and the SimPy Language+-- [http://heather.cs.ucdavis.edu/~matloff/156/PLN/DESimIntro.pdf]. +-- SimPy is available on [http://simpy.sourceforge.net/].+--   +-- The model description is as follows.+--   +-- Two machines, but sometimes break down. Up time is exponentially +-- distributed with mean 1.0, and repair time is exponentially distributed +-- with mean 0.5. In this example, there is only one repairperson, so +-- the two machines cannot be repaired simultaneously if they are down +-- at the same time.+--+-- In addition to finding the long-run proportion of up time as in+-- model MachRep1, let’s also find the long-run proportion of the time +-- that a given machine does not have immediate access to the repairperson +-- when the machine breaks down. Output values should be about 0.6 and 0.67. ++import Data.Typeable+import Data.Binary++import GHC.Generics++import Control.Monad+import Control.Monad.Trans+import Control.Concurrent+import qualified Control.Distributed.Process as DP+import Control.Distributed.Process.Closure+import Control.Distributed.Process.Node (initRemoteTable)+import Control.Distributed.Process.Backend.SimpleLocalnet++import Simulation.Aivika.Trans+import Simulation.Aivika.Distributed++meanUpTime = 1.0+meanRepairTime = 0.5++specs = Specs { spcStartTime = 0.0,+                spcStopTime = 1000.0,+                spcDT = 1.0,+                spcMethod = RungeKutta4,+                spcGeneratorType = SimpleGenerator }++-- | The time shift when replying the messages.+delta = 0.01++data TotalUpTimeChange = TotalUpTimeChange (DP.ProcessId, Double) deriving (Eq, Ord, Show, Typeable, Generic)+data TotalUpTimeChangeResp = TotalUpTimeChangeResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++data NRepChange = NRepChange (DP.ProcessId, Int) deriving (Eq, Ord, Show, Typeable, Generic)+data NRepChangeResp = NRepChangeResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++data NImmedRepChange = NImmedRepChange (DP.ProcessId, Int) deriving (Eq, Ord, Show, Typeable, Generic)+data NImmedRepChangeResp = NImmedRepChangeResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++data RepairPersonCount = RepairPersonCount DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)+data RepairPersonCountResp = RepairPersonCountResp (DP.ProcessId, Int) deriving (Eq, Ord, Show, Typeable, Generic)++data RequestRepairPerson = RequestRepairPerson DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)+data RequestRepairPersonResp = RequestRepairPersonResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++data ReleaseRepairPerson = ReleaseRepairPerson DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)+data ReleaseRepairPersonResp = ReleaseRepairPersonResp DP.ProcessId deriving (Eq, Ord, Show, Typeable, Generic)++instance Binary TotalUpTimeChange+instance Binary TotalUpTimeChangeResp++instance Binary NRepChange+instance Binary NRepChangeResp++instance Binary NImmedRepChange+instance Binary NImmedRepChangeResp++instance Binary RepairPersonCount+instance Binary RepairPersonCountResp++instance Binary RequestRepairPerson+instance Binary RequestRepairPersonResp++instance Binary ReleaseRepairPerson+instance Binary ReleaseRepairPersonResp++-- | A sub-model.+slaveModel :: DP.ProcessId -> Simulation DIO ()+slaveModel masterId =+  do inboxId <- liftComp messageInboxId++     let machine =+           do t <- liftDynamics time+              upTime <-+                liftParameter $ randomExponential meanUpTime+              enqueueMessage masterId (t + delta + upTime) (TotalUpTimeChange (inboxId, upTime))+              enqueueMessage masterId (t + delta + upTime) (NRepChange (inboxId, 1))+              enqueueMessage masterId (t + delta + upTime) (RepairPersonCount inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(RepairPersonCountResp (senderId, n)) ->+       do t <- liftDynamics time+          when (n == 1) $+            enqueueMessage masterId (t + delta) (NImmedRepChange (inboxId, 1))+          enqueueMessage masterId (t + delta) (RequestRepairPerson inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(RequestRepairPersonResp senderId) ->+       do t <- liftDynamics time+          repairTime <-+            liftParameter $ randomExponential meanRepairTime+          enqueueMessage masterId (t + delta + repairTime) (ReleaseRepairPerson inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(ReleaseRepairPersonResp senderId) ->+       machine+  +     runEventInStartTime machine++     syncEventInStopTime $+       liftIO $ putStrLn "The sub-model finished"++-- | The main model.       +masterModel :: Int -> Simulation DIO (Double, Double)+masterModel count =+  do totalUpTime <- newRef 0.0+     nRep <- newRef 0+     nImmedRep <- newRef 0++     let maxRepairPersonCount = 1+     repairPerson <- newFCFSResource maxRepairPersonCount++     inboxId <- liftComp messageInboxId++     runEventInStartTime $+       handleSignal messageReceived $ \(TotalUpTimeChange (senderId, x)) ->+       do modifyRef totalUpTime (+ x)+          t <- liftDynamics time+          enqueueMessage senderId (t + delta) (TotalUpTimeChangeResp inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(NRepChange (senderId, x)) ->+       do modifyRef nRep (+ x)+          t <- liftDynamics time+          enqueueMessage senderId (t + delta) (NRepChangeResp inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(NImmedRepChange (senderId, x)) ->+       do modifyRef nImmedRep (+ x)+          t <- liftDynamics time+          enqueueMessage senderId (t + delta) (NImmedRepChangeResp inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(RepairPersonCount senderId) ->+       do n <- resourceCount repairPerson+          t <- liftDynamics time+          enqueueMessage senderId (t + delta) (RepairPersonCountResp (inboxId, n))++     runEventInStartTime $+       handleSignal messageReceived $ \(RequestRepairPerson senderId) ->+       runProcess $+       do requestResource repairPerson+          t <- liftDynamics time+          liftEvent $+            enqueueMessage senderId (t + delta) (RequestRepairPersonResp inboxId)++     runEventInStartTime $+       handleSignal messageReceived $ \(ReleaseRepairPerson senderId) ->+       do t <- liftDynamics time+          releaseResourceWithinEvent repairPerson+          enqueueMessage senderId (t + delta) (ReleaseRepairPersonResp inboxId)++     runEventInStartTime $+       forM_ [100, 200.. 1000] $ \t ->+       syncEvent t $+       liftIO $+       putStrLn $+       "t = " ++ show t ++ ": synchronization"+          +     let upTimeProp =+           do x <- readRef totalUpTime+              y <- liftDynamics time+              return $ x / (fromIntegral count * y)++         immedProp =+           do n <- readRef nRep+              nImmed <- readRef nImmedRep+              return $+                fromIntegral nImmed /+                fromIntegral n++     runEventInStopTime $+       do x <- upTimeProp+          y <- immedProp+          return (x, y)++runSlaveModel :: (DP.ProcessId, DP.ProcessId) -> DP.Process (DP.ProcessId, DP.Process ())+runSlaveModel (timeServerId, masterId) =+  runDIO m ps timeServerId+  where+    ps = defaultDIOParams { dioLoggingPriority = WARNING }+    m  = do runSimulation (slaveModel masterId) specs+            unregisterDIO++runMasterModel :: DP.ProcessId -> Int -> DP.Process (DP.ProcessId, DP.Process (Double, Double))+runMasterModel timeServerId n =+  runDIO m ps timeServerId+  where+    ps = defaultDIOParams { dioLoggingPriority = WARNING }+    m  = do a <- runSimulation (masterModel n) specs+            terminateDIO+            return a++master = \backend nodes ->+  do liftIO . putStrLn $ "Slaves: " ++ show nodes+     let n = 2+         timeServerParams = defaultTimeServerParams { tsLoggingPriority = DEBUG }+     timeServerId <- DP.spawnLocal $ timeServer timeServerParams+     (masterId, masterProcess) <- runMasterModel timeServerId n+     forM_ [1..n] $ \i ->+       do (slaveId, slaveProcess) <- runSlaveModel (timeServerId, masterId)+          DP.spawnLocal slaveProcess+     a <- masterProcess+     DP.say $+       "The result is " ++ show a+  +main :: IO ()+main = do+  backend <- initializeBackend "localhost" "8080" initRemoteTable+  startMaster backend (master backend)