aivika-gpss (empty) → 0.3
raw patch · 94 files changed
+10071/−0 lines, 94 filesdep +aivikadep +aivika-transformersdep +basesetup-changed
Dependencies added: aivika, aivika-transformers, base, containers, hashable, mtl, unordered-containers
Files
- LICENSE +30/−0
- Setup.lhs +3/−0
- Simulation/Aivika/GPSS.hs +76/−0
- Simulation/Aivika/GPSS/AssemblySet.hs +165/−0
- Simulation/Aivika/GPSS/AssemblySet.hs-boot +18/−0
- Simulation/Aivika/GPSS/Block.hs +53/−0
- Simulation/Aivika/GPSS/Block/Advance.hs +25/−0
- Simulation/Aivika/GPSS/Block/Assemble.hs +27/−0
- Simulation/Aivika/GPSS/Block/Assign.hs +36/−0
- Simulation/Aivika/GPSS/Block/Depart.hs +29/−0
- Simulation/Aivika/GPSS/Block/Enter.hs +29/−0
- Simulation/Aivika/GPSS/Block/Gather.hs +27/−0
- Simulation/Aivika/GPSS/Block/Generate.hs +51/−0
- Simulation/Aivika/GPSS/Block/Leave.hs +29/−0
- Simulation/Aivika/GPSS/Block/Link.hs +38/−0
- Simulation/Aivika/GPSS/Block/Loop.hs +53/−0
- Simulation/Aivika/GPSS/Block/Match.hs +27/−0
- Simulation/Aivika/GPSS/Block/Preempt.hs +79/−0
- Simulation/Aivika/GPSS/Block/Priority.hs +26/−0
- Simulation/Aivika/GPSS/Block/Queue.hs +29/−0
- Simulation/Aivika/GPSS/Block/Release.hs +27/−0
- Simulation/Aivika/GPSS/Block/Return.hs +27/−0
- Simulation/Aivika/GPSS/Block/Seize.hs +27/−0
- Simulation/Aivika/GPSS/Block/Split.hs +52/−0
- Simulation/Aivika/GPSS/Block/Terminate.hs +69/−0
- Simulation/Aivika/GPSS/Block/Test.hs +93/−0
- Simulation/Aivika/GPSS/Block/Transfer.hs +25/−0
- Simulation/Aivika/GPSS/Block/Unlink.hs +35/−0
- Simulation/Aivika/GPSS/Facility.hs +702/−0
- Simulation/Aivika/GPSS/MatchChain.hs +98/−0
- Simulation/Aivika/GPSS/Queue.hs +336/−0
- Simulation/Aivika/GPSS/Queue.hs-boot +27/−0
- Simulation/Aivika/GPSS/Results.hs +175/−0
- Simulation/Aivika/GPSS/Results/Locale.hs +316/−0
- Simulation/Aivika/GPSS/Results/Transform.hs +214/−0
- Simulation/Aivika/GPSS/Storage.hs +514/−0
- Simulation/Aivika/GPSS/Transact.hs +302/−0
- Simulation/Aivika/GPSS/TransactQueueStrategy.hs +166/−0
- Simulation/Aivika/Trans/GPSS.hs +76/−0
- Simulation/Aivika/Trans/GPSS/AssemblySet.hs +171/−0
- Simulation/Aivika/Trans/GPSS/AssemblySet.hs-boot +27/−0
- Simulation/Aivika/Trans/GPSS/Block.hs +58/−0
- Simulation/Aivika/Trans/GPSS/Block/Advance.hs +27/−0
- Simulation/Aivika/Trans/GPSS/Block/Assemble.hs +29/−0
- Simulation/Aivika/Trans/GPSS/Block/Assign.hs +40/−0
- Simulation/Aivika/Trans/GPSS/Block/Depart.hs +31/−0
- Simulation/Aivika/Trans/GPSS/Block/Enter.hs +31/−0
- Simulation/Aivika/Trans/GPSS/Block/Gather.hs +29/−0
- Simulation/Aivika/Trans/GPSS/Block/Generate.hs +57/−0
- Simulation/Aivika/Trans/GPSS/Block/Leave.hs +31/−0
- Simulation/Aivika/Trans/GPSS/Block/Link.hs +40/−0
- Simulation/Aivika/Trans/GPSS/Block/Loop.hs +57/−0
- Simulation/Aivika/Trans/GPSS/Block/Match.hs +29/−0
- Simulation/Aivika/Trans/GPSS/Block/Preempt.hs +84/−0
- Simulation/Aivika/Trans/GPSS/Block/Priority.hs +28/−0
- Simulation/Aivika/Trans/GPSS/Block/Queue.hs +31/−0
- Simulation/Aivika/Trans/GPSS/Block/Release.hs +29/−0
- Simulation/Aivika/Trans/GPSS/Block/Return.hs +29/−0
- Simulation/Aivika/Trans/GPSS/Block/Seize.hs +29/−0
- Simulation/Aivika/Trans/GPSS/Block/Split.hs +54/−0
- Simulation/Aivika/Trans/GPSS/Block/Terminate.hs +74/−0
- Simulation/Aivika/Trans/GPSS/Block/Test.hs +101/−0
- Simulation/Aivika/Trans/GPSS/Block/Transfer.hs +27/−0
- Simulation/Aivika/Trans/GPSS/Block/Unlink.hs +37/−0
- Simulation/Aivika/Trans/GPSS/Facility.hs +760/−0
- Simulation/Aivika/Trans/GPSS/MatchChain.hs +102/−0
- Simulation/Aivika/Trans/GPSS/Queue.hs +376/−0
- Simulation/Aivika/Trans/GPSS/Queue.hs-boot +43/−0
- Simulation/Aivika/Trans/GPSS/Results.hs +183/−0
- Simulation/Aivika/Trans/GPSS/Results/Locale.hs +316/−0
- Simulation/Aivika/Trans/GPSS/Results/Transform.hs +216/−0
- Simulation/Aivika/Trans/GPSS/Storage.hs +570/−0
- Simulation/Aivika/Trans/GPSS/Transact.hs +317/−0
- Simulation/Aivika/Trans/GPSS/TransactQueueStrategy.hs +165/−0
- aivika-gpss.cabal +158/−0
- examples/Example2A.hs +57/−0
- examples/Example2ATrans.hs +61/−0
- examples/Example2B.hs +80/−0
- examples/Example2BTrans.hs +84/−0
- examples/Example2C.hs +78/−0
- examples/Example2CTrans.hs +82/−0
- examples/Example2D.hs +55/−0
- examples/Example2DTrans.hs +59/−0
- examples/Example2E.hs +65/−0
- examples/Example2ETrans.hs +69/−0
- examples/Example7-26.hs +99/−0
- examples/Example7-26Distributed.hs +130/−0
- examples/Example7-26Trans.hs +103/−0
- examples/Example7-28.hs +75/−0
- examples/Example7-28Trans.hs +79/−0
- examples/Example7-31.hs +87/−0
- examples/Example7-31Trans.hs +91/−0
- examples/Example7-35.hs +98/−0
- examples/Example7-35Trans.hs +102/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2017 David Sorokin <david.sorokin@gmail.com>++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of his contributors+ may be used to endorse or promote products derived from this software+ without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ Simulation/Aivika/GPSS.hs view
@@ -0,0 +1,76 @@++-- |+-- Module : Simulation.Aivika.GPSS+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module re-exports the library functionality related to the GPSS-like domain specific language.+--+module Simulation.Aivika.GPSS+ (-- * Modules+ module Simulation.Aivika.GPSS.Block,+ module Simulation.Aivika.GPSS.Block.Advance,+ module Simulation.Aivika.GPSS.Block.Assemble,+ module Simulation.Aivika.GPSS.Block.Assign,+ module Simulation.Aivika.GPSS.Block.Depart,+ module Simulation.Aivika.GPSS.Block.Enter,+ module Simulation.Aivika.GPSS.Block.Gather,+ module Simulation.Aivika.GPSS.Block.Generate,+ module Simulation.Aivika.GPSS.Block.Leave,+ module Simulation.Aivika.GPSS.Block.Link,+ module Simulation.Aivika.GPSS.Block.Loop,+ module Simulation.Aivika.GPSS.Block.Match,+ module Simulation.Aivika.GPSS.Block.Preempt,+ module Simulation.Aivika.GPSS.Block.Priority,+ module Simulation.Aivika.GPSS.Block.Queue,+ module Simulation.Aivika.GPSS.Block.Release,+ module Simulation.Aivika.GPSS.Block.Return,+ module Simulation.Aivika.GPSS.Block.Seize,+ module Simulation.Aivika.GPSS.Block.Split,+ module Simulation.Aivika.GPSS.Block.Terminate,+ module Simulation.Aivika.GPSS.Block.Test,+ module Simulation.Aivika.GPSS.Block.Transfer,+ module Simulation.Aivika.GPSS.Block.Unlink,+ module Simulation.Aivika.GPSS.AssemblySet,+ module Simulation.Aivika.GPSS.Facility,+ module Simulation.Aivika.GPSS.MatchChain,+ module Simulation.Aivika.GPSS.Results,+ module Simulation.Aivika.GPSS.Results.Locale,+ module Simulation.Aivika.GPSS.Storage,+ module Simulation.Aivika.GPSS.Transact,+ module Simulation.Aivika.GPSS.TransactQueueStrategy) where++import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.Block.Advance+import Simulation.Aivika.GPSS.Block.Assemble+import Simulation.Aivika.GPSS.Block.Assign+import Simulation.Aivika.GPSS.Block.Depart+import Simulation.Aivika.GPSS.Block.Enter+import Simulation.Aivika.GPSS.Block.Gather+import Simulation.Aivika.GPSS.Block.Generate+import Simulation.Aivika.GPSS.Block.Leave+import Simulation.Aivika.GPSS.Block.Link+import Simulation.Aivika.GPSS.Block.Loop+import Simulation.Aivika.GPSS.Block.Match+import Simulation.Aivika.GPSS.Block.Preempt+import Simulation.Aivika.GPSS.Block.Priority+import Simulation.Aivika.GPSS.Block.Queue+import Simulation.Aivika.GPSS.Block.Release+import Simulation.Aivika.GPSS.Block.Return+import Simulation.Aivika.GPSS.Block.Seize+import Simulation.Aivika.GPSS.Block.Split+import Simulation.Aivika.GPSS.Block.Terminate+import Simulation.Aivika.GPSS.Block.Test+import Simulation.Aivika.GPSS.Block.Transfer+import Simulation.Aivika.GPSS.Block.Unlink+import Simulation.Aivika.GPSS.AssemblySet+import Simulation.Aivika.GPSS.Facility+import Simulation.Aivika.GPSS.MatchChain+import Simulation.Aivika.GPSS.Results+import Simulation.Aivika.GPSS.Results.Locale+import Simulation.Aivika.GPSS.Storage+import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.TransactQueueStrategy
+ Simulation/Aivika/GPSS/AssemblySet.hs view
@@ -0,0 +1,165 @@++-- |+-- Module : Simulation.Aivika.GPSS.AssemblySet+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines a GPSS assembly set.+--+module Simulation.Aivika.GPSS.AssemblySet+ (-- * Types+ AssemblySet,+ -- * Creating Assembly Set+ newAssemblySet,+ -- * Functions+ assembleTransact,+ gatherTransacts,+ -- * Properties+ transactAssembling,+ transactGathering) where++import Data.IORef+import Data.Monoid+import Data.Maybe+import Data.Hashable++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika+import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Parameter+import Simulation.Aivika.Internal.Simulation++import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.TransactQueueStrategy++-- | Represents an assembly set.+data AssemblySet =+ AssemblySet { assemblySetSequenceNo :: Int,+ assemblySetAssemblingTransact :: IORef (Maybe ProcessId),+ assemblySetAssemblingCounter :: IORef Int,+ assemblySetGatheringTransacts :: StrategyQueue (TransactQueueStrategy FCFS) ProcessId,+ assemblySetGatheringCounter :: IORef Int+ }++instance Eq AssemblySet where+ x == y = (assemblySetAssemblingTransact x) == (assemblySetAssemblingTransact y)++instance Hashable AssemblySet where+ hashWithSalt salt x = hashWithSalt salt (assemblySetSequenceNo x)++-- | Create a new assembly set.+newAssemblySet :: Simulation AssemblySet+newAssemblySet =+ Simulation $ \r ->+ do let g = runGenerator r+ sequenceNo <- generateSequenceNo g+ assemblingTransact <- newIORef Nothing+ assemblingCounter <- newIORef 0+ gatheringTransacts <- invokeSimulation r $ newStrategyQueue (TransactQueueStrategy FCFS)+ gatheringCounter <- newIORef 0+ return AssemblySet { assemblySetSequenceNo = sequenceNo,+ assemblySetAssemblingTransact = assemblingTransact,+ assemblySetAssemblingCounter = assemblingCounter,+ assemblySetGatheringTransacts = gatheringTransacts,+ assemblySetGatheringCounter = gatheringCounter+ }++-- | Assemble the transact by the specified number.+assembleTransact :: Transact a -> Int -> Process ()+assembleTransact t n =+ do (s, a) <-+ liftEvent $+ do s <- transactAssemblySet t+ a <- liftIO $ readIORef (assemblySetAssemblingCounter s)+ return (s, a)+ if a == 0+ then do let n' = n - 1+ when (n' < 0) $+ throwProcess $+ SimulationRetry+ "The number of transacts must be positive: assembleTransact"+ if n' == 0+ then return ()+ else do liftEvent $+ do pid <- requireTransactProcessId t+ liftIO $ writeIORef (assemblySetAssemblingTransact s) (Just pid)+ liftIO $ writeIORef (assemblySetAssemblingCounter s) $! n'+ passivateProcess+ else do let a' = a - 1+ if a' == 0+ then do liftEvent $+ do Just pid <- liftIO $ readIORef (assemblySetAssemblingTransact s)+ liftIO $ writeIORef (assemblySetAssemblingTransact s) Nothing+ liftIO $ writeIORef (assemblySetAssemblingCounter s) $! a'+ reactivateProcessImmediately pid+ cancelProcess+ else do liftIO $ writeIORef (assemblySetAssemblingCounter s) $! a'+ cancelProcess++-- | Gather the transacts by the specified number.+gatherTransacts :: Transact a -> Int -> Process ()+gatherTransacts t n =+ do (s, a) <-+ liftEvent $+ do s <- transactAssemblySet t+ a <- liftIO $ readIORef (assemblySetGatheringCounter s)+ return (s, a)+ if a == 0+ then do let n' = n - 1+ when (n' < 0) $+ throwProcess $+ SimulationRetry+ "The number of transacts must be positive: gatherTransacts"+ if n' == 0+ then return ()+ else do liftEvent $+ do pid <- requireTransactProcessId t+ strategyEnqueueWithPriority+ (assemblySetGatheringTransacts s)+ (transactPriority t)+ pid+ liftIO $ writeIORef (assemblySetGatheringCounter s) $! n'+ passivateProcess+ else do let a' = a - 1+ liftEvent $+ do pid <- requireTransactProcessId t+ strategyEnqueueWithPriority+ (assemblySetGatheringTransacts s)+ (transactPriority t)+ pid+ liftIO $ writeIORef (assemblySetGatheringCounter s) $! a'+ if a' == 0+ then passivateProcessBefore $+ liftEvent $+ do let loop acc =+ do f <- strategyQueueNull (assemblySetGatheringTransacts s)+ if f+ then return (reverse acc)+ else do x <- strategyDequeue (assemblySetGatheringTransacts s)+ loop (x: acc)+ act [] = return ()+ act (pid: pids') =+ do reactivateProcessImmediately pid+ yieldEvent $ act pids'+ pids <- loop []+ act pids+ else passivateProcess++-- | Test whether another transact is assembled for the corresponding assembly set.+transactAssembling :: Transact a -> Event Bool+transactAssembling t =+ do s <- transactAssemblySet t+ a <- liftIO $ readIORef (assemblySetAssemblingCounter s)+ return (a > 0)++-- | Test whether the transacts are gathered for the corresponding assembly set.+transactGathering :: Transact a -> Event Bool+transactGathering t =+ do s <- transactAssemblySet t+ a <- liftIO $ readIORef (assemblySetGatheringCounter s)+ return (a > 0)
+ Simulation/Aivika/GPSS/AssemblySet.hs-boot view
@@ -0,0 +1,18 @@++-- |+-- Module : Simulation.Aivika.GPSS.AssemblySet+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This is an hs-boot file.+--+module Simulation.Aivika.GPSS.AssemblySet where++import Simulation.Aivika++data AssemblySet++newAssemblySet :: Simulation AssemblySet
+ Simulation/Aivika/GPSS/Block.hs view
@@ -0,0 +1,53 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines a GPSS block.+--+module Simulation.Aivika.GPSS.Block+ (Block(..),+ GeneratorBlock(..),+ withinBlock,+ traceBlock) where++import Control.Monad+import Control.Monad.Trans+import qualified Control.Category as C++import Simulation.Aivika++-- | Represents a GPSS block.+data Block a b =+ Block { blockProcess :: a -> Process b+ -- ^ Process the item.+ }++-- | Represents a GPSS generator block.+newtype GeneratorBlock a =+ GeneratorBlock { runGeneratorBlock :: Block a () -> Process ()+ -- ^ Run the generator block.+ }++instance C.Category Block where++ id = Block { blockProcess = return }++ x . y = Block { blockProcess = \a -> do { b <- blockProcess y a; blockProcess x b } }++-- | Perform some action within the block, for example,+-- opening or inverting the 'Gate' to emulate the LOGIC block.+withinBlock :: Process ()+ -- ^ the action to be executed for each transact+ -> Block a a+withinBlock m =+ Block { blockProcess = \a -> m >> return a }++-- | Trace the specified block.+traceBlock :: String -> Block a b -> Block a b+traceBlock message x =+ Block { blockProcess = \a -> traceProcess message (blockProcess x a) }
+ Simulation/Aivika/GPSS/Block/Advance.hs view
@@ -0,0 +1,25 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Advance+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block ADVANCE.+--+module Simulation.Aivika.GPSS.Block.Advance+ (advanceBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block++-- | This is the GPSS construct+--+-- @ADVANCE A,B@+advanceBlock :: Process ()+ -- ^ the delay+ -> Block a a+advanceBlock p =+ Block { blockProcess = \a -> p >> return a }
+ Simulation/Aivika/GPSS/Block/Assemble.hs view
@@ -0,0 +1,27 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Assemble+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block ASSEMBLE.+--+module Simulation.Aivika.GPSS.Block.Assemble+ (assembleBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.AssemblySet+import Simulation.Aivika.GPSS.Transact++-- | This is the GPSS construct+--+-- @ASSEMBLE A@+assembleBlock :: Int+ -- ^ the number of transacts to assemble+ -> Block (Transact a) (Transact a)+assembleBlock n =+ Block { blockProcess = \a -> assembleTransact a n >> return a }
+ Simulation/Aivika/GPSS/Block/Assign.hs view
@@ -0,0 +1,36 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Assign+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block ASSIGN.+--+module Simulation.Aivika.GPSS.Block.Assign+ (assignBlock,+ assignBlockM) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.Transact++-- | This is the GPSS construct+--+-- @ASSIGN A,B,C@+assignBlock :: (a -> b)+ -- ^ the transform+ -> Block (Transact a) (Transact b)+assignBlock f =+ Block { blockProcess = \a -> return (assignTransactValue a f) }++-- | This is the GPSS construct+--+-- @ASSIGN A,B,C@+assignBlockM :: (a -> Process b)+ -- ^ the transform computation+ -> Block (Transact a) (Transact b)+assignBlockM f =+ Block { blockProcess = \a -> assignTransactValueM a f }
+ Simulation/Aivika/GPSS/Block/Depart.hs view
@@ -0,0 +1,29 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Depart+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block DEPART.+--+module Simulation.Aivika.GPSS.Block.Depart+ (departBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.Block+import qualified Simulation.Aivika.GPSS.Queue as Q++-- | This is the GPSS construct+--+-- @DEPART A,B@+departBlock :: Q.Queue+ -- ^ the queue+ -> Int+ -- ^ the content decrement+ -> Block (Transact a) (Transact a)+departBlock q decrement =+ Block { blockProcess = \a -> (liftEvent $ Q.dequeue q a decrement) >> return a }
+ Simulation/Aivika/GPSS/Block/Enter.hs view
@@ -0,0 +1,29 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Enter+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block ENTER.+--+module Simulation.Aivika.GPSS.Block.Enter+ (enterBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.Storage++-- | This is the GPSS construct+--+-- @ENTER A,B@+enterBlock :: Storage+ -- ^ the storage+ -> Int+ -- ^ the content decrement+ -> Block (Transact a) (Transact a)+enterBlock r decrement =+ Block { blockProcess = \a -> enterStorage r a decrement >> return a }
+ Simulation/Aivika/GPSS/Block/Gather.hs view
@@ -0,0 +1,27 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Gather+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block GATHER.+--+module Simulation.Aivika.GPSS.Block.Gather+ (gatherBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.AssemblySet+import Simulation.Aivika.GPSS.Transact++-- | This is the GPSS construct+--+-- @GATHER A@+gatherBlock :: Int+ -- ^ the number of transacts to gather+ -> Block (Transact a) (Transact a)+gatherBlock n =+ Block { blockProcess = \a -> gatherTransacts a n >> return a }
+ Simulation/Aivika/GPSS/Block/Generate.hs view
@@ -0,0 +1,51 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Generate+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block GENERATE.+--+module Simulation.Aivika.GPSS.Block.Generate+ (streamGeneratorBlock0,+ streamGeneratorBlock,+ streamGeneratorBlockM) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.Transact++-- | Return a generator block by the specified stream and priority computation.+streamGeneratorBlockM :: Stream (Arrival a)+ -- ^ the input stream of data+ -> Event Int+ -- ^ the transact priority+ -> GeneratorBlock (Transact a)+streamGeneratorBlockM s priority =+ let loop s block =+ do (a, xs) <- runStream s+ liftEvent $+ do p <- priority+ t <- liftSimulation $ newTransact a p+ runProcess $+ do takeTransact t+ blockProcess block t+ loop xs block+ in GeneratorBlock (loop s)++-- | Return a generator block by the specified stream and priority.+streamGeneratorBlock :: Stream (Arrival a)+ -- ^ the input stream of data+ -> Int+ -- ^ the transact priority+ -> GeneratorBlock (Transact a)+streamGeneratorBlock s = streamGeneratorBlockM s . return++-- | Return a generator block by the specified stream using zero priority.+streamGeneratorBlock0 :: Stream (Arrival a)+ -- ^ the input stream of data+ -> GeneratorBlock (Transact a)+streamGeneratorBlock0 s = streamGeneratorBlock s 0
+ Simulation/Aivika/GPSS/Block/Leave.hs view
@@ -0,0 +1,29 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Leave+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block LEAVE.+--+module Simulation.Aivika.GPSS.Block.Leave+ (leaveBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.Storage++-- | This is the GPSS construct+--+-- @LEAVE A,B@+leaveBlock :: Storage+ -- ^ the storage+ -> Int+ -- ^ the content increment+ -> Block (Transact a) (Transact a)+leaveBlock r increment =+ Block { blockProcess = \a -> leaveStorage r increment >> return a }
+ Simulation/Aivika/GPSS/Block/Link.hs view
@@ -0,0 +1,38 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Link+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines an analog of the GPSS block LINK.+--+module Simulation.Aivika.GPSS.Block.Link+ (linkBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block++-- | This is an analog of the GPSS construct+--+-- @LINK A,B,C@+linkBlock :: (a -> Process (Either (Block a ()) Bool))+ -- ^ try to link the transact and return either the next block to transfer+ -- or a flag indicating whether the transact process should be passivated+ -- in case of successful linking, i.e. storing in the queue+ -> Block a a+linkBlock f =+ Block { blockProcess = \a ->+ do x <- f a+ case x of+ Left transfer ->+ transferProcess $+ blockProcess transfer a+ Right False ->+ return a+ Right True ->+ do passivateProcess+ return a+ }
+ Simulation/Aivika/GPSS/Block/Loop.hs view
@@ -0,0 +1,53 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Loop+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block LOOP.+--+module Simulation.Aivika.GPSS.Block.Loop+ (loopBlock,+ loopBlockM) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block++-- | This is the GPSS construct+--+-- @LOOP A,B@+loopBlock :: (a -> (b, Bool))+ -- ^ by the specified transact return the next version+ -- of the same transact and a condition whether we should+ -- exit the loop+ -> Block b ()+ -- ^ the block to transfer in when the condition fails+ -> Block a b+loopBlock f block =+ Block { blockProcess = \a ->+ do let (b, c) = f a+ if c+ then return b+ else transferProcess (blockProcess block b)+ }++-- | This is the GPSS construct+--+-- @LOOP A,B@+loopBlockM :: (a -> Process (b, Bool))+ -- ^ by the specified transact return the next version+ -- of the same transact and a condition whether we should+ -- exit the loop+ -> Block b ()+ -- ^ the block to transfer in when the condition fails+ -> Block a b+loopBlockM f block =+ Block { blockProcess = \a ->+ do (b, c) <- f a+ if c+ then return b+ else transferProcess (blockProcess block b)+ }
+ Simulation/Aivika/GPSS/Block/Match.hs view
@@ -0,0 +1,27 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Match+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block MATCH.+--+module Simulation.Aivika.GPSS.Block.Match+ (matchBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.MatchChain+import Simulation.Aivika.GPSS.Transact++-- | This is an analog of the GPSS construct+--+-- @MATCH A@+matchBlock :: MatchChain+ -- ^ the corresponding match chain+ -> Block (Transact a) (Transact a)+matchBlock chain =+ Block { blockProcess = \a -> matchTransact chain a >> return a }
+ Simulation/Aivika/GPSS/Block/Preempt.hs view
@@ -0,0 +1,79 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Preempt+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block Preempt.+--+module Simulation.Aivika.GPSS.Block.Preempt+ (preemptBlock,+ PreemptBlockMode(..),+ defaultPreemptBlockMode,+ toFacilityPreemptMode,+ fromFacilityPreemptMode) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.Facility++-- | Specifies the Preempt block mode.+data PreemptBlockMode a =+ PreemptBlockMode { preemptBlockPriorityMode :: Bool,+ -- ^ the Priority mode; otherwise, the Interrupt mode+ preemptBlockTransfer :: Maybe (Maybe Double -> Block (Transact a) ()),+ -- ^ where to transfer the preempted transact,+ -- passing in the remaining time from the process holding + -- computation such as the ADVANCE block+ preemptBlockRemoveMode :: Bool+ -- ^ the Remove mode+ }++-- | Convert 'PreemptBlockMode' to 'FacilityPreemptMode'.+toFacilityPreemptMode :: PreemptBlockMode a -> FacilityPreemptMode a+toFacilityPreemptMode m =+ FacilityPreemptMode { facilityPriorityMode = preemptBlockPriorityMode m,+ facilityTransfer = transfer,+ facilityRemoveMode = preemptBlockRemoveMode m+ }+ where+ transfer =+ case preemptBlockTransfer m of+ Nothing -> Nothing+ Just f -> Just (\a dt -> blockProcess (f dt) a)++-- | Convert 'PreemptBlockMode' from 'FacilityPreemptMode'.+fromFacilityPreemptMode :: FacilityPreemptMode a -> PreemptBlockMode a+fromFacilityPreemptMode m =+ PreemptBlockMode { preemptBlockPriorityMode = facilityPriorityMode m,+ preemptBlockTransfer = transfer,+ preemptBlockRemoveMode = facilityRemoveMode m+ }+ where+ transfer =+ case facilityTransfer m of+ Nothing -> Nothing+ Just f -> Just (\dt -> Block $ \a -> f a dt)++-- | The default Preempt block mode.+defaultPreemptBlockMode :: PreemptBlockMode a+defaultPreemptBlockMode =+ PreemptBlockMode { preemptBlockPriorityMode = False,+ preemptBlockTransfer = Nothing,+ preemptBlockRemoveMode = False+ }++-- | This is the GPSS construct+--+-- @PREEMPT A,B,C,D,E@+preemptBlock :: Facility a+ -- ^ the facility+ -> PreemptBlockMode a+ -- ^ the Preempt block mode+ -> Block (Transact a) (Transact a)+preemptBlock r m =+ Block { blockProcess = \a -> preemptFacility r a (toFacilityPreemptMode m) >> return a }
+ Simulation/Aivika/GPSS/Block/Priority.hs view
@@ -0,0 +1,26 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Priority+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block PRIORITY.+--+module Simulation.Aivika.GPSS.Block.Priority+ (priorityBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.Transact++-- | This is the GPSS construct+--+-- @PRIORITY A@+priorityBlock :: Int+ -- ^ the priority+ -> Block (Transact a) (Transact a)+priorityBlock priority =+ Block { blockProcess = \a -> return (assignTransactPriority a priority) }
+ Simulation/Aivika/GPSS/Block/Queue.hs view
@@ -0,0 +1,29 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Queue+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block QUEUE.+--+module Simulation.Aivika.GPSS.Block.Queue+ (queueBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.Block+import qualified Simulation.Aivika.GPSS.Queue as Q++-- | This is the GPSS construct+--+-- @QUEUE A,B@+queueBlock :: Q.Queue+ -- ^ the queue+ -> Int+ -- ^ the content increment+ -> Block (Transact a) (Transact a)+queueBlock q increment =+ Block { blockProcess = \a -> (liftEvent $ Q.enqueue q a increment) >> return a }
+ Simulation/Aivika/GPSS/Block/Release.hs view
@@ -0,0 +1,27 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Release+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block RELEASE.+--+module Simulation.Aivika.GPSS.Block.Release+ (releaseBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.Facility++-- | This is the GPSS construct+--+-- @RELEASE A@+releaseBlock :: Facility a+ -- ^ the facility+ -> Block (Transact a) (Transact a)+releaseBlock r =+ Block { blockProcess = \a -> releaseFacility r a >> return a }
+ Simulation/Aivika/GPSS/Block/Return.hs view
@@ -0,0 +1,27 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Return+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block RETURN.+--+module Simulation.Aivika.GPSS.Block.Return+ (returnBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.Facility++-- | This is the GPSS construct+--+-- @RETURN A@+returnBlock :: Facility a+ -- ^ the facility+ -> Block (Transact a) (Transact a)+returnBlock r =+ Block { blockProcess = \a -> returnFacility r a >> return a }
+ Simulation/Aivika/GPSS/Block/Seize.hs view
@@ -0,0 +1,27 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Seize+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block SEIZE.+--+module Simulation.Aivika.GPSS.Block.Seize+ (seizeBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.Facility++-- | This is the GPSS construct+--+-- @SEIZE A@+seizeBlock :: Facility a+ -- ^ the facility+ -> Block (Transact a) (Transact a)+seizeBlock r =+ Block { blockProcess = \a -> seizeFacility r a >> return a }
+ Simulation/Aivika/GPSS/Block/Split.hs view
@@ -0,0 +1,52 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Split+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines an analog of the GPSS block SPLIT.+--+module Simulation.Aivika.GPSS.Block.Split+ (splitBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.Transact++-- | This is an analog of the GPSS construct+--+-- @SPLIT A,B,C@+--+-- Parameter @A@ is a length of the list parameter passed in to the function.+-- Parameter @B@ is the list itself. If you need to define parameter @C@ then+-- you can create the blocks dynamically that could depend on the index and+-- where we could assign a new value for each new transcact after splitting.+--+-- An example is+--+-- @+-- let blocks :: [Block (Transact (a, Int)) ()]+-- blocks = ...+-- f :: (Int, Block (Transact (a, Int)) ()) -> Block (Transact a) ()+-- f (n, block) = assignBlock (\a -> (a, n)) >>> block+-- blocks' :: [Block (Transact a) ()]+-- blocks' = map f $ zip [0..] blocks+-- in splitBlock blocks'+-- @+splitBlock :: [Block (Transact a) ()]+ -- ^ split and transfer new transacts to the specified blocks+ -> Block (Transact a) (Transact a)+splitBlock blocks =+ Block { blockProcess = \a ->+ do let loop [] = return ()+ loop (transfer: transfers) =+ do a' <- liftSimulation $ splitTransact a+ transferTransact a' $+ blockProcess transfer a'+ loop transfers+ liftEvent $ loop blocks+ return a+ }
+ Simulation/Aivika/GPSS/Block/Terminate.hs view
@@ -0,0 +1,69 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Terminate+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block TERMINATE.+--+module Simulation.Aivika.GPSS.Block.Terminate+ (terminateBlock,+ terminateBlockByCount,+ terminateBlockByCountM) where++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block++-- | This is the GPSS construct+--+-- @TERMINATE@+terminateBlock :: Block a ()+terminateBlock =+ Block { blockProcess = \a -> return () }++-- | This is the GPSS construct+--+-- @TERMINATE Count@+terminateBlockByCountM :: Ref Int+ -- ^ the counter+ -> Event Int+ -- ^ the computation of decrement+ -> Block a ()+terminateBlockByCountM counter decrement =+ Block { blockProcess = \a -> action }+ where+ action = + liftEvent $+ do i <- decrement+ n <- readRef counter+ let n' = n - i+ n' `seq` writeRef counter n'+ when (n' <= 0) $+ throwEvent $+ SimulationAbort "Terminated by exceeding the counter"++-- | This is the GPSS construct+--+-- @TERMINATE Count@+terminateBlockByCount :: Ref Int+ -- ^ the counter+ -> Int+ -- ^ the decrement+ -> Block a ()+terminateBlockByCount counter i =+ Block { blockProcess = \a -> action }+ where+ action = + liftEvent $+ do n <- readRef counter+ let n' = n - i+ n' `seq` writeRef counter n'+ when (n' <= 0) $+ throwEvent $+ SimulationAbort "Terminated by exceeding the counter"
+ Simulation/Aivika/GPSS/Block/Test.hs view
@@ -0,0 +1,93 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Test+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block TEST.+--+module Simulation.Aivika.GPSS.Block.Test+ (awaitingTestBlock,+ awaitingTestBlockM,+ transferringTestBlock,+ transferringTestBlockM) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block++-- | This is the GPSS construct+--+-- @TEST O A,B@+awaitingTestBlock :: (a -> Signalable Bool)+ -- ^ by the specified transact return+ -- a test condition and signal that notifies+ -- about changing the condition+ -> Block a a+awaitingTestBlock f =+ Block { blockProcess = \a ->+ do let s = f a+ loop =+ do f <- liftEvent $ readSignalable s+ if f+ then return ()+ else do processAwait $ signalableChanged_ s+ loop+ loop+ return a+ }++-- | This is the GPSS construct+--+-- @TEST O A,B@+awaitingTestBlockM :: (a -> Process (Signalable Bool))+ -- ^ by the specified transact return+ -- a test condition and signal that notifies+ -- about changing the condition+ -> Block a a+awaitingTestBlockM f =+ Block { blockProcess = \a ->+ do s <- f a+ let loop =+ do f <- liftEvent $ readSignalable s+ if f+ then return ()+ else do processAwait $ signalableChanged_ s+ loop+ loop+ return a+ }++-- | This is the GPSS construct+--+-- @TEST O A,B,C@+transferringTestBlock :: (a -> Bool)+ -- ^ the predicate+ -> Block a ()+ -- ^ the block to transfer in when the condition fails+ -> Block a a+transferringTestBlock pred block =+ Block { blockProcess = \a ->+ do let f = pred a+ if f+ then return a+ else transferProcess (blockProcess block a)+ }++-- | This is the GPSS construct+--+-- @TEST O A,B,C@+transferringTestBlockM :: (a -> Process Bool)+ -- ^ the predicate+ -> Block a ()+ -- ^ the block to transfer in when the condition fails+ -> Block a a+transferringTestBlockM pred block =+ Block { blockProcess = \a ->+ do f <- pred a+ if f+ then return a+ else transferProcess (blockProcess block a)+ }
+ Simulation/Aivika/GPSS/Block/Transfer.hs view
@@ -0,0 +1,25 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Transfer+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block TRANSFER.+--+module Simulation.Aivika.GPSS.Block.Transfer+ (transferBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block++-- | This is the GPSS construct+--+-- @TRANSFER ,New_Place@+transferBlock :: Block a ()+ -- ^ a new place+ -> Block a b+transferBlock x =+ Block { blockProcess = \a -> transferProcess (blockProcess x a) }
+ Simulation/Aivika/GPSS/Block/Unlink.hs view
@@ -0,0 +1,35 @@++-- |+-- Module : Simulation.Aivika.GPSS.Block.Unlink+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines an analog of the GPSS block UNLINK.+--+module Simulation.Aivika.GPSS.Block.Unlink+ (unlinkBlock) where++import Simulation.Aivika+import Simulation.Aivika.GPSS.Block+import Simulation.Aivika.GPSS.Transact++-- | This is an analog of the GPSS construct+--+-- @UNLINK O A,B,C,D,E,F@+unlinkBlock :: Process [(Transact a, Maybe (Block (Transact a) ()))]+ -- ^ a computation of the list of transacts to reactivate,+ -- transfering them to the specified blocks if required+ -> Block b b+unlinkBlock m =+ Block { blockProcess = \b ->+ do let f (a, Nothing) = (a, Nothing)+ f (a, Just transfer) = (a, Just $ blockProcess transfer a)+ xs <- m+ liftEvent $+ reactivateTransacts $+ map f xs+ return b+ }
+ Simulation/Aivika/GPSS/Facility.hs view
@@ -0,0 +1,702 @@++-- |+-- Module : Simulation.Aivika.GPSS.Facility+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.1+--+-- This module defines the GPSS Facility entity.+--+module Simulation.Aivika.GPSS.Facility+ (-- * Facility Type+ Facility,+ FacilityPreemptMode(..),+ FacilityPreemptTransfer,+ -- * Creating Facility+ newFacility,+ -- * Facility Properties+ facilityCount,+ facilityCountStats,+ facilityCaptureCount,+ facilityUtilisationCount,+ facilityUtilisationCountStats,+ facilityQueueCount,+ facilityQueueCountStats,+ facilityTotalWaitTime,+ facilityWaitTime,+ facilityTotalHoldingTime,+ facilityHoldingTime,+ facilityInterrupted,+ -- * Seizing-Releasing and Preempting-Returning Facility+ seizeFacility,+ releaseFacility,+ preemptFacility,+ returnFacility,+ -- * Statistics Reset+ resetFacility,+ -- * Signals+ facilityCountChanged,+ facilityCountChanged_,+ facilityCaptureCountChanged,+ facilityCaptureCountChanged_,+ facilityUtilisationCountChanged,+ facilityUtilisationCountChanged_,+ facilityQueueCountChanged,+ facilityQueueCountChanged_,+ facilityWaitTimeChanged,+ facilityWaitTimeChanged_,+ facilityHoldingTimeChanged,+ facilityHoldingTimeChanged_,+ facilityChanged_) where++import Data.IORef+import Data.Monoid+import Data.Maybe++import Control.Monad+import Control.Monad.Trans+import Control.Exception++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Cont+import Simulation.Aivika.Internal.Process+import Simulation.Aivika.QueueStrategy+import Simulation.Aivika.Statistics+import Simulation.Aivika.Signal++import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.TransactQueueStrategy++-- | Represents a GPSS Facility entity.+data Facility a = + Facility { facilityCountRef :: IORef Int,+ facilityCountStatsRef :: IORef (TimingStats Int),+ facilityCountSource :: SignalSource Int,+ facilityCaptureCountRef :: IORef Int,+ facilityCaptureCountSource :: SignalSource Int,+ facilityUtilisationCountRef :: IORef Int,+ facilityUtilisationCountStatsRef :: IORef (TimingStats Int),+ facilityUtilisationCountSource :: SignalSource Int,+ facilityQueueCountRef :: IORef Int,+ facilityQueueCountStatsRef :: IORef (TimingStats Int),+ facilityQueueCountSource :: SignalSource Int,+ facilityTotalWaitTimeRef :: IORef Double,+ facilityWaitTimeRef :: IORef (SamplingStats Double),+ facilityWaitTimeSource :: SignalSource (),+ facilityTotalHoldingTimeRef :: IORef Double,+ facilityHoldingTimeRef :: IORef (SamplingStats Double),+ facilityHoldingTimeSource :: SignalSource (),+ facilityOwnerRef :: IORef (Maybe (FacilityOwnerItem a)),+ facilityDelayChain :: StrategyQueue (TransactQueueStrategy FCFS) (FacilityDelayedItem a),+ facilityInterruptChain :: StrategyQueue (TransactQueueStrategy LCFS) (FacilityInterruptedItem a),+ facilityPendingChain :: StrategyQueue (TransactQueueStrategy FCFS) (FacilityPendingItem a) }++-- | Identifies a transact item that owns the facility.+data FacilityOwnerItem a =+ FacilityOwnerItem { ownerItemTransact :: Transact a,+ ownerItemTime :: Double,+ ownerItemPreempting :: Bool,+ ownerItemInterrupting :: Bool,+ ownerItemAccHoldingTime :: Double }++-- | Idenitifies a transact item that was delayed.+data FacilityDelayedItem a =+ FacilityDelayedItem { delayedItemTransact :: Transact a,+ delayedItemTime :: Double,+ delayedItemPreempting :: Bool,+ delayedItemInterrupting :: Bool,+ delayedItemCont :: FrozenCont () }++-- | Idenitifies a transact item that was interrupted.+data FacilityInterruptedItem a =+ FacilityInterruptedItem { interruptedItemTransact :: Transact a,+ interruptedItemTime :: Double,+ interruptedItemPreempting :: Bool,+ interruptedItemInterrupting :: Bool,+ interruptedItemRemainingTime :: Maybe Double,+ interruptedItemTransfer :: Maybe (FacilityPreemptTransfer a),+ interruptedItemAccHoldingTime :: Double }++-- | Idenitifies a transact item which is pending.+data FacilityPendingItem a =+ FacilityPendingItem { pendingItemTransact :: Transact a,+ pendingItemTime :: Double,+ pendingItemPreempting :: Bool,+ pendingItemInterrupting :: Bool,+ pendingItemCont :: FrozenCont () }++instance Eq (Facility a) where+ x == y = facilityCountRef x == facilityCountRef y -- unique references++-- | The facility preemption mode.+data FacilityPreemptMode a =+ FacilityPreemptMode { facilityPriorityMode :: Bool,+ -- ^ the Priority mode; otherwise, the Interrupt mode+ facilityTransfer :: Maybe (FacilityPreemptTransfer a),+ -- ^ where to transfer the preempted transact,+ -- passing in the remaining time from the process holding+ -- computation such as the ADVANCE block+ facilityRemoveMode :: Bool+ -- ^ the Remove mode+ }++-- | Proceed with the computation by the specified preempted transact+-- and remaining time from the process holding computation such as the ADVANCE block.+type FacilityPreemptTransfer a = Transact a -> Maybe Double -> Process ()++-- | The default facility preemption mode.+defaultFacilityPreemptMode :: FacilityPreemptMode a+defaultFacilityPreemptMode =+ FacilityPreemptMode { facilityPriorityMode = False,+ facilityTransfer = Nothing,+ facilityRemoveMode = False+ }++-- | Create a new facility.+newFacility :: Event (Facility a)+newFacility =+ Event $ \p ->+ do let r = pointRun p+ t = pointTime p+ countRef <- newIORef 1+ countStatsRef <- newIORef $ returnTimingStats t 1+ countSource <- invokeSimulation r newSignalSource+ captureCountRef <- newIORef 0+ captureCountSource <- invokeSimulation r newSignalSource+ utilCountRef <- newIORef 0+ utilCountStatsRef <- newIORef $ returnTimingStats t 0+ utilCountSource <- invokeSimulation r newSignalSource+ queueCountRef <- newIORef 0+ queueCountStatsRef <- newIORef $ returnTimingStats t 0+ queueCountSource <- invokeSimulation r newSignalSource+ totalWaitTimeRef <- newIORef 0+ waitTimeRef <- newIORef emptySamplingStats+ waitTimeSource <- invokeSimulation r newSignalSource+ totalHoldingTimeRef <- newIORef 0+ holdingTimeRef <- newIORef emptySamplingStats+ holdingTimeSource <- invokeSimulation r newSignalSource+ ownerRef <- newIORef Nothing+ delayChain <- invokeSimulation r $ newStrategyQueue (TransactQueueStrategy FCFS)+ interruptChain <- invokeSimulation r $ newStrategyQueue (TransactQueueStrategy LCFS)+ pendingChain <- invokeSimulation r $ newStrategyQueue (TransactQueueStrategy FCFS)+ return Facility { facilityCountRef = countRef,+ facilityCountStatsRef = countStatsRef,+ facilityCountSource = countSource,+ facilityCaptureCountRef = captureCountRef,+ facilityCaptureCountSource = captureCountSource,+ facilityUtilisationCountRef = utilCountRef,+ facilityUtilisationCountStatsRef = utilCountStatsRef,+ facilityUtilisationCountSource = utilCountSource,+ facilityQueueCountRef = queueCountRef,+ facilityQueueCountStatsRef = queueCountStatsRef,+ facilityQueueCountSource = queueCountSource,+ facilityTotalWaitTimeRef = totalWaitTimeRef,+ facilityWaitTimeRef = waitTimeRef,+ facilityWaitTimeSource = waitTimeSource,+ facilityTotalHoldingTimeRef = totalHoldingTimeRef,+ facilityHoldingTimeRef = holdingTimeRef,+ facilityHoldingTimeSource = holdingTimeSource,+ facilityOwnerRef = ownerRef,+ facilityDelayChain = delayChain,+ facilityInterruptChain = interruptChain,+ facilityPendingChain = pendingChain }++-- | Return the current available count of the facility.+facilityCount :: Facility a -> Event Int+facilityCount r =+ Event $ \p -> readIORef (facilityCountRef r)++-- | Return the statistics for the available count of the facility.+facilityCountStats :: Facility a -> Event (TimingStats Int)+facilityCountStats r =+ Event $ \p -> readIORef (facilityCountStatsRef r)++-- | Signal triggered when the 'facilityCount' property changes.+facilityCountChanged :: Facility a -> Signal Int+facilityCountChanged r =+ publishSignal $ facilityCountSource r++-- | Signal triggered when the 'facilityCount' property changes.+facilityCountChanged_ :: Facility a -> Signal ()+facilityCountChanged_ r =+ mapSignal (const ()) $ facilityCountChanged r++-- | Return the current capture count of the facility.+facilityCaptureCount :: Facility a -> Event Int+facilityCaptureCount r =+ Event $ \p -> readIORef (facilityCaptureCountRef r)++-- | Signal triggered when the 'facilityCaptureCount' property changes.+facilityCaptureCountChanged :: Facility a -> Signal Int+facilityCaptureCountChanged r =+ publishSignal $ facilityCaptureCountSource r++-- | Signal triggered when the 'facilityCaptureCount' property changes.+facilityCaptureCountChanged_ :: Facility a -> Signal ()+facilityCaptureCountChanged_ r =+ mapSignal (const ()) $ facilityCaptureCountChanged r++-- | Return the current utilisation count of the facility.+facilityUtilisationCount :: Facility a -> Event Int+facilityUtilisationCount r =+ Event $ \p -> readIORef (facilityUtilisationCountRef r)++-- | Return the statistics for the utilisation count of the facility.+facilityUtilisationCountStats :: Facility a -> Event (TimingStats Int)+facilityUtilisationCountStats r =+ Event $ \p -> readIORef (facilityUtilisationCountStatsRef r)++-- | Signal triggered when the 'facilityUtilisationCount' property changes.+facilityUtilisationCountChanged :: Facility a -> Signal Int+facilityUtilisationCountChanged r =+ publishSignal $ facilityUtilisationCountSource r++-- | Signal triggered when the 'facilityUtilisationCount' property changes.+facilityUtilisationCountChanged_ :: Facility a -> Signal ()+facilityUtilisationCountChanged_ r =+ mapSignal (const ()) $ facilityUtilisationCountChanged r++-- | Return the current queue length of the facility.+facilityQueueCount :: Facility a -> Event Int+facilityQueueCount r =+ Event $ \p -> readIORef (facilityQueueCountRef r)++-- | Return the statistics for the queue length of the facility.+facilityQueueCountStats :: Facility a -> Event (TimingStats Int)+facilityQueueCountStats r =+ Event $ \p -> readIORef (facilityQueueCountStatsRef r)++-- | Signal triggered when the 'facilityQueueCount' property changes.+facilityQueueCountChanged :: Facility a -> Signal Int+facilityQueueCountChanged r =+ publishSignal $ facilityQueueCountSource r++-- | Signal triggered when the 'facilityQueueCount' property changes.+facilityQueueCountChanged_ :: Facility a -> Signal ()+facilityQueueCountChanged_ r =+ mapSignal (const ()) $ facilityQueueCountChanged r++-- | Return the total wait time of the facility.+facilityTotalWaitTime :: Facility a -> Event Double+facilityTotalWaitTime r =+ Event $ \p -> readIORef (facilityTotalWaitTimeRef r)++-- | Return the statistics for the wait time of the facility.+facilityWaitTime :: Facility a -> Event (SamplingStats Double)+facilityWaitTime r =+ Event $ \p -> readIORef (facilityWaitTimeRef r)++-- | Signal triggered when the 'facilityTotalWaitTime' and 'facilityWaitTime' properties change.+facilityWaitTimeChanged :: Facility a -> Signal (SamplingStats Double)+facilityWaitTimeChanged r =+ mapSignalM (\() -> facilityWaitTime r) $ facilityWaitTimeChanged_ r++-- | Signal triggered when the 'facilityTotalWaitTime' and 'facilityWaitTime' properties change.+facilityWaitTimeChanged_ :: Facility a -> Signal ()+facilityWaitTimeChanged_ r =+ publishSignal $ facilityWaitTimeSource r++-- | Return the total holding time of the facility.+facilityTotalHoldingTime :: Facility a -> Event Double+facilityTotalHoldingTime r =+ Event $ \p -> readIORef (facilityTotalHoldingTimeRef r)++-- | Return the statistics for the holding time of the facility.+facilityHoldingTime :: Facility a -> Event (SamplingStats Double)+facilityHoldingTime r =+ Event $ \p -> readIORef (facilityHoldingTimeRef r)++-- | Signal triggered when the 'facilityTotalHoldingTime' and 'facilityHoldingTime' properties change.+facilityHoldingTimeChanged :: Facility a -> Signal (SamplingStats Double)+facilityHoldingTimeChanged r =+ mapSignalM (\() -> facilityHoldingTime r) $ facilityHoldingTimeChanged_ r++-- | Signal triggered when the 'facilityTotalHoldingTime' and 'facilityHoldingTime' properties change.+facilityHoldingTimeChanged_ :: Facility a -> Signal ()+facilityHoldingTimeChanged_ r =+ publishSignal $ facilityHoldingTimeSource r++-- | Whether the facility is currently interrupted.+facilityInterrupted :: Facility a -> Event Bool+facilityInterrupted r =+ Event $ \p ->+ do x <- readIORef (facilityOwnerRef r)+ case x of+ Nothing -> return False+ Just a -> return (ownerItemPreempting a)++-- | Seize the facility.+seizeFacility :: Facility a+ -- ^ the requested facility+ -> Transact a+ -- ^ the transact that tries to seize the facility+ -> Process ()+seizeFacility r transact =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let t = pointTime p+ f <- do f1 <- invokeEvent p $ strategyQueueNull (facilityDelayChain r)+ if f1+ then do f2 <- invokeEvent p $ strategyQueueNull (facilityInterruptChain r)+ if f2+ then invokeEvent p $ strategyQueueNull (facilityPendingChain r)+ else return False+ else return False+ if f+ then invokeEvent p $+ invokeCont c $+ invokeProcess pid $+ seizeFacility' r transact+ else do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ seizeFacility r transact+ invokeEvent p $+ strategyEnqueueWithPriority+ (facilityDelayChain r)+ (transactPriority transact)+ (FacilityDelayedItem transact t False False c)+ invokeEvent p $ updateFacilityQueueCount r 1++-- | Seize the facility.+seizeFacility' :: Facility a+ -- ^ the requested facility+ -> Transact a+ -- ^ the transact that tries to seize the facility+ -> Process ()+seizeFacility' r transact =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let t = pointTime p+ a <- readIORef (facilityOwnerRef r)+ case a of+ Nothing ->+ do writeIORef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t False False 0)+ invokeEvent p $ updateFacilityWaitTime r 0+ invokeEvent p $ updateFacilityCount r (-1)+ invokeEvent p $ updateFacilityCaptureCount r 1+ invokeEvent p $ updateFacilityUtilisationCount r 1+ invokeEvent p $ resumeCont c ()+ Just owner ->+ do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ seizeFacility r transact+ invokeEvent p $+ strategyEnqueueWithPriority+ (facilityDelayChain r)+ (transactPriority transact)+ (FacilityDelayedItem transact t False False c)+ invokeEvent p $ updateFacilityQueueCount r 1++-- | Preempt the facility.+preemptFacility :: Facility a+ -- ^ the requested facility+ -> Transact a+ -- ^ the transact that tries to preempt the facility+ -> FacilityPreemptMode a+ -- ^ the Preempt mode+ -> Process ()+preemptFacility r transact mode =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let t = pointTime p+ a <- readIORef (facilityOwnerRef r)+ case a of+ Nothing ->+ do writeIORef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t True False 0)+ invokeEvent p $ updateFacilityWaitTime r 0+ invokeEvent p $ updateFacilityCount r (-1)+ invokeEvent p $ updateFacilityCaptureCount r 1+ invokeEvent p $ updateFacilityUtilisationCount r 1+ invokeEvent p $ resumeCont c ()+ Just owner@(FacilityOwnerItem transact0 t0 preempting0 interrupting0 acc0)+ | (not $ facilityPriorityMode mode) && interrupting0 ->+ do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ preemptFacility r transact mode+ invokeEvent p $+ strategyEnqueueWithPriority+ (facilityPendingChain r)+ (transactPriority transact)+ (FacilityPendingItem transact t True True c)+ invokeEvent p $ updateFacilityQueueCount r 1+ Just owner@(FacilityOwnerItem transact0 t0 preempting0 interrupting0 acc0)+ | facilityPriorityMode mode && (transactPriority transact <= transactPriority transact0) ->+ do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ preemptFacility r transact mode+ invokeEvent p $+ strategyEnqueueWithPriority+ (facilityDelayChain r)+ (transactPriority transact)+ (FacilityDelayedItem transact t True True c)+ invokeEvent p $ updateFacilityQueueCount r 1+ Just owner@(FacilityOwnerItem transact0 t0 preempting0 interrupting0 acc0)+ | (not $ facilityRemoveMode mode) ->+ do writeIORef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t True True 0)+ pid0 <- invokeEvent p $ requireTransactProcessId transact0+ t2 <- invokeEvent p $ processInterruptionTime pid0+ let dt0 = fmap (\x -> x - t) t2+ invokeEvent p $+ strategyEnqueueWithPriority+ (facilityInterruptChain r)+ (transactPriority transact0)+ (FacilityInterruptedItem transact0 t preempting0 interrupting0 dt0 (facilityTransfer mode) (acc0 + (t - t0)))+ invokeEvent p $ updateFacilityQueueCount r 1+ invokeEvent p $ updateFacilityWaitTime r 0+ invokeEvent p $ updateFacilityCaptureCount r 1+ invokeEvent p $ transactPreemptionBegin transact0+ invokeEvent p $ resumeCont c ()+ Just owner@(FacilityOwnerItem transact0 t0 preempting0 interrupting0 acc0)+ | facilityRemoveMode mode ->+ do writeIORef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t True True 0)+ pid0 <- invokeEvent p $ requireTransactProcessId transact0+ t2 <- invokeEvent p $ processInterruptionTime pid0+ let dt0 = fmap (\x -> x - t) t2+ invokeEvent p $ updateFacilityWaitTime r 0+ invokeEvent p $ updateFacilityCaptureCount r 1+ invokeEvent p $ updateFacilityHoldingTime r (acc0 + (t - t0))+ case facilityTransfer mode of+ Nothing ->+ throwIO $+ SimulationRetry+ "The transfer destination is not specified for the removed preempted transact: preemptFacility"+ Just transfer ->+ invokeEvent p $ transferTransact transact0 (transfer transact0 dt0)+ invokeEvent p $ resumeCont c ()++-- | Return the facility by the active transact.+returnFacility :: Facility a+ -- ^ the facility to return+ -> Transact a+ -- ^ the active transact that tries to return the facility+ -> Process ()+returnFacility r transact = releaseFacility' r transact True ++-- | Release the facility by the active transact.+releaseFacility :: Facility a+ -- ^ the facility to release+ -> Transact a+ -- ^ the active transact that tries to release the facility+ -> Process ()+releaseFacility r transact = releaseFacility' r transact False ++-- | Release the facility by the active transact.+releaseFacility' :: Facility a+ -- ^ the facility to release+ -> Transact a+ -- ^ the active transact that tries to release the facility+ -> Bool+ -- ^ whether the transact is preempting+ -> Process ()+releaseFacility' r transact preempting = + Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let t = pointTime p+ a <- readIORef (facilityOwnerRef r)+ case a of+ Nothing ->+ throwIO $+ SimulationRetry+ "There is no owner of the facility: releaseFacility'"+ Just owner@(FacilityOwnerItem transact0 t0 preempting0 interrupting0 acc0) | transact0 == transact && preempting0 /= preempting ->+ throwIO $+ SimulationRetry+ "The mismatch use of releaseFacility and returnFacility: releaseFacility'"+ Just owner@(FacilityOwnerItem transact0 t0 preempting0 interrupting0 acc0) | transact0 == transact ->+ do writeIORef (facilityOwnerRef r) Nothing+ invokeEvent p $ updateFacilityUtilisationCount r (-1)+ invokeEvent p $ updateFacilityHoldingTime r (acc0 + (t - t0))+ invokeEvent p $ updateFacilityCount r 1+ invokeEvent p $ enqueueEvent t $ tryCaptureFacility r+ invokeEvent p $ resumeCont c ()+ Just owner ->+ throwIO $+ SimulationRetry+ "The facility has another owner: releaseFacility'"++-- | Try to capture the facility.+tryCaptureFacility :: Facility a -> Event ()+tryCaptureFacility r =+ Event $ \p ->+ do let t = pointTime p+ a <- readIORef (facilityOwnerRef r)+ case a of+ Nothing ->+ invokeEvent p $ captureFacility r+ Just owner -> return ()++-- | Find another owner of the facility.+captureFacility :: Facility a -> Event ()+captureFacility r =+ Event $ \p ->+ do let t = pointTime p+ f <- invokeEvent p $ strategyQueueNull (facilityPendingChain r)+ if not f+ then do FacilityPendingItem transact t0 preempting interrupting c0 <- invokeEvent p $ strategyDequeue (facilityPendingChain r)+ invokeEvent p $ updateFacilityQueueCount r (-1)+ c <- invokeEvent p $ unfreezeCont c0+ case c of+ Nothing ->+ invokeEvent p $ captureFacility r+ Just c ->+ do writeIORef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t preempting interrupting 0)+ invokeEvent p $ updateFacilityWaitTime r (t - t0)+ invokeEvent p $ updateFacilityUtilisationCount r 1+ invokeEvent p $ updateFacilityCaptureCount r 1+ invokeEvent p $ updateFacilityCount r (-1)+ invokeEvent p $ enqueueEvent t $ reenterCont c ()+ else do f <- invokeEvent p $ strategyQueueNull (facilityInterruptChain r)+ if not f+ then do FacilityInterruptedItem transact t0 preempting interrupting dt0 transfer0 acc0 <- invokeEvent p $ strategyDequeue (facilityInterruptChain r)+ pid <- invokeEvent p $ requireTransactProcessId transact+ invokeEvent p $ updateFacilityQueueCount r (-1)+ f <- invokeEvent p $ processCancelled pid+ case f of+ True ->+ invokeEvent p $ captureFacility r+ False ->+ do writeIORef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t preempting interrupting acc0)+ invokeEvent p $ updateFacilityWaitTime r (t - t0)+ invokeEvent p $ updateFacilityUtilisationCount r 1+ invokeEvent p $ updateFacilityCount r (-1)+ case transfer0 of+ Nothing -> return ()+ Just transfer ->+ invokeEvent p $ transferTransact transact (transfer transact dt0)+ invokeEvent p $ transactPreemptionEnd transact+ else do f <- invokeEvent p $ strategyQueueNull (facilityDelayChain r)+ if not f+ then do FacilityDelayedItem transact t0 preempting interrupting c0 <- invokeEvent p $ strategyDequeue (facilityDelayChain r)+ invokeEvent p $ updateFacilityQueueCount r (-1)+ c <- invokeEvent p $ unfreezeCont c0+ case c of+ Nothing ->+ invokeEvent p $ captureFacility r+ Just c ->+ do writeIORef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t preempting interrupting 0)+ invokeEvent p $ updateFacilityWaitTime r (t - t0)+ invokeEvent p $ updateFacilityUtilisationCount r 1+ invokeEvent p $ updateFacilityCaptureCount r 1+ invokeEvent p $ updateFacilityCount r (-1)+ invokeEvent p $ enqueueEvent t $ reenterCont c ()+ else return ()++-- | Signal triggered when one of the facility counters changes.+facilityChanged_ :: Facility a -> Signal ()+facilityChanged_ r =+ facilityCountChanged_ r <>+ facilityCaptureCountChanged_ r <>+ facilityUtilisationCountChanged_ r <>+ facilityQueueCountChanged_ r++-- | Update the facility count and its statistics.+updateFacilityCount :: Facility a -> Int -> Event ()+updateFacilityCount r delta =+ Event $ \p ->+ do a <- readIORef (facilityCountRef r)+ let a' = a + delta+ a' `seq` writeIORef (facilityCountRef r) a'+ modifyIORef' (facilityCountStatsRef r) $+ addTimingStats (pointTime p) a'+ invokeEvent p $+ triggerSignal (facilityCountSource r) a'++-- | Update the facility capture count.+updateFacilityCaptureCount :: Facility a -> Int -> Event ()+updateFacilityCaptureCount r delta =+ Event $ \p ->+ do a <- readIORef (facilityCaptureCountRef r)+ let a' = a + delta+ a' `seq` writeIORef (facilityCaptureCountRef r) a'+ invokeEvent p $+ triggerSignal (facilityCaptureCountSource r) a'++-- | Update the facility queue length and its statistics.+updateFacilityQueueCount :: Facility a -> Int -> Event ()+updateFacilityQueueCount r delta =+ Event $ \p ->+ do a <- readIORef (facilityQueueCountRef r)+ let a' = a + delta+ a' `seq` writeIORef (facilityQueueCountRef r) a'+ modifyIORef' (facilityQueueCountStatsRef r) $+ addTimingStats (pointTime p) a'+ invokeEvent p $+ triggerSignal (facilityQueueCountSource r) a'++-- | Update the facility utilisation count and its statistics.+updateFacilityUtilisationCount :: Facility a -> Int -> Event ()+updateFacilityUtilisationCount r delta =+ Event $ \p ->+ do a <- readIORef (facilityUtilisationCountRef r)+ let a' = a + delta+ a' `seq` writeIORef (facilityUtilisationCountRef r) a'+ modifyIORef' (facilityUtilisationCountStatsRef r) $+ addTimingStats (pointTime p) a'+ invokeEvent p $+ triggerSignal (facilityUtilisationCountSource r) a'++-- | Update the facility wait time and its statistics.+updateFacilityWaitTime :: Facility a -> Double -> Event ()+updateFacilityWaitTime r delta =+ Event $ \p ->+ do a <- readIORef (facilityTotalWaitTimeRef r)+ let a' = a + delta+ a' `seq` writeIORef (facilityTotalWaitTimeRef r) a'+ modifyIORef' (facilityWaitTimeRef r) $+ addSamplingStats delta+ invokeEvent p $+ triggerSignal (facilityWaitTimeSource r) ()++-- | Update the facility holding time and its statistics.+updateFacilityHoldingTime :: Facility a -> Double -> Event ()+updateFacilityHoldingTime r delta =+ Event $ \p ->+ do a <- readIORef (facilityTotalHoldingTimeRef r)+ let a' = a + delta+ a' `seq` writeIORef (facilityTotalHoldingTimeRef r) a'+ modifyIORef' (facilityHoldingTimeRef r) $+ addSamplingStats delta+ invokeEvent p $+ triggerSignal (facilityHoldingTimeSource r) ()++-- | Reset the statistics.+resetFacility :: Facility a -> Event ()+resetFacility r =+ Event $ \p ->+ do let t = pointTime p+ count <- readIORef (facilityCountRef r)+ writeIORef (facilityCountStatsRef r) $+ returnTimingStats t count+ writeIORef (facilityCaptureCountRef r) 0+ utilCount <- readIORef (facilityUtilisationCountRef r)+ writeIORef (facilityUtilisationCountStatsRef r) $+ returnTimingStats t utilCount+ queueCount <- readIORef (facilityQueueCountRef r)+ writeIORef (facilityQueueCountStatsRef r) $+ returnTimingStats t queueCount+ writeIORef (facilityTotalWaitTimeRef r) 0+ writeIORef (facilityWaitTimeRef r) emptySamplingStats+ writeIORef (facilityTotalHoldingTimeRef r) 0+ writeIORef (facilityHoldingTimeRef r) emptySamplingStats+ invokeEvent p $+ triggerSignal (facilityCaptureCountSource r) 0+ invokeEvent p $+ triggerSignal (facilityWaitTimeSource r) ()+ invokeEvent p $+ triggerSignal (facilityHoldingTimeSource r) ()
+ Simulation/Aivika/GPSS/MatchChain.hs view
@@ -0,0 +1,98 @@++-- |+-- Module : Simulation.Aivika.GPSS.MatchChain+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines a GPSS Match Chain.+--+module Simulation.Aivika.GPSS.MatchChain+ (MatchChain,+ newMatchChain,+ matchTransact,+ transactMatching,+ transactMatchingChanged,+ transactMatchingChangedByTransact_,+ transactMatchingChangedByAssemblySet_) where++import Data.IORef++import Control.Monad+import Control.Monad.Trans++import qualified Data.HashMap.Lazy as HM++import Simulation.Aivika+import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.AssemblySet++-- | Represents a Match Chain.+data MatchChain =+ MatchChain { matchChainMap :: IORef (HM.HashMap AssemblySet ProcessId),+ matchChainSource :: SignalSource AssemblySet+ }++-- | Create a new Match Chain.+newMatchChain :: Simulation MatchChain+newMatchChain =+ do map <- liftIO $ newIORef HM.empty+ src <- newSignalSource+ return MatchChain { matchChainMap = map,+ matchChainSource = src+ }++-- | Match the transact.+matchTransact :: MatchChain -> Transact a -> Process ()+matchTransact chain t =+ do (map, set) <-+ liftEvent $+ do map <- liftIO $ readIORef (matchChainMap chain)+ set <- transactAssemblySet t+ return (map, set)+ case HM.lookup set map of+ Just pid ->+ liftEvent $+ do liftIO $ modifyIORef (matchChainMap chain) $+ HM.delete set+ yieldEvent $+ triggerSignal (matchChainSource chain) set+ reactivateProcess pid+ Nothing ->+ do liftEvent $+ do pid <- requireTransactProcessId t+ liftIO $ modifyIORef (matchChainMap chain) $+ HM.insert set pid+ yieldEvent $+ triggerSignal (matchChainSource chain) set+ passivateProcess++-- | Test whether there is a matching transact.+transactMatching :: MatchChain -> AssemblySet -> Event Bool+transactMatching chain set =+ do map <- liftIO $ readIORef (matchChainMap chain)+ return (HM.member set map)++-- | Signal each time the 'transactMatching' flag changes.+transactMatchingChangedByAssemblySet_ :: MatchChain -> AssemblySet -> Signal ()+transactMatchingChangedByAssemblySet_ chain set =+ mapSignal (const ()) $+ filterSignal (== set) $+ transactMatchingChanged chain++-- | Signal each time the 'transactMatching' flag changes.+transactMatchingChangedByTransact_ :: MatchChain -> Transact a -> Signal ()+transactMatchingChangedByTransact_ chain t =+ mapSignal (const ()) $+ filterSignalM pred $+ transactMatchingChanged chain+ where pred set =+ do set' <- transactAssemblySet t+ return (set == set')++-- | Signal each time the 'transactMatching' flag changes.+transactMatchingChanged :: MatchChain -> Signal AssemblySet+transactMatchingChanged chain =+ publishSignal (matchChainSource chain)
+ Simulation/Aivika/GPSS/Queue.hs view
@@ -0,0 +1,336 @@++-- |+-- Module : Simulation.Aivika.GPSS.Queue+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines a GPSS queue entity.+--+module Simulation.Aivika.GPSS.Queue+ (-- * Queue Types+ Queue,+ QueueEntry(..),+ -- * Creating Queue+ newQueue,+ -- * Queue Properties and Activities+ queueNull,+ queueContent,+ queueContentStats,+ enqueueCount,+ enqueueZeroEntryCount,+ queueWaitTime,+ queueNonZeroEntryWaitTime,+ queueRate,+ -- * Dequeuing and Enqueuing+ enqueue,+ dequeue,+ -- * Statistics Reset+ resetQueue,+ -- * Derived Signals for Properties+ queueNullChanged,+ queueNullChanged_,+ queueContentChanged,+ queueContentChanged_,+ enqueueCountChanged,+ enqueueCountChanged_,+ enqueueZeroEntryCountChanged,+ enqueueZeroEntryCountChanged_,+ queueWaitTimeChanged,+ queueWaitTimeChanged_,+ queueNonZeroEntryWaitTimeChanged,+ queueNonZeroEntryWaitTimeChanged_,+ queueRateChanged,+ queueRateChanged_,+ -- * Basic Signals+ enqueued,+ dequeued,+ -- * Overall Signal+ queueChanged_) where++import Data.IORef+import Data.Monoid+import Data.Maybe+import Data.Hashable++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika+import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Dynamics+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Process+import Simulation.Aivika.Signal+import Simulation.Aivika.Statistics++import Simulation.Aivika.GPSS.Transact++-- | Represents the queue entity.+data Queue =+ Queue { queueSequenceNo :: Int,+ queueContentRef :: IORef Int,+ queueContentStatsRef :: IORef (TimingStats Int),+ enqueueCountRef :: IORef Int,+ enqueueZeroEntryCountRef :: IORef Int,+ queueWaitTimeRef :: IORef (SamplingStats Double),+ queueNonZeroEntryWaitTimeRef :: IORef (SamplingStats Double),+ enqueuedSource :: SignalSource (),+ dequeuedSource :: SignalSource ()+ }++-- | The information about queue entry.+data QueueEntry =+ QueueEntry { entryQueue :: Queue,+ -- ^ the entry queue+ entryEnqueueTime :: Double+ -- ^ the time of registering the queue entry+ } deriving Eq++instance Eq Queue where+ x == y = (queueContentRef x) == (queueContentRef y)++instance Hashable Queue where+ hashWithSalt salt x = hashWithSalt salt (queueSequenceNo x)++-- | Create a new queue.+newQueue :: Event Queue +newQueue =+ do t <- liftDynamics time+ g <- liftParameter generatorParameter+ no <- liftIO $ generateSequenceNo g+ i <- liftIO $ newIORef 0+ is <- liftIO $ newIORef $ returnTimingStats t 0+ e <- liftIO $ newIORef 0+ z <- liftIO $ newIORef 0 + w <- liftIO $ newIORef mempty+ w2 <- liftIO $ newIORef mempty+ s1 <- liftSimulation $ newSignalSource+ s2 <- liftSimulation $ newSignalSource+ return Queue { queueSequenceNo = no,+ queueContentRef = i,+ queueContentStatsRef = is,+ enqueueCountRef = e,+ enqueueZeroEntryCountRef = z,+ queueWaitTimeRef = w,+ queueNonZeroEntryWaitTimeRef = w2,+ enqueuedSource = s1,+ dequeuedSource = s2 }+ +-- | Test whether the queue is empty.+--+-- See also 'queueNullChanged' and 'queueNullChanged_'.+queueNull :: Queue -> Event Bool+queueNull q =+ Event $ \p ->+ do n <- readIORef (queueContentRef q)+ return (n == 0)+ +-- | Signal when the 'queueNull' property value has changed.+queueNullChanged :: Queue -> Signal Bool+queueNullChanged q =+ mapSignalM (const $ queueNull q) (queueNullChanged_ q)+ +-- | Signal when the 'queueNull' property value has changed.+queueNullChanged_ :: Queue -> Signal ()+queueNullChanged_ = queueContentChanged_++-- | Return the current queue content.+--+-- See also 'queueContentStats', 'queueContentChanged' and 'queueContentChanged_'.+queueContent :: Queue -> Event Int+queueContent q =+ Event $ \p -> readIORef (queueContentRef q)++-- | Return the queue content statistics.+queueContentStats :: Queue -> Event (TimingStats Int)+queueContentStats q =+ Event $ \p -> readIORef (queueContentStatsRef q)+ +-- | Signal when the 'queueContent' property value has changed.+queueContentChanged :: Queue -> Signal Int+queueContentChanged q =+ mapSignalM (const $ queueContent q) (queueContentChanged_ q)+ +-- | Signal when the 'queueContent' property value has changed.+queueContentChanged_ :: Queue -> Signal ()+queueContentChanged_ q =+ mapSignal (const ()) (enqueued q) <>+ mapSignal (const ()) (dequeued q)++-- | Return the total number of input items that were enqueued.+--+-- See also 'enqueueCountChanged' and 'enqueueCountChanged_'.+enqueueCount :: Queue -> Event Int+enqueueCount q =+ Event $ \p -> readIORef (enqueueCountRef q)+ +-- | Signal when the 'enqueueCount' property value has changed.+enqueueCountChanged :: Queue -> Signal Int+enqueueCountChanged q =+ mapSignalM (const $ enqueueCount q) (enqueueCountChanged_ q)+ +-- | Signal when the 'enqueueCount' property value has changed.+enqueueCountChanged_ :: Queue -> Signal ()+enqueueCountChanged_ q =+ mapSignal (const ()) (enqueued q)++-- | Return the total number of zero entry items.+--+-- See also 'enqueueZeroEntryCountChanged' and 'enqueueZeroEntryCountChanged_'.+enqueueZeroEntryCount :: Queue -> Event Int+enqueueZeroEntryCount q =+ Event $ \p -> readIORef (enqueueZeroEntryCountRef q)+ +-- | Signal when the 'enqueueZeroEntryCount' property value has changed.+enqueueZeroEntryCountChanged :: Queue -> Signal Int+enqueueZeroEntryCountChanged q =+ mapSignalM (const $ enqueueZeroEntryCount q) (enqueueZeroEntryCountChanged_ q)+ +-- | Signal when the 'enqueueZeroEntryCount' property value has changed.+enqueueZeroEntryCountChanged_ :: Queue -> Signal ()+enqueueZeroEntryCountChanged_ q =+ mapSignal (const ()) (dequeued q)++-- | Return the wait (or residence) time.+--+-- See also 'queueWaitTimeChanged' and 'queueWaitTimeChanged_'.+queueWaitTime :: Queue -> Event (SamplingStats Double)+queueWaitTime q =+ Event $ \p -> readIORef (queueWaitTimeRef q)+ +-- | Signal when the 'queueWaitTime' property value has changed.+queueWaitTimeChanged :: Queue -> Signal (SamplingStats Double)+queueWaitTimeChanged q =+ mapSignalM (const $ queueWaitTime q) (queueWaitTimeChanged_ q)+ +-- | Signal when the 'queueWaitTime' property value has changed.+queueWaitTimeChanged_ :: Queue -> Signal ()+queueWaitTimeChanged_ q =+ mapSignal (const ()) (dequeued q)+ +-- | Return the wait (or residence) time excluding zero entries.+--+-- See also 'queueNonZeroEntryWaitTimeChanged' and 'queueNonZeroEntryWaitTimeChanged_'.+queueNonZeroEntryWaitTime :: Queue -> Event (SamplingStats Double)+queueNonZeroEntryWaitTime q =+ Event $ \p -> readIORef (queueNonZeroEntryWaitTimeRef q)+ +-- | Signal when the 'queueNonZeroEntryWaitTime' property value has changed.+queueNonZeroEntryWaitTimeChanged :: Queue -> Signal (SamplingStats Double)+queueNonZeroEntryWaitTimeChanged q =+ mapSignalM (const $ queueNonZeroEntryWaitTime q) (queueNonZeroEntryWaitTimeChanged_ q)+ +-- | Signal when the 'queueNonZeroEntryWaitTime' property value has changed.+queueNonZeroEntryWaitTimeChanged_ :: Queue -> Signal ()+queueNonZeroEntryWaitTimeChanged_ q =+ mapSignal (const ()) (dequeued q)++-- | Return a long-term average queue rate calculated as+-- the average queue content divided by the average wait time.+--+-- See also 'queueRateChanged' and 'queueRateChanged_'.+queueRate :: Queue -> Event Double+queueRate q =+ Event $ \p ->+ do x <- readIORef (queueContentStatsRef q)+ y <- readIORef (queueWaitTimeRef q)+ return (timingStatsMean x / samplingStatsMean y) + +-- | Signal when the 'queueRate' property value has changed.+queueRateChanged :: Queue -> Signal Double+queueRateChanged q =+ mapSignalM (const $ queueRate q) (queueRateChanged_ q)+ +-- | Signal when the 'queueRate' property value has changed.+queueRateChanged_ :: Queue -> Signal ()+queueRateChanged_ q =+ mapSignal (const ()) (enqueued q) <>+ mapSignal (const ()) (dequeued q)++-- | Return a signal that notifies when enqueuing an item.+enqueued:: Queue -> Signal ()+enqueued q = publishSignal (enqueuedSource q)++-- | Return a signal that notifies when the dequeuing the item.+dequeued :: Queue -> Signal ()+dequeued q = publishSignal (dequeuedSource q)++-- | Enqueue the item.+enqueue :: Queue+ -- ^ the queue+ -> Transact a+ -- ^ the item to be enqueued+ -> Int+ -- ^ the content increment+ -> Event ()+enqueue q transact increment =+ Event $ \p ->+ do let t = pointTime p+ e = QueueEntry { entryQueue = q,+ entryEnqueueTime = t }+ n <- readIORef (enqueueCountRef q)+ let n' = n + 1+ n' `seq` writeIORef (enqueueCountRef q) n'+ c <- readIORef (queueContentRef q)+ let c' = c + increment+ c' `seq` writeIORef (queueContentRef q) c'+ modifyIORef' (queueContentStatsRef q) (addTimingStats t c')+ invokeEvent p $+ registerTransactQueueEntry transact e+ invokeEvent p $+ triggerSignal (enqueuedSource q) ()++-- | Dequeue the item.+dequeue :: Queue+ -- ^ the queue+ -> Transact a+ -- ^ the item to be dequeued+ -> Int+ -- ^ the content decrement+ -> Event ()+dequeue q transact decrement =+ Event $ \p ->+ do e <- invokeEvent p $+ unregisterTransactQueueEntry transact q+ let t = pointTime p+ t0 = entryEnqueueTime e+ dt = t - t0+ c <- readIORef (queueContentRef q)+ let c' = c - decrement+ c' `seq` writeIORef (queueContentRef q) c'+ modifyIORef' (queueContentStatsRef q) (addTimingStats t c')+ modifyIORef' (queueWaitTimeRef q) $+ addSamplingStats dt+ if t == t0+ then modifyIORef' (enqueueZeroEntryCountRef q) (+ 1)+ else modifyIORef' (queueNonZeroEntryWaitTimeRef q) $+ addSamplingStats dt+ invokeEvent p $+ triggerSignal (dequeuedSource q) ()++-- | Signal whenever any property of the queue changes.+--+-- The property must have the corresponded signal. There are also characteristics+-- similar to the properties but that have no signals. As a rule, such characteristics+-- already depend on the simulation time and therefore they may change at any+-- time point.+queueChanged_ :: Queue -> Signal ()+queueChanged_ q =+ mapSignal (const ()) (enqueued q) <>+ mapSignal (const ()) (dequeued q)++-- | Reset the statistics.+resetQueue :: Queue -> Event () +resetQueue q =+ do t <- liftDynamics time+ content <- liftIO $ readIORef (queueContentRef q)+ liftIO $ writeIORef (queueContentStatsRef q) $+ returnTimingStats t content+ liftIO $ writeIORef (enqueueCountRef q) 0+ liftIO $ writeIORef (enqueueZeroEntryCountRef q) 0+ liftIO $ writeIORef (queueWaitTimeRef q) mempty+ liftIO $ writeIORef (queueNonZeroEntryWaitTimeRef q) mempty
+ Simulation/Aivika/GPSS/Queue.hs-boot view
@@ -0,0 +1,27 @@++-- |+-- Module : Simulation.Aivika.GPSS.Queue+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This is an hs-boot file.+--+module Simulation.Aivika.GPSS.Queue+ (Queue,+ QueueEntry,+ entryQueue) where++import Data.Hashable++data Queue+data QueueEntry++instance Eq Queue+instance Hashable Queue++instance Eq QueueEntry++entryQueue :: QueueEntry -> Queue
+ Simulation/Aivika/GPSS/Results.hs view
@@ -0,0 +1,175 @@++-- |+-- Module : Simulation.Aivika.GPSS.Results+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- The module allows exporting the simulation results from the model.+--+module Simulation.Aivika.GPSS.Results () where++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika++import qualified Simulation.Aivika.GPSS.Queue as Q+import Simulation.Aivika.GPSS.Facility+import Simulation.Aivika.GPSS.Storage+import Simulation.Aivika.GPSS.Results.Locale+ +-- | Return a source by the specified queue.+queueResultSource :: ResultContainer Q.Queue+ -- ^ the queue container+ -> ResultSource+queueResultSource c =+ ResultObjectSource $+ ResultObject {+ resultObjectName = resultContainerName c,+ resultObjectId = resultContainerId c,+ resultObjectTypeId = queueId,+ resultObjectSignal = resultContainerSignal c,+ resultObjectSummary = queueResultSummary c,+ resultObjectProperties = [+ resultContainerProperty c "queueNull" queueNullId Q.queueNull Q.queueNullChanged_,+ resultContainerProperty c "queueContent" queueContentId Q.queueContent Q.queueContentChanged_,+ resultContainerProperty c "queueContentStats" queueContentStatsId Q.queueContentStats Q.queueContentChanged_,+ resultContainerProperty c "enqueueCount" enqueueCountId Q.enqueueCount Q.enqueueCountChanged_,+ resultContainerProperty c "enqueueZeroEntryCount" enqueueZeroEntryCountId Q.enqueueZeroEntryCount Q.enqueueZeroEntryCountChanged_,+ resultContainerProperty c "queueWaitTime" queueWaitTimeId Q.queueWaitTime Q.queueWaitTimeChanged_,+ resultContainerProperty c "queueNonZeroEntryWaitTime" queueNonZeroEntryWaitTimeId Q.queueNonZeroEntryWaitTime Q.queueNonZeroEntryWaitTimeChanged_,+ resultContainerProperty c "queueRate" queueRateId Q.queueRate Q.queueRateChanged_ ] }++-- | Return the summary by the specified queue.+queueResultSummary :: ResultContainer Q.Queue+ -- ^ the queue container+ -> ResultSource+queueResultSummary c =+ ResultObjectSource $+ ResultObject {+ resultObjectName = resultContainerName c,+ resultObjectId = resultContainerId c,+ resultObjectTypeId = queueId,+ resultObjectSignal = resultContainerSignal c,+ resultObjectSummary = queueResultSummary c,+ resultObjectProperties = [+ resultContainerProperty c "queueContentStats" queueContentStatsId Q.queueContentStats Q.queueContentChanged_,+ resultContainerProperty c "enqueueCount" enqueueCountId Q.enqueueCount Q.enqueueCountChanged_,+ resultContainerProperty c "enqueueZeroEntryCount" enqueueZeroEntryCountId Q.enqueueZeroEntryCount Q.enqueueZeroEntryCountChanged_,+ resultContainerProperty c "queueWaitTime" queueWaitTimeId Q.queueWaitTime Q.queueWaitTimeChanged_,+ resultContainerProperty c "queueNonZeroEntryWaitTime" queueNonZeroEntryWaitTimeId Q.queueNonZeroEntryWaitTime Q.queueNonZeroEntryWaitTimeChanged_,+ resultContainerProperty c "queueRate" queueRateId Q.queueRate Q.queueRateChanged_ ] }++-- | Return a source by the specified facility.+facilityResultSource :: ResultContainer (Facility a)+ -- ^ the facility container+ -> ResultSource+facilityResultSource c =+ ResultObjectSource $+ ResultObject {+ resultObjectName = resultContainerName c,+ resultObjectId = resultContainerId c,+ resultObjectTypeId = facilityId,+ resultObjectSignal = resultContainerSignal c,+ resultObjectSummary = facilityResultSummary c,+ resultObjectProperties = [+ resultContainerProperty c "queueCount" facilityQueueCountId facilityQueueCount facilityQueueCountChanged_,+ resultContainerProperty c "queueCountStats" facilityQueueCountStatsId facilityQueueCountStats facilityQueueCountChanged_,+ resultContainerProperty c "totalWaitTime" facilityTotalWaitTimeId facilityTotalWaitTime facilityWaitTimeChanged_,+ resultContainerProperty c "waitTime" facilityWaitTimeId facilityWaitTime facilityWaitTimeChanged_,+ resultContainerProperty c "totalHoldingTime" facilityTotalHoldingTimeId facilityTotalHoldingTime facilityHoldingTimeChanged_,+ resultContainerProperty c "holdingTime" facilityHoldingTimeId facilityHoldingTime facilityHoldingTimeChanged_,+ resultContainerIntegProperty c "interrupted" facilityInterruptedId facilityInterrupted,+ resultContainerProperty c "count" facilityCountId facilityCount facilityCountChanged_,+ resultContainerProperty c "countStats" facilityCountStatsId facilityCountStats facilityCountChanged_,+ resultContainerProperty c "captureCount" facilityCaptureCountId facilityCaptureCount facilityCaptureCountChanged_,+ resultContainerProperty c "utilisationCount" facilityUtilisationCountId facilityUtilisationCount facilityUtilisationCountChanged_,+ resultContainerProperty c "utilisationCountStats" facilityUtilisationCountStatsId facilityUtilisationCountStats facilityUtilisationCountChanged_ ] }++-- | Return a summary by the specified facility.+facilityResultSummary :: ResultContainer (Facility a)+ -- ^ the facility container+ -> ResultSource+facilityResultSummary c =+ ResultObjectSource $+ ResultObject {+ resultObjectName = resultContainerName c,+ resultObjectId = resultContainerId c,+ resultObjectTypeId = facilityId,+ resultObjectSignal = resultContainerSignal c,+ resultObjectSummary = facilityResultSummary c,+ resultObjectProperties = [+ resultContainerProperty c "queueCountStats" facilityQueueCountStatsId facilityQueueCountStats facilityQueueCountChanged_,+ resultContainerProperty c "waitTime" facilityWaitTimeId facilityWaitTime facilityWaitTimeChanged_,+ resultContainerProperty c "holdingTime" facilityHoldingTimeId facilityHoldingTime facilityHoldingTimeChanged_,+ resultContainerProperty c "countStats" facilityCountStatsId facilityCountStats facilityCountChanged_,+ resultContainerProperty c "captureCount" facilityCaptureCountId facilityCaptureCount facilityCaptureCountChanged_,+ resultContainerProperty c "utilisationCountStats" facilityUtilisationCountStatsId facilityUtilisationCountStats facilityUtilisationCountChanged_ ] }++-- | Return a source by the specified storage.+storageResultSource :: ResultContainer Storage+ -- ^ the storage container+ -> ResultSource+storageResultSource c =+ ResultObjectSource $+ ResultObject {+ resultObjectName = resultContainerName c,+ resultObjectId = resultContainerId c,+ resultObjectTypeId = storageId,+ resultObjectSignal = resultContainerSignal c,+ resultObjectSummary = storageResultSummary c,+ resultObjectProperties = [+ resultContainerConstProperty c "capacity" storageCapacityId storageCapacity,+ resultContainerIntegProperty c "empty" storageEmptyId storageEmpty,+ resultContainerIntegProperty c "full" storageFullId storageFull,+ resultContainerProperty c "queueCount" storageQueueCountId storageQueueCount storageQueueCountChanged_,+ resultContainerProperty c "queueCountStats" storageQueueCountStatsId storageQueueCountStats storageQueueCountChanged_,+ resultContainerProperty c "totalWaitTime" storageTotalWaitTimeId storageTotalWaitTime storageWaitTimeChanged_,+ resultContainerProperty c "waitTime" storageWaitTimeId storageWaitTime storageWaitTimeChanged_,+ resultContainerIntegProperty c "averageHoldingTime" storageAverageHoldingTimeId storageAverageHoldingTime,+ resultContainerProperty c "content" storageContentId storageContent storageContentChanged_,+ resultContainerProperty c "contentStats" storageContentStatsId storageContentStats storageContentChanged_,+ resultContainerProperty c "useCount" storageUseCountId storageUseCount storageUseCountChanged_,+ resultContainerProperty c "usedContent" storageUsedContentId storageUsedContent storageUsedContentChanged_,+ resultContainerProperty c "utilisationCount" storageUtilisationCountId storageUtilisationCount storageUtilisationCountChanged_,+ resultContainerProperty c "utilisationCountStats" storageUtilisationCountStatsId storageUtilisationCountStats storageUtilisationCountChanged_ ] }++-- | Return a summary by the specified storage.+storageResultSummary :: ResultContainer Storage+ -- ^ the storage container+ -> ResultSource+storageResultSummary c =+ ResultObjectSource $+ ResultObject {+ resultObjectName = resultContainerName c,+ resultObjectId = resultContainerId c,+ resultObjectTypeId = storageId,+ resultObjectSignal = resultContainerSignal c,+ resultObjectSummary = storageResultSummary c,+ resultObjectProperties = [+ resultContainerConstProperty c "capacity" storageCapacityId storageCapacity,+ resultContainerProperty c "queueCountStats" storageQueueCountStatsId storageQueueCountStats storageQueueCountChanged_,+ resultContainerProperty c "waitTime" storageWaitTimeId storageWaitTime storageWaitTimeChanged_,+ resultContainerIntegProperty c "averageHoldingTime" storageAverageHoldingTimeId storageAverageHoldingTime,+ resultContainerProperty c "contentStats" storageContentStatsId storageContentStats storageContentChanged_,+ resultContainerProperty c "useCount" storageUseCountId storageUseCount storageUseCountChanged_,+ resultContainerProperty c "usedContent" storageUsedContentId storageUsedContent storageUsedContentChanged_,+ resultContainerProperty c "utilisationCountStats" storageUtilisationCountStatsId storageUtilisationCountStats storageUtilisationCountChanged_ ] }++instance ResultProvider Q.Queue where++ resultSource' name i m =+ queueResultSource $ ResultContainer name i m (ResultSignal $ Q.queueChanged_ m)++instance ResultProvider (Facility a) where++ resultSource' name i m =+ facilityResultSource $ ResultContainer name i m (ResultSignal $ facilityChanged_ m)++instance ResultProvider Storage where++ resultSource' name i m =+ storageResultSource $ ResultContainer name i m (ResultSignal $ storageChanged_ m)
+ Simulation/Aivika/GPSS/Results/Locale.hs view
@@ -0,0 +1,316 @@++-- |+-- Module : Simulation.Aivika.GPSS.Results.Locale+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- The module defines locales for the simulation results.+--+module Simulation.Aivika.GPSS.Results.Locale where++import qualified Data.Map as M++import Simulation.Aivika++import qualified Simulation.Aivika.GPSS.Queue as Q+import Simulation.Aivika.GPSS.Facility+import Simulation.Aivika.GPSS.Storage++-- | The queue identifier.+queueId :: ResultId+queueId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "очередь"),+ (englishResultLocale, "the queue")]++-- | Property 'Q.queueNull'.+queueNullId :: ResultId+queueNullId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "очередь пуста?"),+ (englishResultLocale, "is the queue empty?")]++-- | Property 'Q.queueContent'.+queueContentId :: ResultId+queueContentId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущее содержимое очереди"),+ (englishResultLocale, "the current queue content")]++-- | Property 'Q.queueContentStats'.+queueContentStatsId :: ResultId+queueContentStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистика по содержимому очереди"),+ (englishResultLocale, "the queue content statistics")]++-- | Property 'Q.enqueueCount'.+enqueueCountId :: ResultId+enqueueCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "количество добавленных транзактов"),+ (englishResultLocale, "the number of enqueued transacts")]++-- | Property 'Q.enqueueZeroEntryCount'.+enqueueZeroEntryCountId :: ResultId+enqueueZeroEntryCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "количество добавленных транзактов без фактического ожидания"),+ (englishResultLocale, "the number of zero entry enqueued transacts")]++-- | Property 'Q.queueWaitTime'.+queueWaitTimeId :: ResultId+queueWaitTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "время ожидания"),+ (englishResultLocale, "the wait time")]++-- | Property 'Q.queueNonZeroEntryWaitTime'.+queueNonZeroEntryWaitTimeId :: ResultId+queueNonZeroEntryWaitTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "время ожидания без учета фактически неожидавших транзактов"),+ (englishResultLocale, "the wait time without zero entries")]++-- | Property 'Q.queueRate'.+queueRateId :: ResultId+queueRateId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "усредненная скорость (как средняя длина очереди на среднее время ожидания)"),+ (englishResultLocale, "the average queue rate (= queue size / wait time)")]++-- | The facility identifier.+facilityId :: ResultId+facilityId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "прибор"),+ (englishResultLocale, "the facility")]++-- | Property 'facilityCount'.+facilityCountId :: ResultId+facilityCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущее доступное количество прибора"),+ (englishResultLocale, "the current available count")]++-- | Property 'facilityCountStats'.+facilityCountStatsId :: ResultId+facilityCountStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистика доступного количества прибора"),+ (englishResultLocale, "the available count statistics")]++-- | Property 'facilityCaptureCount'.+facilityCaptureCountId :: ResultId+facilityCaptureCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущее количество захвата прибора"),+ (englishResultLocale, "the current capture count")]++-- | Property 'facilityUtilisationCount'.+facilityUtilisationCountId :: ResultId+facilityUtilisationCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущее используемое количество прибора"),+ (englishResultLocale, "the current utilisation count")]++-- | Property 'facilityUtilisationCountStats'.+facilityUtilisationCountStatsId :: ResultId+facilityUtilisationCountStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистка по используемому количеству прибора"),+ (englishResultLocale, "the utilisation count statistics")]++-- | Property 'facilityQueueCount'.+facilityQueueCountId :: ResultId+facilityQueueCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущая длина очереди к прибору"),+ (englishResultLocale, "the current queue size")]++-- | Property 'facilityQueueCountStats'.+facilityQueueCountStatsId :: ResultId+facilityQueueCountStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистика длины очереди к прибору"),+ (englishResultLocale, "the queue size statistics")]++-- | Property 'facilityTotalWaitTime'.+facilityTotalWaitTimeId :: ResultId+facilityTotalWaitTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "общее время ожидания прибора"),+ (englishResultLocale, "the total wait time")]++-- | Property 'facilityWaitTime'.+facilityWaitTimeId :: ResultId+facilityWaitTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "время ожидания прибора"),+ (englishResultLocale, "the wait time")]++-- | Property 'facilityTotalHoldingTime'.+facilityTotalHoldingTimeId :: ResultId+facilityTotalHoldingTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "общее время удержания прибора"),+ (englishResultLocale, "the total holding time")]++-- | Property 'facilityHoldingTime'.+facilityHoldingTimeId :: ResultId+facilityHoldingTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "время удержания прибора"),+ (englishResultLocale, "the holding time")]++-- | Property 'facilityInterrupted'.+facilityInterruptedId :: ResultId+facilityInterruptedId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "удержание прибора сейчас прервано?"),+ (englishResultLocale, "is the facility interrupted now?")]++-- | The storage identifier.+storageId :: ResultId+storageId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "многоканальное устройство"),+ (englishResultLocale, "the storage")]++-- | Property 'storageCapacity'.+storageCapacityId :: ResultId+storageCapacityId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "емкость многоканального устройства"),+ (englishResultLocale, "the storage capacity")]++-- | Property 'storageEmpty'.+storageEmptyId :: ResultId+storageEmptyId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "не используется ли совсем устройство сейчас?"),+ (englishResultLocale, "is the storage unused completely now?")]++-- | Property 'storageFull'.+storageFullId :: ResultId+storageFullId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "используется ли устройство полностью сейчас?"),+ (englishResultLocale, "is the storage used completely now?")]++-- | Property 'storageContent'.+storageContentId :: ResultId+storageContentId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "доступное содержимое устройства"),+ (englishResultLocale, "the current available content")]++-- | Property 'storageContentStats'.+storageContentStatsId :: ResultId+storageContentStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистика доступного содержимого устройства"),+ (englishResultLocale, "the available content statistics")]++-- | Property 'storageUseCount'.+storageUseCountId :: ResultId+storageUseCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "общее количество использований устройства"),+ (englishResultLocale, "the total use count")]++-- | Property 'storageUsedContent'.+storageUsedContentId :: ResultId+storageUsedContentId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "общее использованное количество устройства"),+ (englishResultLocale, "the total used content")]++-- | Property 'storageUtilisationCount'.+storageUtilisationCountId :: ResultId+storageUtilisationCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущее используемое количество устройства"),+ (englishResultLocale, "the current utilisation count")]++-- | Property 'storageUtilisationCountStats'.+storageUtilisationCountStatsId :: ResultId+storageUtilisationCountStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистка по используемому количеству устройства"),+ (englishResultLocale, "the utilisation count statistics")]++-- | Property 'storageQueueCount'.+storageQueueCountId :: ResultId+storageQueueCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущая длина очереди к устройству"),+ (englishResultLocale, "the current queue size")]++-- | Property 'storageQueueCountStats'.+storageQueueCountStatsId :: ResultId+storageQueueCountStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистика длины очереди к устройству"),+ (englishResultLocale, "the queue size statistics")]++-- | Property 'storageTotalWaitTime'.+storageTotalWaitTimeId :: ResultId+storageTotalWaitTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "общее время ожидания устройства"),+ (englishResultLocale, "the total wait time")]++-- | Property 'storageWaitTime'.+storageWaitTimeId :: ResultId+storageWaitTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "время ожидания устройства"),+ (englishResultLocale, "the wait time")]++-- | Property 'storageAverageHoldingTime'.+storageAverageHoldingTimeId :: ResultId+storageAverageHoldingTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "среднее время удержания устройства на единицу устройства"),+ (englishResultLocale, "the average holding time per unit")]
+ Simulation/Aivika/GPSS/Results/Transform.hs view
@@ -0,0 +1,214 @@++-- |+-- Module : Simulation.Aivika.GPSS.Results.Transform+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- The module defines transformations for the simulation results.+--+module Simulation.Aivika.GPSS.Results.Transform where++import Control.Category++import Simulation.Aivika+import qualified Simulation.Aivika.Results.Transform as T++import qualified Simulation.Aivika.GPSS.Queue as Q+import qualified Simulation.Aivika.GPSS.Facility as F+import qualified Simulation.Aivika.GPSS.Storage as S+import Simulation.Aivika.GPSS.Results+import Simulation.Aivika.GPSS.Results.Locale++-- | Represents the 'Q.Queue'.+newtype Queue = Queue ResultTransform++-- | An instance of the result transformer.+instance T.ResultTransformer Queue where+ tr (Queue a) = a++-- | Property 'Q.queueNull'.+queueNull :: Queue -> ResultTransform+queueNull (Queue a) =+ a >>> resultById queueNullId++-- | Property 'Q.queueContent'.+queueContent :: Queue -> ResultTransform+queueContent (Queue a) =+ a >>> resultById queueContentId++-- | Property 'Q.queueContentStats'.+queueContentStats :: Queue -> T.TimingStats+queueContentStats (Queue a) =+ T.TimingStats (a >>> resultById queueContentStatsId)++-- | Property 'Q.enqueueCount'.+enqueueCount :: Queue -> ResultTransform+enqueueCount (Queue a) =+ a >>> resultById enqueueCountId++-- | Property 'Q.enqueueZeroEntryCount'.+enqueueZeroEntryCount :: Queue -> ResultTransform+enqueueZeroEntryCount (Queue a) =+ a >>> resultById enqueueZeroEntryCountId++-- | Property 'Q.queueWaitTime'.+queueWaitTime :: Queue -> T.SamplingStats+queueWaitTime (Queue a) =+ T.SamplingStats (a >>> resultById queueWaitTimeId)++-- | Property 'Q.queueNonZeroEntryWaitTime'.+queueNonZeroEntryWaitTime :: Queue -> T.SamplingStats+queueNonZeroEntryWaitTime (Queue a) =+ T.SamplingStats (a >>> resultById queueNonZeroEntryWaitTimeId)++-- | Property 'Q.queueRate'.+queueRate :: Queue -> ResultTransform+queueRate (Queue a) =+ a >>> resultById queueRateId++-- | Represents the 'F.Facility'.+newtype Facility = Facility ResultTransform++-- | An instance of the result transformer.+instance T.ResultTransformer Facility where+ tr (Facility a) = a++-- | Property 'F.facilityCount'.+facilityCount :: Facility -> ResultTransform+facilityCount (Facility a) =+ a >>> resultById facilityCountId++-- | Property 'F.facilityCountStats'.+facilityCountStats :: Facility -> T.TimingStats+facilityCountStats (Facility a) =+ T.TimingStats (a >>> resultById facilityCountStatsId)++-- | Property 'F.facilityCaptureCount'.+facilityCaptureCount :: Facility -> ResultTransform+facilityCaptureCount (Facility a) =+ a >>> resultById facilityCaptureCountId++-- | Property 'F.facilityUtilisationCount'.+facilityUtilisationCount :: Facility -> ResultTransform+facilityUtilisationCount (Facility a) =+ a >>> resultById facilityUtilisationCountId++-- | Property 'F.facilityUtilisationCountStats'.+facilityUtilisationCountStats :: Facility -> T.TimingStats+facilityUtilisationCountStats (Facility a) =+ T.TimingStats (a >>> resultById facilityUtilisationCountStatsId)++-- | Property 'F.facilityQueueCount'.+facilityQueueCount :: Facility -> ResultTransform+facilityQueueCount (Facility a) =+ a >>> resultById facilityQueueCountId++-- | Property 'F.facilityQueueCountStats'.+facilityQueueCountStats :: Facility -> T.TimingStats+facilityQueueCountStats (Facility a) =+ T.TimingStats (a >>> resultById facilityQueueCountStatsId)++-- | Property 'F.facilityTotalWaitTime'.+facilityTotalWaitTime :: Facility -> ResultTransform+facilityTotalWaitTime (Facility a) =+ a >>> resultById facilityTotalWaitTimeId++-- | Property 'F.facilityWaitTime'.+facilityWaitTime :: Facility -> T.SamplingStats+facilityWaitTime (Facility a) =+ T.SamplingStats (a >>> resultById facilityWaitTimeId)++-- | Property 'F.facilityTotalHoldingTime'.+facilityTotalHoldingTime :: Facility -> ResultTransform+facilityTotalHoldingTime (Facility a) =+ a >>> resultById facilityTotalHoldingTimeId++-- | Property 'F.facilityHoldingTime'.+facilityHoldingTime :: Facility -> T.SamplingStats+facilityHoldingTime (Facility a) =+ T.SamplingStats (a >>> resultById facilityHoldingTimeId)++-- | Property 'F.facilityInterrupted'.+facilityInterrupted :: Facility -> ResultTransform+facilityInterrupted (Facility a) =+ a >>> resultById facilityInterruptedId++-- | Represents the 'S.Storage'.+newtype Storage = Storage ResultTransform++-- | An instance of the result transformer.+instance T.ResultTransformer Storage where+ tr (Storage a) = a++-- | Property 'S.storageCapacity'.+storageCapacity :: Storage -> ResultTransform+storageCapacity (Storage a) =+ a >>> resultById storageCapacityId++-- | Property 'S.storageEmpty'.+storageEmpty :: Storage -> ResultTransform+storageEmpty (Storage a) =+ a >>> resultById storageEmptyId++-- | Property 'S.storageFull'.+storageFull :: Storage -> ResultTransform+storageFull (Storage a) =+ a >>> resultById storageFullId++-- | Property 'S.storageContent'.+storageContent :: Storage -> ResultTransform+storageContent (Storage a) =+ a >>> resultById storageContentId++-- | Property 'S.storageContentStats'.+storageContentStats :: Storage -> T.TimingStats+storageContentStats (Storage a) =+ T.TimingStats (a >>> resultById storageContentStatsId)++-- | Property 'S.storageUseCount'.+storageUseCount :: Storage -> ResultTransform+storageUseCount (Storage a) =+ a >>> resultById storageUseCountId++-- | Property 'S.storageUsedContent'.+storageUsedContent :: Storage -> ResultTransform+storageUsedContent (Storage a) =+ a >>> resultById storageUsedContentId++-- | Property 'S.storageUtilisationCount'.+storageUtilisationCount :: Storage -> ResultTransform+storageUtilisationCount (Storage a) =+ a >>> resultById storageUtilisationCountId++-- | Property 'S.storageUtilisationCountStats'.+storageUtilisationCountStats :: Storage -> T.TimingStats+storageUtilisationCountStats (Storage a) =+ T.TimingStats (a >>> resultById storageUtilisationCountStatsId)++-- | Property 'S.storageQueueCount'.+storageQueueCount :: Storage -> ResultTransform+storageQueueCount (Storage a) =+ a >>> resultById storageQueueCountId++-- | Property 'S.storageQueueCountStats'.+storageQueueCountStats :: Storage -> T.TimingStats+storageQueueCountStats (Storage a) =+ T.TimingStats (a >>> resultById storageQueueCountStatsId)++-- | Property 'S.storageTotalWaitTime'.+storageTotalWaitTime :: Storage -> ResultTransform+storageTotalWaitTime (Storage a) =+ a >>> resultById storageTotalWaitTimeId++-- | Property 'S.storageWaitTime'.+storageWaitTime :: Storage -> T.SamplingStats+storageWaitTime (Storage a) =+ T.SamplingStats (a >>> resultById storageWaitTimeId)++-- | Property 'S.storageAverageHoldingTime'.+storageAverageHoldingTime :: Storage -> ResultTransform+storageAverageHoldingTime (Storage a) =+ a >>> resultById storageAverageHoldingTimeId
+ Simulation/Aivika/GPSS/Storage.hs view
@@ -0,0 +1,514 @@++-- |+-- Module : Simulation.Aivika.GPSS.Storage+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.1+--+-- This module defines the GPSS Storage entity.+--+module Simulation.Aivika.GPSS.Storage+ (-- * Storage Type+ Storage,+ -- * Creating Storage+ newStorage,+ -- * Storage Properties+ storageCapacity,+ storageEmpty,+ storageFull,+ storageContent,+ storageContentStats,+ storageUseCount,+ storageUsedContent,+ storageUtilisationCount,+ storageUtilisationCountStats,+ storageQueueCount,+ storageQueueCountStats,+ storageTotalWaitTime,+ storageWaitTime,+ storageAverageHoldingTime,+ -- * Entering-Leaving Storage+ enterStorage,+ leaveStorage,+ leaveStorageWithinEvent,+ -- * Statistics Reset+ resetStorage,+ -- * Signals+ storageContentChanged,+ storageContentChanged_,+ storageUseCountChanged,+ storageUseCountChanged_,+ storageUsedContentChanged,+ storageUsedContentChanged_,+ storageUtilisationCountChanged,+ storageUtilisationCountChanged_,+ storageQueueCountChanged,+ storageQueueCountChanged_,+ storageWaitTimeChanged,+ storageWaitTimeChanged_,+ storageChanged_) where++import Data.IORef+import Data.Monoid+import Data.Maybe++import Control.Monad+import Control.Monad.Trans+import Control.Exception++import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Cont+import Simulation.Aivika.Internal.Process+import Simulation.Aivika.QueueStrategy+import Simulation.Aivika.Statistics+import Simulation.Aivika.Signal++import Simulation.Aivika.GPSS.Transact+import Simulation.Aivika.GPSS.TransactQueueStrategy++-- | Represents a GPSS Storage entity.+data Storage = + Storage { storageCapacity :: Int,+ -- ^ Return the storage capacity.+ storageContentRef :: IORef Int,+ storageContentStatsRef :: IORef (TimingStats Int),+ storageContentSource :: SignalSource Int,+ storageUseCountRef :: IORef Int,+ storageUseCountSource :: SignalSource Int,+ storageUsedContentRef :: IORef Int,+ storageUsedContentSource :: SignalSource Int,+ storageUtilisationCountRef :: IORef Int,+ storageUtilisationCountStatsRef :: IORef (TimingStats Int),+ storageUtilisationCountSource :: SignalSource Int,+ storageQueueCountRef :: IORef Int,+ storageQueueCountStatsRef :: IORef (TimingStats Int),+ storageQueueCountSource :: SignalSource Int,+ storageTotalWaitTimeRef :: IORef Double,+ storageWaitTimeRef :: IORef (SamplingStats Double),+ storageWaitTimeSource :: SignalSource (),+ storageDelayChain :: StrategyQueue (TransactQueueStrategy FCFS) StorageDelayedItem }++-- | Identifies an item that was delayed.+data StorageDelayedItem =+ StorageDelayedItem { delayedItemTime :: Double,+ delayedItemDecrement :: Int,+ delayedItemCont :: FrozenCont () }++instance Eq Storage where+ x == y = storageContentRef x == storageContentRef y -- unique references++-- | Create a new storage by the specified capacity.+newStorage :: Int -> Event Storage+newStorage capacity =+ Event $ \p ->+ do let r = pointRun p+ t = pointTime p+ contentRef <- newIORef capacity+ contentStatsRef <- newIORef $ returnTimingStats t capacity+ contentSource <- invokeSimulation r newSignalSource+ useCountRef <- newIORef 0+ useCountSource <- invokeSimulation r newSignalSource+ usedContentRef <- newIORef 0+ usedContentSource <- invokeSimulation r newSignalSource+ utilCountRef <- newIORef 0+ utilCountStatsRef <- newIORef $ returnTimingStats t 0+ utilCountSource <- invokeSimulation r newSignalSource+ queueCountRef <- newIORef 0+ queueCountStatsRef <- newIORef $ returnTimingStats t 0+ queueCountSource <- invokeSimulation r newSignalSource+ totalWaitTimeRef <- newIORef 0+ waitTimeRef <- newIORef emptySamplingStats+ waitTimeSource <- invokeSimulation r newSignalSource+ delayChain <- invokeSimulation r $ newStrategyQueue (TransactQueueStrategy FCFS)+ return Storage { storageCapacity = capacity,+ storageContentRef = contentRef,+ storageContentStatsRef = contentStatsRef,+ storageContentSource = contentSource,+ storageUseCountRef = useCountRef,+ storageUseCountSource = useCountSource,+ storageUsedContentRef = usedContentRef,+ storageUsedContentSource = usedContentSource,+ storageUtilisationCountRef = utilCountRef,+ storageUtilisationCountStatsRef = utilCountStatsRef,+ storageUtilisationCountSource = utilCountSource,+ storageQueueCountRef = queueCountRef,+ storageQueueCountStatsRef = queueCountStatsRef,+ storageQueueCountSource = queueCountSource,+ storageTotalWaitTimeRef = totalWaitTimeRef,+ storageWaitTimeRef = waitTimeRef,+ storageWaitTimeSource = waitTimeSource,+ storageDelayChain = delayChain }++-- | Whether the storage is empty, i.e. completely unused.+storageEmpty :: Storage -> Event Bool+storageEmpty r =+ Event $ \p ->+ do n <- readIORef (storageContentRef r)+ return (n == storageCapacity r)++-- | Whether the storage is full, i.e. completely used.+storageFull :: Storage -> Event Bool+storageFull r =+ Event $ \p ->+ do n <- readIORef (storageContentRef r)+ return (n == 0)++-- | Return the current storage content available for use.+storageContent :: Storage -> Event Int+storageContent r =+ Event $ \p -> readIORef (storageContentRef r)++-- | Return the statistics of the storage content available for use.+storageContentStats :: Storage -> Event (TimingStats Int)+storageContentStats r =+ Event $ \p -> readIORef (storageContentStatsRef r)++-- | Signal triggered when the 'storageContent' property changes.+storageContentChanged :: Storage -> Signal Int+storageContentChanged r =+ publishSignal $ storageContentSource r++-- | Signal triggered when the 'storageContent' property changes.+storageContentChanged_ :: Storage -> Signal ()+storageContentChanged_ r =+ mapSignal (const ()) $ storageContentChanged r++-- | Return the total use count of the storage.+storageUseCount :: Storage -> Event Int+storageUseCount r =+ Event $ \p -> readIORef (storageUseCountRef r)++-- | Signal triggered when the 'storageUseCount' property changes.+storageUseCountChanged :: Storage -> Signal Int+storageUseCountChanged r =+ publishSignal $ storageUseCountSource r++-- | Signal triggered when the 'storageUseCount' property changes.+storageUseCountChanged_ :: Storage -> Signal ()+storageUseCountChanged_ r =+ mapSignal (const ()) $ storageUseCountChanged r++-- | Return the total used content of the storage.+storageUsedContent :: Storage -> Event Int+storageUsedContent r =+ Event $ \p -> readIORef (storageUsedContentRef r)++-- | Signal triggered when the 'storageUsedContent' property changes.+storageUsedContentChanged :: Storage -> Signal Int+storageUsedContentChanged r =+ publishSignal $ storageUsedContentSource r++-- | Signal triggered when the 'storageUsedContent' property changes.+storageUsedContentChanged_ :: Storage -> Signal ()+storageUsedContentChanged_ r =+ mapSignal (const ()) $ storageUsedContentChanged r++-- | Return the current utilisation count of the storage.+storageUtilisationCount :: Storage -> Event Int+storageUtilisationCount r =+ Event $ \p -> readIORef (storageUtilisationCountRef r)++-- | Return the statistics for the utilisation count of the storage.+storageUtilisationCountStats :: Storage -> Event (TimingStats Int)+storageUtilisationCountStats r =+ Event $ \p -> readIORef (storageUtilisationCountStatsRef r)++-- | Signal triggered when the 'storageUtilisationCount' property changes.+storageUtilisationCountChanged :: Storage -> Signal Int+storageUtilisationCountChanged r =+ publishSignal $ storageUtilisationCountSource r++-- | Signal triggered when the 'storageUtilisationCount' property changes.+storageUtilisationCountChanged_ :: Storage -> Signal ()+storageUtilisationCountChanged_ r =+ mapSignal (const ()) $ storageUtilisationCountChanged r++-- | Return the current queue length of the storage.+storageQueueCount :: Storage -> Event Int+storageQueueCount r =+ Event $ \p -> readIORef (storageQueueCountRef r)++-- | Return the statistics for the queue length of the storage.+storageQueueCountStats :: Storage -> Event (TimingStats Int)+storageQueueCountStats r =+ Event $ \p -> readIORef (storageQueueCountStatsRef r)++-- | Signal triggered when the 'storageQueueCount' property changes.+storageQueueCountChanged :: Storage -> Signal Int+storageQueueCountChanged r =+ publishSignal $ storageQueueCountSource r++-- | Signal triggered when the 'storageQueueCount' property changes.+storageQueueCountChanged_ :: Storage -> Signal ()+storageQueueCountChanged_ r =+ mapSignal (const ()) $ storageQueueCountChanged r++-- | Return the total wait time of the storage.+storageTotalWaitTime :: Storage -> Event Double+storageTotalWaitTime r =+ Event $ \p -> readIORef (storageTotalWaitTimeRef r)++-- | Return the statistics for the wait time of the storage.+storageWaitTime :: Storage -> Event (SamplingStats Double)+storageWaitTime r =+ Event $ \p -> readIORef (storageWaitTimeRef r)++-- | Signal triggered when the 'storageTotalWaitTime' and 'storageWaitTime' properties change.+storageWaitTimeChanged :: Storage -> Signal (SamplingStats Double)+storageWaitTimeChanged r =+ mapSignalM (\() -> storageWaitTime r) $ storageWaitTimeChanged_ r++-- | Signal triggered when the 'storageTotalWaitTime' and 'storageWaitTime' properties change.+storageWaitTimeChanged_ :: Storage -> Signal ()+storageWaitTimeChanged_ r =+ publishSignal $ storageWaitTimeSource r++-- | Return the average holding time per unit.+storageAverageHoldingTime :: Storage -> Event Double+storageAverageHoldingTime r =+ Event $ \p ->+ do s <- readIORef (storageUtilisationCountStatsRef r)+ n <- readIORef (storageUtilisationCountRef r)+ m <- readIORef (storageUsedContentRef r)+ let t = pointTime p+ s' = addTimingStats t n s+ k = timingStatsSum s' / (fromRational $ toRational m)+ return k++-- | Enter the storage.+enterStorage :: Storage+ -- ^ the requested storage+ -> Transact a+ -- ^ a transact that makes the request+ -> Int+ -- ^ the content decrement+ -> Process ()+enterStorage r transact decrement =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let t = pointTime p+ f <- invokeEvent p $ strategyQueueNull (storageDelayChain r)+ if f+ then invokeEvent p $+ invokeCont c $+ invokeProcess pid $+ enterStorage' r transact decrement+ else do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ enterStorage r transact decrement+ invokeEvent p $+ strategyEnqueueWithPriority+ (storageDelayChain r)+ (transactPriority transact)+ (StorageDelayedItem t decrement c)+ invokeEvent p $ updateStorageQueueCount r 1+ +-- | Enter the storage.+enterStorage' :: Storage+ -- ^ the requested storage+ -> Transact a+ -- ^ a transact that makes the request+ -> Int+ -- ^ the content decrement+ -> Process ()+enterStorage' r transact decrement =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let t = pointTime p+ a <- readIORef (storageContentRef r)+ if a < decrement+ then do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ enterStorage r transact decrement+ invokeEvent p $+ strategyEnqueueWithPriority+ (storageDelayChain r)+ (transactPriority transact)+ (StorageDelayedItem t decrement c)+ invokeEvent p $ updateStorageQueueCount r 1+ else do invokeEvent p $ updateStorageWaitTime r 0+ invokeEvent p $ updateStorageContent r (- decrement)+ invokeEvent p $ updateStorageUseCount r 1+ invokeEvent p $ updateStorageUsedContent r decrement+ invokeEvent p $ updateStorageUtilisationCount r decrement+ invokeEvent p $ resumeCont c ()++-- | Leave the storage.+leaveStorage :: Storage+ -- ^ the storage to leave+ -> Int+ -- ^ the content increment+ -> Process ()+leaveStorage r increment =+ Process $ \_ ->+ Cont $ \c ->+ Event $ \p ->+ do invokeEvent p $ leaveStorageWithinEvent r increment+ invokeEvent p $ resumeCont c ()++-- | Leave the storage.+leaveStorageWithinEvent :: Storage+ -- ^ the storage to leave+ -> Int+ -- ^ the content increment+ -> Event ()+leaveStorageWithinEvent r increment =+ Event $ \p ->+ do let t = pointTime p+ invokeEvent p $ updateStorageUtilisationCount r (- increment)+ invokeEvent p $ updateStorageContent r increment+ invokeEvent p $ enqueueEvent t $ tryEnterStorage r++-- | Try to enter the storage.+tryEnterStorage :: Storage -> Event ()+tryEnterStorage r =+ Event $ \p ->+ do let t = pointTime p+ a <- readIORef (storageContentRef r)+ if a > 0+ then invokeEvent p $ letEnterStorage r+ else return ()++-- | Let enter the storage.+letEnterStorage :: Storage -> Event ()+letEnterStorage r =+ Event $ \p ->+ do let t = pointTime p+ a <- readIORef (storageContentRef r)+ when (a > storageCapacity r) $+ throwIO $+ SimulationRetry $+ "The storage content cannot exceed the limited capacity: leaveStorage'"+ x <- invokeEvent p $+ strategyQueueDeleteBy+ (storageDelayChain r)+ (\i -> delayedItemDecrement i <= a)+ case x of+ Nothing -> return ()+ Just (StorageDelayedItem t0 decrement0 c0) ->+ do invokeEvent p $ updateStorageQueueCount r (-1)+ c <- invokeEvent p $ unfreezeCont c0+ case c of+ Nothing ->+ invokeEvent p $ letEnterStorage r+ Just c ->+ do invokeEvent p $ updateStorageContent r (- decrement0)+ invokeEvent p $ updateStorageWaitTime r (t - t0)+ invokeEvent p $ updateStorageUtilisationCount r decrement0+ invokeEvent p $ updateStorageUseCount r 1+ invokeEvent p $ updateStorageUsedContent r decrement0+ invokeEvent p $ enqueueEvent t $ reenterCont c ()++-- | Signal triggered when one of the storage counters changes.+storageChanged_ :: Storage -> Signal ()+storageChanged_ r =+ storageContentChanged_ r <>+ storageUsedContentChanged_ r <>+ storageUtilisationCountChanged_ r <>+ storageQueueCountChanged_ r++-- | Update the storage content and its statistics.+updateStorageContent :: Storage -> Int -> Event ()+updateStorageContent r delta =+ Event $ \p ->+ do a <- readIORef (storageContentRef r)+ let a' = a + delta+ a' `seq` writeIORef (storageContentRef r) a'+ modifyIORef' (storageContentStatsRef r) $+ addTimingStats (pointTime p) a'+ invokeEvent p $+ triggerSignal (storageContentSource r) a'++-- | Update the storage use count.+updateStorageUseCount :: Storage -> Int -> Event ()+updateStorageUseCount r delta =+ Event $ \p ->+ do a <- readIORef (storageUseCountRef r)+ let a' = a + delta+ a' `seq` writeIORef (storageUseCountRef r) a'+ invokeEvent p $+ triggerSignal (storageUseCountSource r) a'++-- | Update the storage used content.+updateStorageUsedContent :: Storage -> Int -> Event ()+updateStorageUsedContent r delta =+ Event $ \p ->+ do a <- readIORef (storageUsedContentRef r)+ let a' = a + delta+ a' `seq` writeIORef (storageUsedContentRef r) a'+ invokeEvent p $+ triggerSignal (storageUsedContentSource r) a'++-- | Update the storage queue length and its statistics.+updateStorageQueueCount :: Storage -> Int -> Event ()+updateStorageQueueCount r delta =+ Event $ \p ->+ do a <- readIORef (storageQueueCountRef r)+ let a' = a + delta+ a' `seq` writeIORef (storageQueueCountRef r) a'+ modifyIORef' (storageQueueCountStatsRef r) $+ addTimingStats (pointTime p) a'+ invokeEvent p $+ triggerSignal (storageQueueCountSource r) a'++-- | Update the storage utilisation count and its statistics.+updateStorageUtilisationCount :: Storage -> Int -> Event ()+updateStorageUtilisationCount r delta =+ Event $ \p ->+ do a <- readIORef (storageUtilisationCountRef r)+ let a' = a + delta+ a' `seq` writeIORef (storageUtilisationCountRef r) a'+ modifyIORef' (storageUtilisationCountStatsRef r) $+ addTimingStats (pointTime p) a'+ invokeEvent p $+ triggerSignal (storageUtilisationCountSource r) a'++-- | Update the storage wait time and its statistics.+updateStorageWaitTime :: Storage -> Double -> Event ()+updateStorageWaitTime r delta =+ Event $ \p ->+ do a <- readIORef (storageTotalWaitTimeRef r)+ let a' = a + delta+ a' `seq` writeIORef (storageTotalWaitTimeRef r) a'+ modifyIORef' (storageWaitTimeRef r) $+ addSamplingStats delta+ invokeEvent p $+ triggerSignal (storageWaitTimeSource r) ()++-- | Reset the statistics.+resetStorage :: Storage -> Event ()+resetStorage r =+ Event $ \p ->+ do let t = pointTime p+ content <- readIORef (storageContentRef r)+ writeIORef (storageContentStatsRef r) $+ returnTimingStats t content+ writeIORef (storageUseCountRef r) 0+ let usedContent = storageCapacity r - content+ writeIORef (storageUsedContentRef r) usedContent+ utilCount <- readIORef (storageUtilisationCountRef r)+ writeIORef (storageUtilisationCountStatsRef r) $+ returnTimingStats t utilCount+ queueCount <- readIORef (storageQueueCountRef r)+ writeIORef (storageQueueCountStatsRef r) $+ returnTimingStats t queueCount+ writeIORef (storageTotalWaitTimeRef r) 0+ writeIORef (storageWaitTimeRef r) emptySamplingStats+ invokeEvent p $+ triggerSignal (storageUseCountSource r) 0+ invokeEvent p $+ triggerSignal (storageUsedContentSource r) usedContent+ invokeEvent p $+ triggerSignal (storageUtilisationCountSource r) utilCount+ invokeEvent p $+ triggerSignal (storageWaitTimeSource r) ()
+ Simulation/Aivika/GPSS/Transact.hs view
@@ -0,0 +1,302 @@++-- |+-- Module : Simulation.Aivika.GPSS.Transact+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines a GPSS transact.+--+module Simulation.Aivika.GPSS.Transact+ (Transact,+ transactValue,+ transactArrivalDelay,+ transactArrivalTime,+ transactPriority,+ transactAssemblySet,+ newTransact,+ splitTransact,+ assignTransactValue,+ assignTransactValueM,+ assignTransactPriority,+ takeTransact,+ releaseTransact,+ transactPreemptionBegin,+ transactPreemptionEnd,+ requireTransactProcessId,+ transferTransact,+ reactivateTransacts,+ registerTransactQueueEntry,+ unregisterTransactQueueEntry) where++import Control.Monad+import Control.Monad.Trans+import Control.Exception++import Data.IORef+import qualified Data.HashMap.Lazy as HM++import Simulation.Aivika+import Simulation.Aivika.Internal.Specs+import Simulation.Aivika.Internal.Simulation+import Simulation.Aivika.Internal.Event+import Simulation.Aivika.Internal.Cont+import Simulation.Aivika.Internal.Process++import {-# SOURCE #-} Simulation.Aivika.GPSS.Queue+import {-# SOURCE #-} Simulation.Aivika.GPSS.AssemblySet++-- | Represents a GPSS transact.+data Transact a =+ Transact { transactValue :: a,+ -- ^ The data of the transact.+ transactArrivalDelay :: Maybe Double,+ -- ^ The delay between the transacts generated.+ transactArrivalTime :: Double,+ -- ^ The time at which the transact was generated.+ transactPriority :: Int,+ -- ^ The transact priority.+ transactAssemblySetRef :: IORef (Maybe AssemblySet),+ -- ^ The assembly set.+ transactPreemptionCountRef :: IORef Int,+ -- ^ How many times the transact is preempted.+ transactProcessIdRef :: IORef (Maybe ProcessId),+ -- ^ An identifier of the process that handles the transact at present+ transactProcessContRef :: IORef (Maybe (FrozenCont ())),+ -- ^ A continuation of the process that tried to handle the transact.+ transactQueueEntryRef :: IORef (HM.HashMap Queue QueueEntry)+ -- ^ The queue entries registered by the the transact.+ }++instance Eq (Transact a) where+ x == y = (transactProcessIdRef x) == (transactProcessIdRef y)++-- | Create a new transact.+newTransact :: Arrival a+ -- ^ the arrival data+ -> Int+ -- ^ the transact priority+ -> Simulation (Transact a)+newTransact a priority =+ Simulation $ \r ->+ do r0 <- newIORef 0+ r1 <- newIORef Nothing+ r2 <- newIORef Nothing+ r3 <- newIORef HM.empty+ r4 <- newIORef Nothing+ return Transact { transactValue = arrivalValue a,+ transactArrivalDelay = arrivalDelay a,+ transactArrivalTime = arrivalTime a,+ transactPriority = priority,+ transactAssemblySetRef = r4,+ transactPreemptionCountRef = r0,+ transactProcessIdRef = r1,+ transactProcessContRef = r2,+ transactQueueEntryRef = r3+ }++-- | Split the transact.+splitTransact :: Transact a -> Simulation (Transact a)+splitTransact t =+ Simulation $ \r ->+ do r0 <- newIORef 0+ r1 <- newIORef Nothing+ r2 <- newIORef Nothing+ r3 <- newIORef HM.empty+ return Transact { transactValue = transactValue t,+ transactArrivalDelay = transactArrivalDelay t,+ transactArrivalTime = transactArrivalTime t,+ transactPriority = transactPriority t,+ transactAssemblySetRef = transactAssemblySetRef t,+ transactPreemptionCountRef = r0,+ transactProcessIdRef = r1,+ transactProcessContRef = r2,+ transactQueueEntryRef = r3+ }++-- | Return the transact assembly set.+transactAssemblySet :: Transact a -> Event AssemblySet+transactAssemblySet t =+ Event $ \p ->+ do let r = pointRun p+ x <- readIORef (transactAssemblySetRef t)+ case x of+ Just a -> return a+ Nothing ->+ do a <- invokeSimulation r newAssemblySet+ writeIORef (transactAssemblySetRef t) (Just a)+ return a++-- | Take the transact.+takeTransact :: Transact a -> Process ()+takeTransact t =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do pid0 <- readIORef (transactProcessIdRef t)+ case pid0 of+ Just pid0 ->+ throwIO $+ SimulationRetry+ "The transact is acquired by another process: takeTransact"+ Nothing ->+ do writeIORef (transactProcessIdRef t) (Just pid)+ n <- readIORef (transactPreemptionCountRef t)+ if n == 0+ then invokeEvent p $ resumeCont c ()+ else do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ takeTransact t+ writeIORef (transactProcessContRef t) (Just c)+ forM_ [1 .. n] $ \_ ->+ invokeEvent p $+ processPreemptionBegin pid++-- | Release the transact.+releaseTransact :: Transact a -> Process ()+releaseTransact t =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do pid0 <- readIORef (transactProcessIdRef t)+ case pid0 of+ Nothing ->+ throwIO $+ SimulationRetry+ "The transact is not acquired by any process: releaseTransact"+ Just pid0 | pid0 /= pid ->+ throwIO $+ SimulationRetry+ "The transact is acquired by another process: releaseTransact"+ Just pid0 ->+ do writeIORef (transactProcessIdRef t) Nothing+ writeIORef (transactProcessContRef t) Nothing+ invokeEvent p $ resumeCont c ()++-- | Preempt the computation that handles the transact.+transactPreemptionBegin :: Transact a -> Event ()+transactPreemptionBegin t =+ Event $ \p ->+ do n <- readIORef (transactPreemptionCountRef t)+ let n' = n + 1+ n' `seq` writeIORef (transactPreemptionCountRef t) n'+ pid <- readIORef (transactProcessIdRef t)+ case pid of+ Nothing -> return ()+ Just pid -> invokeEvent p $ processPreemptionBegin pid++-- | Proceed with the computation after the transact was preempted earlier.+transactPreemptionEnd :: Transact a -> Event ()+transactPreemptionEnd t =+ Event $ \p ->+ do n <- readIORef (transactPreemptionCountRef t)+ let n' = n - 1+ unless (n' >= 0) $+ throwIO $+ SimulationRetry+ "The transact preemption count cannot be negative: transactPreemptionEnd"+ n' `seq` writeIORef (transactPreemptionCountRef t) n'+ pid <- readIORef (transactProcessIdRef t)+ case pid of+ Nothing -> return ()+ Just pid ->+ do invokeEvent p $ processPreemptionEnd pid+ c <- readIORef (transactProcessContRef t)+ case c of+ Nothing -> return ()+ Just c ->+ do writeIORef (transactProcessContRef t) Nothing+ c <- invokeEvent p $ unfreezeCont c+ case c of+ Nothing -> return ()+ Just c -> invokeEvent p $ enqueueEvent (pointTime p) $ resumeCont c ()++-- | Require to return an identifier of the process associated with the transact.+requireTransactProcessId :: Transact a -> Event ProcessId+requireTransactProcessId t =+ Event $ \p ->+ do a <- readIORef (transactProcessIdRef t)+ case a of+ Nothing ->+ throwIO $+ SimulationRetry+ "The transact must be associated with any process: requireTransactProcessId"+ Just pid ->+ return pid++-- | Like the GoTo statement, it associates the transact with another process.+transferTransact :: Transact a -> Process () -> Event ()+transferTransact t transfer =+ Event $ \p ->+ do a <- readIORef (transactProcessIdRef t)+ case a of+ Nothing -> return ()+ Just pid ->+ invokeEvent p $ cancelProcessWithId pid+ writeIORef (transactProcessIdRef t) Nothing+ writeIORef (transactProcessContRef t) Nothing+ invokeEvent p $+ runProcess $+ do takeTransact t+ transferProcess transfer++-- | Register the queue entry in the transact.+registerTransactQueueEntry :: Transact a -> QueueEntry -> Event ()+registerTransactQueueEntry t e =+ Event $ \p ->+ do let q = entryQueue e+ m <- readIORef (transactQueueEntryRef t)+ case HM.lookup q m of+ Just e0 ->+ throwIO $+ SimulationRetry+ "There is already another queue entry for the specified queue: registerTransactQueueEntry"+ Nothing ->+ writeIORef (transactQueueEntryRef t) (HM.insert q e m)++-- | Unregister the queue entry from the transact.+unregisterTransactQueueEntry :: Transact a -> Queue -> Event QueueEntry+unregisterTransactQueueEntry t q =+ Event $ \p ->+ do m <- readIORef (transactQueueEntryRef t)+ case HM.lookup q m of+ Nothing ->+ throwIO $+ SimulationRetry+ "There is no queue entry for the specified queue: unregisterTransactQueueEntry"+ Just e ->+ do writeIORef (transactQueueEntryRef t) (HM.delete q m)+ return e++-- | Assign the transact value and return a new version of the same transact.+assignTransactValue :: Transact a -> (a -> b) -> Transact b+assignTransactValue t f =+ let b = f (transactValue t)+ in t { transactValue = b }++-- | Assign the transact value and return a new version of the same transact.+assignTransactValueM :: Monad c => Transact a -> (a -> c b) -> c (Transact b)+{-# INLINABLE assignTransactValue #-}+assignTransactValueM t f =+ do b <- f (transactValue t)+ return t { transactValue = b }++-- | Assign the priority and return a new version of the same transact.+assignTransactPriority :: Transact a -> Int -> Transact a+assignTransactPriority t priority =+ t { transactPriority = priority }++-- | Reactivate the transacts or transfer them to the specified computations.+reactivateTransacts :: [(Transact a, Maybe (Process ()))] -> Event ()+reactivateTransacts [] = return ()+reactivateTransacts ((t, Nothing): xs) =+ do pid <- requireTransactProcessId t+ reactivateProcess pid+ reactivateTransacts xs+reactivateTransacts ((t, Just transfer): xs) =+ do transferTransact t transfer+ reactivateTransacts xs
+ Simulation/Aivika/GPSS/TransactQueueStrategy.hs view
@@ -0,0 +1,166 @@++{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}++-- |+-- Module : Simulation.Aivika.GPSS.TransactQueueStrategy+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines a GPSS transact queue strategy.+--+module Simulation.Aivika.GPSS.TransactQueueStrategy+ (TransactQueueStrategy(..),+ transactStrategyQueueDeleteBy,+ transactStrategyQueueContainsBy) where++import Control.Monad+import Control.Monad.Trans++import Data.IORef+import qualified Data.IntMap as M++import Simulation.Aivika+import qualified Simulation.Aivika.DoubleLinkedList as DLL++-- | The transact queue strategy.+data TransactQueueStrategy s = TransactQueueStrategy s++-- | An implementation of the 'QueueStrategy' class.+instance QueueStrategy (TransactQueueStrategy s) where++ -- | A queue used by the 'TransactQueueStrategy' strategy.+ data StrategyQueue (TransactQueueStrategy s) a =+ TransactStrategyQueue { transactStrategy :: TransactQueueStrategy s,+ -- ^ the strategy itself+ transactStrategyQueue :: IORef (M.IntMap (DLL.DoubleLinkedList a))+ -- ^ the transact queue+ }++ newStrategyQueue s =+ liftIO $+ do r <- newIORef M.empty+ return $ TransactStrategyQueue s r++ strategyQueueNull q =+ liftIO $+ do m <- readIORef (transactStrategyQueue q)+ return $ M.null m++instance DequeueStrategy (TransactQueueStrategy FCFS) where++ strategyDequeue q =+ liftIO $ + do m <- readIORef (transactStrategyQueue q)+ let (k, xs) = M.findMin m+ i <- DLL.listFirst xs+ DLL.listRemoveFirst xs+ empty <- DLL.listNull xs+ when empty $+ modifyIORef (transactStrategyQueue q) $+ M.delete k+ return i++instance DequeueStrategy (TransactQueueStrategy LCFS) where++ strategyDequeue q =+ liftIO $ + do m <- readIORef (transactStrategyQueue q)+ let (k, xs) = M.findMin m+ i <- DLL.listLast xs+ DLL.listRemoveLast xs+ empty <- DLL.listNull xs+ when empty $+ modifyIORef (transactStrategyQueue q) $+ M.delete k+ return i++instance DequeueStrategy (TransactQueueStrategy s) => PriorityQueueStrategy (TransactQueueStrategy s) Int where++ {-# SPECIALISE instance PriorityQueueStrategy (TransactQueueStrategy FCFS) Int #-}+ {-# SPECIALISE instance PriorityQueueStrategy (TransactQueueStrategy LCFS) Int #-}+ + strategyEnqueueWithPriority q priority i =+ liftIO $+ do m <- readIORef (transactStrategyQueue q)+ let k = - priority+ xs = M.lookup k m+ case xs of+ Nothing ->+ do xs <- DLL.newList+ DLL.listAddLast xs i+ modifyIORef (transactStrategyQueue q) $+ M.insert k xs+ Just xs ->+ DLL.listAddLast xs i++instance DeletingQueueStrategy (TransactQueueStrategy FCFS) where++ strategyQueueDeleteBy q pred =+ liftIO $+ do m <- readIORef (transactStrategyQueue q)+ let loop [] = return Nothing+ loop ((k, xs): tail) =+ do a <- DLL.listRemoveBy xs pred+ case a of+ Nothing -> loop tail+ Just _ ->+ do empty <- DLL.listNull xs+ when empty $+ modifyIORef (transactStrategyQueue q) $+ M.delete k+ return a+ loop (M.assocs m)++ strategyQueueContainsBy q pred =+ liftIO $+ do m <- readIORef (transactStrategyQueue q)+ let loop [] = return Nothing+ loop ((k, xs): tail) =+ do a <- DLL.listContainsBy xs pred+ case a of+ Nothing -> loop tail+ Just _ -> return a+ loop (M.assocs m)++-- | Try to delete the transact by the specified priority and satisfying to the provided predicate.+transactStrategyQueueDeleteBy :: StrategyQueue (TransactQueueStrategy s) a+ -- ^ the queue+ -> Int+ -- ^ the transact priority+ -> (a -> Bool)+ -- ^ the predicate+ -> Event (Maybe a)+transactStrategyQueueDeleteBy q priority pred =+ liftIO $+ do m <- readIORef (transactStrategyQueue q)+ let k = - priority+ xs = M.lookup k m+ case xs of+ Nothing -> return Nothing+ Just xs ->+ do a <- DLL.listRemoveBy xs pred+ empty <- DLL.listNull xs+ when empty $+ modifyIORef (transactStrategyQueue q) $+ M.delete k+ return a++-- | Test whether the queue contains a transact with the specified priority satisfying the provided predicate.+transactStrategyQueueContainsBy :: StrategyQueue (TransactQueueStrategy s) a+ -- ^ the queue+ -> Int+ -- ^ the transact priority+ -> (a -> Bool)+ -- ^ the predicate+ -> Event (Maybe a)+transactStrategyQueueContainsBy q priority pred =+ liftIO $+ do m <- readIORef (transactStrategyQueue q)+ let k = - priority+ xs = M.lookup k m+ case xs of+ Nothing -> return Nothing+ Just xs -> DLL.listContainsBy xs pred
+ Simulation/Aivika/Trans/GPSS.hs view
@@ -0,0 +1,76 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module re-exports the library functionality related to the GPSS-like domain specific language.+--+module Simulation.Aivika.Trans.GPSS+ (-- * Modules+ module Simulation.Aivika.Trans.GPSS.Block,+ module Simulation.Aivika.Trans.GPSS.Block.Advance,+ module Simulation.Aivika.Trans.GPSS.Block.Assemble,+ module Simulation.Aivika.Trans.GPSS.Block.Assign,+ module Simulation.Aivika.Trans.GPSS.Block.Depart,+ module Simulation.Aivika.Trans.GPSS.Block.Enter,+ module Simulation.Aivika.Trans.GPSS.Block.Gather,+ module Simulation.Aivika.Trans.GPSS.Block.Generate,+ module Simulation.Aivika.Trans.GPSS.Block.Leave,+ module Simulation.Aivika.Trans.GPSS.Block.Link,+ module Simulation.Aivika.Trans.GPSS.Block.Loop,+ module Simulation.Aivika.Trans.GPSS.Block.Match,+ module Simulation.Aivika.Trans.GPSS.Block.Preempt,+ module Simulation.Aivika.Trans.GPSS.Block.Priority,+ module Simulation.Aivika.Trans.GPSS.Block.Queue,+ module Simulation.Aivika.Trans.GPSS.Block.Release,+ module Simulation.Aivika.Trans.GPSS.Block.Return,+ module Simulation.Aivika.Trans.GPSS.Block.Seize,+ module Simulation.Aivika.Trans.GPSS.Block.Split,+ module Simulation.Aivika.Trans.GPSS.Block.Terminate,+ module Simulation.Aivika.Trans.GPSS.Block.Test,+ module Simulation.Aivika.Trans.GPSS.Block.Transfer,+ module Simulation.Aivika.Trans.GPSS.Block.Unlink,+ module Simulation.Aivika.Trans.GPSS.AssemblySet,+ module Simulation.Aivika.Trans.GPSS.Facility,+ module Simulation.Aivika.Trans.GPSS.MatchChain,+ module Simulation.Aivika.Trans.GPSS.Results,+ module Simulation.Aivika.Trans.GPSS.Results.Locale,+ module Simulation.Aivika.Trans.GPSS.Storage,+ module Simulation.Aivika.Trans.GPSS.Transact,+ module Simulation.Aivika.Trans.GPSS.TransactQueueStrategy) where++import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.Block.Advance+import Simulation.Aivika.Trans.GPSS.Block.Assemble+import Simulation.Aivika.Trans.GPSS.Block.Assign+import Simulation.Aivika.Trans.GPSS.Block.Depart+import Simulation.Aivika.Trans.GPSS.Block.Enter+import Simulation.Aivika.Trans.GPSS.Block.Gather+import Simulation.Aivika.Trans.GPSS.Block.Generate+import Simulation.Aivika.Trans.GPSS.Block.Leave+import Simulation.Aivika.Trans.GPSS.Block.Link+import Simulation.Aivika.Trans.GPSS.Block.Loop+import Simulation.Aivika.Trans.GPSS.Block.Match+import Simulation.Aivika.Trans.GPSS.Block.Preempt+import Simulation.Aivika.Trans.GPSS.Block.Priority+import Simulation.Aivika.Trans.GPSS.Block.Queue+import Simulation.Aivika.Trans.GPSS.Block.Release+import Simulation.Aivika.Trans.GPSS.Block.Return+import Simulation.Aivika.Trans.GPSS.Block.Seize+import Simulation.Aivika.Trans.GPSS.Block.Split+import Simulation.Aivika.Trans.GPSS.Block.Terminate+import Simulation.Aivika.Trans.GPSS.Block.Test+import Simulation.Aivika.Trans.GPSS.Block.Transfer+import Simulation.Aivika.Trans.GPSS.Block.Unlink+import Simulation.Aivika.Trans.GPSS.AssemblySet+import Simulation.Aivika.Trans.GPSS.Facility+import Simulation.Aivika.Trans.GPSS.MatchChain+import Simulation.Aivika.Trans.GPSS.Results+import Simulation.Aivika.Trans.GPSS.Results.Locale+import Simulation.Aivika.Trans.GPSS.Storage+import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.TransactQueueStrategy
+ Simulation/Aivika/Trans/GPSS/AssemblySet.hs view
@@ -0,0 +1,171 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.AssemblySet+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines a GPSS assembly set.+--+module Simulation.Aivika.Trans.GPSS.AssemblySet+ (-- * Types+ AssemblySet,+ -- * Creating Assembly Set+ newAssemblySet,+ -- * Functions+ assembleTransact,+ gatherTransacts,+ -- * Properties+ transactAssembling,+ transactGathering) where++import Data.Monoid+import Data.Maybe+import Data.Hashable++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.Internal.Specs+import Simulation.Aivika.Trans.Internal.Parameter+import Simulation.Aivika.Trans.Internal.Simulation++import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.TransactQueueStrategy++-- | Represents an assembly set.+data AssemblySet m =+ AssemblySet { assemblySetSequenceNo :: Int,+ assemblySetAssemblingTransact :: Ref m (Maybe (ProcessId m)),+ assemblySetAssemblingCounter :: Ref m Int,+ assemblySetGatheringTransacts :: StrategyQueue m (TransactQueueStrategy FCFS) (ProcessId m),+ assemblySetGatheringCounter :: Ref m Int+ }++instance MonadDES m => Eq (AssemblySet m) where++ {-# INLINABLE (==) #-}+ x == y = (assemblySetAssemblingTransact x) == (assemblySetAssemblingTransact y)++instance Hashable (AssemblySet m) where+ hashWithSalt salt x = hashWithSalt salt (assemblySetSequenceNo x)++-- | Create a new assembly set.+newAssemblySet :: MonadDES m => Simulation m (AssemblySet m)+{-# INLINABLE newAssemblySet #-}+newAssemblySet =+ Simulation $ \r ->+ do let g = runGenerator r+ sequenceNo <- generateSequenceNo g+ assemblingTransact <- invokeSimulation r $ newRef Nothing+ assemblingCounter <- invokeSimulation r $ newRef 0+ gatheringTransacts <- invokeSimulation r $ newStrategyQueue (TransactQueueStrategy FCFS)+ gatheringCounter <- invokeSimulation r $ newRef 0+ return AssemblySet { assemblySetSequenceNo = sequenceNo,+ assemblySetAssemblingTransact = assemblingTransact,+ assemblySetAssemblingCounter = assemblingCounter,+ assemblySetGatheringTransacts = gatheringTransacts,+ assemblySetGatheringCounter = gatheringCounter+ }++-- | Assemble the transact by the specified number.+assembleTransact :: MonadDES m => Transact m a -> Int -> Process m ()+{-# INLINABLE assembleTransact #-}+assembleTransact t n =+ do (s, a) <-+ liftEvent $+ do s <- transactAssemblySet t+ a <- readRef (assemblySetAssemblingCounter s)+ return (s, a)+ if a == 0+ then do let n' = n - 1+ when (n' < 0) $+ throwProcess $+ SimulationRetry+ "The number of transacts must be positive: assembleTransact"+ if n' == 0+ then return ()+ else do liftEvent $+ do pid <- requireTransactProcessId t+ writeRef (assemblySetAssemblingTransact s) (Just pid)+ writeRef (assemblySetAssemblingCounter s) $! n'+ passivateProcess+ else do let a' = a - 1+ if a' == 0+ then do liftEvent $+ do Just pid <- readRef (assemblySetAssemblingTransact s)+ writeRef (assemblySetAssemblingTransact s) Nothing+ writeRef (assemblySetAssemblingCounter s) $! a'+ reactivateProcessImmediately pid+ cancelProcess+ else do liftEvent $ writeRef (assemblySetAssemblingCounter s) $! a'+ cancelProcess++-- | Gather the transacts by the specified number.+gatherTransacts :: MonadDES m => Transact m a -> Int -> Process m ()+{-# INLINABLE gatherTransacts #-}+gatherTransacts t n =+ do (s, a) <-+ liftEvent $+ do s <- transactAssemblySet t+ a <- readRef (assemblySetGatheringCounter s)+ return (s, a)+ if a == 0+ then do let n' = n - 1+ when (n' < 0) $+ throwProcess $+ SimulationRetry+ "The number of transacts must be positive: gatherTransacts"+ if n' == 0+ then return ()+ else do liftEvent $+ do pid <- requireTransactProcessId t+ strategyEnqueueWithPriority+ (assemblySetGatheringTransacts s)+ (transactPriority t)+ pid+ writeRef (assemblySetGatheringCounter s) $! n'+ passivateProcess+ else do let a' = a - 1+ liftEvent $+ do pid <- requireTransactProcessId t+ strategyEnqueueWithPriority+ (assemblySetGatheringTransacts s)+ (transactPriority t)+ pid+ writeRef (assemblySetGatheringCounter s) $! a'+ if a' == 0+ then passivateProcessBefore $+ liftEvent $+ do let loop acc =+ do f <- strategyQueueNull (assemblySetGatheringTransacts s)+ if f+ then return (reverse acc)+ else do x <- strategyDequeue (assemblySetGatheringTransacts s)+ loop (x: acc)+ act [] = return ()+ act (pid: pids') =+ do reactivateProcessImmediately pid+ yieldEvent $ act pids'+ pids <- loop []+ act pids+ else passivateProcess++-- | Test whether another transact is assembled for the corresponding assembly set.+transactAssembling :: MonadDES m => Transact m a -> Event m Bool+{-# INLINABLE transactAssembling #-}+transactAssembling t =+ do s <- transactAssemblySet t+ a <- readRef (assemblySetAssemblingCounter s)+ return (a > 0)++-- | Test whether the transacts are gathered for the corresponding assembly set.+transactGathering :: MonadDES m => Transact m a -> Event m Bool+{-# INLINABLE transactGathering #-}+transactGathering t =+ do s <- transactAssemblySet t+ a <- readRef (assemblySetGatheringCounter s)+ return (a > 0)
+ Simulation/Aivika/Trans/GPSS/AssemblySet.hs-boot view
@@ -0,0 +1,27 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.AssemblySet+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This is an hs-boot file.+--+module Simulation.Aivika.Trans.GPSS.AssemblySet+ (AssemblySet,+ newAssemblySet) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.TransactQueueStrategy++data AssemblySet m =+ AssemblySet { assemblySetSequenceNo :: Int,+ assemblySetAssemblingTransact :: Ref m (Maybe (ProcessId m)),+ assemblySetAssemblingCounter :: Ref m Int,+ assemblySetGatheringTransacts :: StrategyQueue m (TransactQueueStrategy FCFS) (ProcessId m),+ assemblySetGatheringCounter :: Ref m Int+ }++newAssemblySet :: MonadDES m => Simulation m (AssemblySet m)
+ Simulation/Aivika/Trans/GPSS/Block.hs view
@@ -0,0 +1,58 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines a GPSS block.+--+module Simulation.Aivika.Trans.GPSS.Block+ (Block(..),+ GeneratorBlock(..),+ withinBlock,+ traceBlock) where++import Control.Monad+import Control.Monad.Trans+import qualified Control.Category as C++import Simulation.Aivika.Trans++-- | Represents a GPSS block.+data Block m a b =+ Block { blockProcess :: a -> Process m b+ -- ^ Process the item.+ }++-- | Represents a GPSS generator block.+newtype GeneratorBlock m a =+ GeneratorBlock { runGeneratorBlock :: Block m a () -> Process m ()+ -- ^ Run the generator block.+ }++instance MonadDES m => C.Category (Block m) where++ {-# INLINABLE id #-}+ id = Block { blockProcess = return }++ {-# INLINABLE (.) #-}+ x . y = Block { blockProcess = \a -> do { b <- blockProcess y a; blockProcess x b } }++-- | Perform some action within the block, for example,+-- opening or inverting the 'Gate' to emulate the LOGIC block.+withinBlock :: MonadDES m+ => Process m ()+ -- ^ the action to be executed for each transact+ -> Block m a a+{-# INLINABLE withinBlock #-}+withinBlock m =+ Block { blockProcess = \a -> m >> return a }++-- | Trace the specified block.+traceBlock :: MonadDES m => String -> Block m a b -> Block m a b+{-# INLINABLE traceBlock #-}+traceBlock message x =+ Block { blockProcess = \a -> traceProcess message (blockProcess x a) }
+ Simulation/Aivika/Trans/GPSS/Block/Advance.hs view
@@ -0,0 +1,27 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Advance+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block ADVANCE.+--+module Simulation.Aivika.Trans.GPSS.Block.Advance+ (advanceBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block++-- | This is the GPSS construct+--+-- @ADVANCE A,B@+advanceBlock :: MonadDES m+ => Process m ()+ -- ^ the delay+ -> Block m a a+{-# INLINABLE advanceBlock #-}+advanceBlock p =+ Block { blockProcess = \a -> p >> return a }
+ Simulation/Aivika/Trans/GPSS/Block/Assemble.hs view
@@ -0,0 +1,29 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Assemble+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block ASSEMBLE.+--+module Simulation.Aivika.Trans.GPSS.Block.Assemble+ (assembleBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.AssemblySet+import Simulation.Aivika.Trans.GPSS.Transact++-- | This is the GPSS construct+--+-- @ASSEMBLE A@+assembleBlock :: MonadDES m+ => Int+ -- ^ the number of transacts to assemble+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE assembleBlock #-}+assembleBlock n =+ Block { blockProcess = \a -> assembleTransact a n >> return a }
+ Simulation/Aivika/Trans/GPSS/Block/Assign.hs view
@@ -0,0 +1,40 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Assign+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block ASSIGN.+--+module Simulation.Aivika.Trans.GPSS.Block.Assign+ (assignBlock,+ assignBlockM) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.Transact++-- | This is the GPSS construct+--+-- @ASSIGN A,B,C@+assignBlock :: MonadDES m+ => (a -> b)+ -- ^ the transform+ -> Block m (Transact m a) (Transact m b)+{-# INLINABLE assignBlock #-}+assignBlock f =+ Block { blockProcess = \a -> return (assignTransactValue a f) }++-- | This is the GPSS construct+--+-- @ASSIGN A,B,C@+assignBlockM :: MonadDES m =>+ (a -> Process m b)+ -- ^ the transform computation+ -> Block m (Transact m a) (Transact m b)+{-# INLINABLE assignBlockM #-}+assignBlockM f =+ Block { blockProcess = \a -> assignTransactValueM a f }
+ Simulation/Aivika/Trans/GPSS/Block/Depart.hs view
@@ -0,0 +1,31 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Depart+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block DEPART.+--+module Simulation.Aivika.Trans.GPSS.Block.Depart+ (departBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.Block+import qualified Simulation.Aivika.Trans.GPSS.Queue as Q++-- | This is the GPSS construct+--+-- @DEPART A,B@+departBlock :: MonadDES m+ => Q.Queue m+ -- ^ the queue+ -> Int+ -- ^ the content decrement+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE departBlock #-}+departBlock q decrement =+ Block { blockProcess = \a -> (liftEvent $ Q.dequeue q a decrement) >> return a }
+ Simulation/Aivika/Trans/GPSS/Block/Enter.hs view
@@ -0,0 +1,31 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Enter+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block ENTER.+--+module Simulation.Aivika.Trans.GPSS.Block.Enter+ (enterBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.Storage++-- | This is the GPSS construct+--+-- @ENTER A,B@+enterBlock :: MonadDES m+ => Storage m+ -- ^ the storage+ -> Int+ -- ^ the content decrement+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE enterBlock #-}+enterBlock r decrement =+ Block { blockProcess = \a -> enterStorage r a decrement >> return a }
+ Simulation/Aivika/Trans/GPSS/Block/Gather.hs view
@@ -0,0 +1,29 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Gather+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block GATHER.+--+module Simulation.Aivika.Trans.GPSS.Block.Gather+ (gatherBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.AssemblySet+import Simulation.Aivika.Trans.GPSS.Transact++-- | This is the GPSS construct+--+-- @GATHER A@+gatherBlock :: MonadDES m+ => Int+ -- ^ the number of transacts to gather+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE gatherBlock #-}+gatherBlock n =+ Block { blockProcess = \a -> gatherTransacts a n >> return a }
+ Simulation/Aivika/Trans/GPSS/Block/Generate.hs view
@@ -0,0 +1,57 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Generate+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block GENERATE.+--+module Simulation.Aivika.Trans.GPSS.Block.Generate+ (streamGeneratorBlock0,+ streamGeneratorBlock,+ streamGeneratorBlockM) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.Transact++-- | Return a generator block by the specified stream and priority computation.+streamGeneratorBlockM :: MonadDES m+ => Stream m (Arrival a)+ -- ^ the input stream of data+ -> Event m Int+ -- ^ the transact priority+ -> GeneratorBlock m (Transact m a)+{-# INLINABLE streamGeneratorBlockM #-}+streamGeneratorBlockM s priority =+ let loop s block =+ do (a, xs) <- runStream s+ liftEvent $+ do p <- priority+ t <- liftSimulation $ newTransact a p+ runProcess $+ do takeTransact t+ blockProcess block t+ loop xs block+ in GeneratorBlock (loop s)++-- | Return a generator block by the specified stream and priority.+streamGeneratorBlock :: MonadDES m+ => Stream m (Arrival a)+ -- ^ the input stream of data+ -> Int+ -- ^ the transact priority+ -> GeneratorBlock m (Transact m a)+{-# INLINABLE streamGeneratorBlock #-}+streamGeneratorBlock s = streamGeneratorBlockM s . return++-- | Return a generator block by the specified stream using zero priority.+streamGeneratorBlock0 :: MonadDES m+ => Stream m (Arrival a)+ -- ^ the input stream of data+ -> GeneratorBlock m (Transact m a)+{-# INLINABLE streamGeneratorBlock0 #-}+streamGeneratorBlock0 s = streamGeneratorBlock s 0
+ Simulation/Aivika/Trans/GPSS/Block/Leave.hs view
@@ -0,0 +1,31 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Leave+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block LEAVE.+--+module Simulation.Aivika.Trans.GPSS.Block.Leave+ (leaveBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.Storage++-- | This is the GPSS construct+--+-- @LEAVE A,B@+leaveBlock :: MonadDES m+ => Storage m+ -- ^ the storage+ -> Int+ -- ^ the content increment+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE leaveBlock #-}+leaveBlock r increment =+ Block { blockProcess = \a -> leaveStorage r increment >> return a }
+ Simulation/Aivika/Trans/GPSS/Block/Link.hs view
@@ -0,0 +1,40 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Link+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines an analog of the GPSS block LINK.+--+module Simulation.Aivika.Trans.GPSS.Block.Link+ (linkBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block++-- | This is an analog of the GPSS construct+--+-- @LINK A,B,C@+linkBlock :: MonadDES m+ => (a -> Process m (Either (Block m a ()) Bool))+ -- ^ try to link the transact and return either the next block to transfer+ -- or a flag indicating whether the transact process should be passivated+ -- in case of successful linking, i.e. storing in the queue+ -> Block m a a+{-# INLINABLE linkBlock #-}+linkBlock f =+ Block { blockProcess = \a ->+ do x <- f a+ case x of+ Left transfer ->+ transferProcess $+ blockProcess transfer a+ Right False ->+ return a+ Right True ->+ do passivateProcess+ return a+ }
+ Simulation/Aivika/Trans/GPSS/Block/Loop.hs view
@@ -0,0 +1,57 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Loop+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block LOOP.+--+module Simulation.Aivika.Trans.GPSS.Block.Loop+ (loopBlock,+ loopBlockM) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block++-- | This is the GPSS construct+--+-- @LOOP A,B@+loopBlock :: MonadDES m+ => (a -> (b, Bool))+ -- ^ by the specified transact return the next version+ -- of the same transact and a condition whether we should+ -- exit the loop+ -> Block m b ()+ -- ^ the block to transfer in when the condition fails+ -> Block m a b+{-# INLINABLE loopBlock #-}+loopBlock f block =+ Block { blockProcess = \a ->+ do let (b, c) = f a+ if c+ then return b+ else transferProcess (blockProcess block b)+ }++-- | This is the GPSS construct+--+-- @LOOP A,B@+loopBlockM :: MonadDES m+ => (a -> Process m (b, Bool))+ -- ^ by the specified transact return the next version+ -- of the same transact and a condition whether we should+ -- exit the loop+ -> Block m b ()+ -- ^ the block to transfer in when the condition fails+ -> Block m a b+{-# INLINABLE loopBlockM #-}+loopBlockM f block =+ Block { blockProcess = \a ->+ do (b, c) <- f a+ if c+ then return b+ else transferProcess (blockProcess block b)+ }
+ Simulation/Aivika/Trans/GPSS/Block/Match.hs view
@@ -0,0 +1,29 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Match+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block MATCH.+--+module Simulation.Aivika.Trans.GPSS.Block.Match+ (matchBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.MatchChain+import Simulation.Aivika.Trans.GPSS.Transact++-- | This is an analog of the GPSS construct+--+-- @MATCH A@+matchBlock :: MonadDES m+ => MatchChain m+ -- ^ the corresponding match chain+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE matchBlock #-}+matchBlock chain =+ Block { blockProcess = \a -> matchTransact chain a >> return a }
+ Simulation/Aivika/Trans/GPSS/Block/Preempt.hs view
@@ -0,0 +1,84 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Preempt+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block Preempt.+--+module Simulation.Aivika.Trans.GPSS.Block.Preempt+ (preemptBlock,+ PreemptBlockMode(..),+ defaultPreemptBlockMode,+ toFacilityPreemptMode,+ fromFacilityPreemptMode) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.Facility++-- | Specifies the Preempt block mode.+data PreemptBlockMode m a =+ PreemptBlockMode { preemptBlockPriorityMode :: Bool,+ -- ^ the Priority mode; otherwise, the Interrupt mode+ preemptBlockTransfer :: Maybe (Maybe Double -> Block m (Transact m a) ()),+ -- ^ where to transfer the preempted transact,+ -- passing in the remaining time from the process holding + -- computation such as the ADVANCE block+ preemptBlockRemoveMode :: Bool+ -- ^ the Remove mode+ }++-- | Convert 'PreemptBlockMode' to 'FacilityPreemptMode'.+toFacilityPreemptMode :: MonadDES m => PreemptBlockMode m a -> FacilityPreemptMode m a+{-# INLINABLE toFacilityPreemptMode #-}+toFacilityPreemptMode m =+ FacilityPreemptMode { facilityPriorityMode = preemptBlockPriorityMode m,+ facilityTransfer = transfer,+ facilityRemoveMode = preemptBlockRemoveMode m+ }+ where+ transfer =+ case preemptBlockTransfer m of+ Nothing -> Nothing+ Just f -> Just (\a dt -> blockProcess (f dt) a)++-- | Convert 'PreemptBlockMode' from 'FacilityPreemptMode'.+fromFacilityPreemptMode :: MonadDES m => FacilityPreemptMode m a -> PreemptBlockMode m a+{-# INLINABLE fromFacilityPreemptMode #-}+fromFacilityPreemptMode m =+ PreemptBlockMode { preemptBlockPriorityMode = facilityPriorityMode m,+ preemptBlockTransfer = transfer,+ preemptBlockRemoveMode = facilityRemoveMode m+ }+ where+ transfer =+ case facilityTransfer m of+ Nothing -> Nothing+ Just f -> Just (\dt -> Block $ \a -> f a dt)++-- | The default Preempt block mode.+defaultPreemptBlockMode :: MonadDES m => PreemptBlockMode m a+{-# INLINABLE defaultPreemptBlockMode #-}+defaultPreemptBlockMode =+ PreemptBlockMode { preemptBlockPriorityMode = False,+ preemptBlockTransfer = Nothing,+ preemptBlockRemoveMode = False+ }++-- | This is the GPSS construct+--+-- @PREEMPT A,B,C,D,E@+preemptBlock :: MonadDES m+ => Facility m a+ -- ^ the facility+ -> PreemptBlockMode m a+ -- ^ the Preempt block mode+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE preemptBlock #-}+preemptBlock r m =+ Block { blockProcess = \a -> preemptFacility r a (toFacilityPreemptMode m) >> return a }
+ Simulation/Aivika/Trans/GPSS/Block/Priority.hs view
@@ -0,0 +1,28 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Priority+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block PRIORITY.+--+module Simulation.Aivika.Trans.GPSS.Block.Priority+ (priorityBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.Transact++-- | This is the GPSS construct+--+-- @PRIORITY A@+priorityBlock :: MonadDES m + => Int+ -- ^ the priority+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE priorityBlock #-}+priorityBlock priority =+ Block { blockProcess = \a -> return (assignTransactPriority a priority) }
+ Simulation/Aivika/Trans/GPSS/Block/Queue.hs view
@@ -0,0 +1,31 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Queue+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block QUEUE.+--+module Simulation.Aivika.Trans.GPSS.Block.Queue+ (queueBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.Block+import qualified Simulation.Aivika.Trans.GPSS.Queue as Q++-- | This is the GPSS construct+--+-- @QUEUE A,B@+queueBlock :: MonadDES m+ => Q.Queue m+ -- ^ the queue+ -> Int+ -- ^ the content increment+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE queueBlock #-}+queueBlock q increment =+ Block { blockProcess = \a -> (liftEvent $ Q.enqueue q a increment) >> return a }
+ Simulation/Aivika/Trans/GPSS/Block/Release.hs view
@@ -0,0 +1,29 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Release+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block RELEASE.+--+module Simulation.Aivika.Trans.GPSS.Block.Release+ (releaseBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.Facility++-- | This is the GPSS construct+--+-- @RELEASE A@+releaseBlock :: MonadDES m+ => Facility m a+ -- ^ the facility+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE releaseBlock #-}+releaseBlock r =+ Block { blockProcess = \a -> releaseFacility r a >> return a }
+ Simulation/Aivika/Trans/GPSS/Block/Return.hs view
@@ -0,0 +1,29 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Return+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block RETURN.+--+module Simulation.Aivika.Trans.GPSS.Block.Return+ (returnBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.Facility++-- | This is the GPSS construct+--+-- @RETURN A@+returnBlock :: MonadDES m+ => Facility m a+ -- ^ the facility+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE returnBlock #-}+returnBlock r =+ Block { blockProcess = \a -> returnFacility r a >> return a }
+ Simulation/Aivika/Trans/GPSS/Block/Seize.hs view
@@ -0,0 +1,29 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Seize+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block SEIZE.+--+module Simulation.Aivika.Trans.GPSS.Block.Seize+ (seizeBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.Facility++-- | This is the GPSS construct+--+-- @SEIZE A@+seizeBlock :: MonadDES m+ => Facility m a+ -- ^ the facility+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE seizeBlock #-}+seizeBlock r =+ Block { blockProcess = \a -> seizeFacility r a >> return a }
+ Simulation/Aivika/Trans/GPSS/Block/Split.hs view
@@ -0,0 +1,54 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Split+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines an analog of the GPSS block SPLIT.+--+module Simulation.Aivika.Trans.GPSS.Block.Split+ (splitBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.Transact++-- | This is an analog of the GPSS construct+--+-- @SPLIT A,B,C@+--+-- Parameter @A@ is a length of the list parameter passed in to the function.+-- Parameter @B@ is the list itself. If you need to define parameter @C@ then+-- you can create the blocks dynamically that could depend on the index and+-- where we could assign a new value for each new transcact after splitting.+--+-- An example is+--+-- @+-- let blocks :: [Block DIO (Transact DIO (a, Int)) ()]+-- blocks = ...+-- f :: (Int, Block DIO (Transact DIO (a, Int)) ()) -> Block DIO (Transact DIO a) ()+-- f (n, block) = assignBlock (\a -> (a, n)) >>> block+-- blocks' :: [Block DIO (Transact DIO a) ()]+-- blocks' = map f $ zip [0..] blocks+-- in splitBlock blocks'+-- @+splitBlock :: MonadDES m+ => [Block m (Transact m a) ()]+ -- ^ split and transfer new transacts to the specified blocks+ -> Block m (Transact m a) (Transact m a)+{-# INLINABLE splitBlock #-}+splitBlock blocks =+ Block { blockProcess = \a ->+ do let loop [] = return ()+ loop (transfer: transfers) =+ do a' <- liftSimulation $ splitTransact a+ transferTransact a' $+ blockProcess transfer a'+ loop transfers+ liftEvent $ loop blocks+ return a+ }
+ Simulation/Aivika/Trans/GPSS/Block/Terminate.hs view
@@ -0,0 +1,74 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Terminate+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block TERMINATE.+--+module Simulation.Aivika.Trans.GPSS.Block.Terminate+ (terminateBlock,+ terminateBlockByCount,+ terminateBlockByCountM) where++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block++-- | This is the GPSS construct+--+-- @TERMINATE@+terminateBlock :: MonadDES m => Block m a ()+{-# INLINABLE terminateBlock #-}+terminateBlock =+ Block { blockProcess = \a -> return () }++-- | This is the GPSS construct+--+-- @TERMINATE Count@+terminateBlockByCountM :: MonadDES m+ => Ref m Int+ -- ^ the counter+ -> Event m Int+ -- ^ the computation of decrement+ -> Block m a ()+{-# INLINABLE terminateBlockByCountM #-}+terminateBlockByCountM counter decrement =+ Block { blockProcess = \a -> action }+ where+ action = + liftEvent $+ do i <- decrement+ n <- readRef counter+ let n' = n - i+ n' `seq` writeRef counter n'+ when (n' <= 0) $+ throwEvent $+ SimulationAbort "Terminated by exceeding the counter"++-- | This is the GPSS construct+--+-- @TERMINATE Count@+terminateBlockByCount :: MonadDES m+ => Ref m Int+ -- ^ the counter+ -> Int+ -- ^ the decrement+ -> Block m a ()+{-# INLINABLE terminateBlockByCount #-}+terminateBlockByCount counter i =+ Block { blockProcess = \a -> action }+ where+ action = + liftEvent $+ do n <- readRef counter+ let n' = n - i+ n' `seq` writeRef counter n'+ when (n' <= 0) $+ throwEvent $+ SimulationAbort "Terminated by exceeding the counter"
+ Simulation/Aivika/Trans/GPSS/Block/Test.hs view
@@ -0,0 +1,101 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Test+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block TEST.+--+module Simulation.Aivika.Trans.GPSS.Block.Test+ (awaitingTestBlock,+ awaitingTestBlockM,+ transferringTestBlock,+ transferringTestBlockM) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block++-- | This is the GPSS construct+--+-- @TEST O A,B@+awaitingTestBlock :: MonadDES m+ => (a -> Signalable m Bool)+ -- ^ by the specified transact return+ -- a test condition and signal that notifies+ -- about changing the condition+ -> Block m a a+{-# INLINABLE awaitingTestBlock #-}+awaitingTestBlock f =+ Block { blockProcess = \a ->+ do let s = f a+ loop =+ do f <- liftEvent $ readSignalable s+ if f+ then return ()+ else do processAwait $ signalableChanged_ s+ loop+ loop+ return a+ }++-- | This is the GPSS construct+--+-- @TEST O A,B@+awaitingTestBlockM :: MonadDES m+ => (a -> Process m (Signalable m Bool))+ -- ^ by the specified transact return+ -- a test condition and signal that notifies+ -- about changing the condition+ -> Block m a a+{-# INLINABLE awaitingTestBlockM #-}+awaitingTestBlockM f =+ Block { blockProcess = \a ->+ do s <- f a+ let loop =+ do f <- liftEvent $ readSignalable s+ if f+ then return ()+ else do processAwait $ signalableChanged_ s+ loop+ loop+ return a+ }++-- | This is the GPSS construct+--+-- @TEST O A,B,C@+transferringTestBlock :: MonadDES m+ => (a -> Bool)+ -- ^ the predicate+ -> Block m a ()+ -- ^ the block to transfer in when the condition fails+ -> Block m a a+{-# INLINABLE transferringTestBlock #-}+transferringTestBlock pred block =+ Block { blockProcess = \a ->+ do let f = pred a+ if f+ then return a+ else transferProcess (blockProcess block a)+ }++-- | This is the GPSS construct+--+-- @TEST O A,B,C@+transferringTestBlockM :: MonadDES m+ => (a -> Process m Bool)+ -- ^ the predicate+ -> Block m a ()+ -- ^ the block to transfer in when the condition fails+ -> Block m a a+{-# INLINABLE transferringTestBlockM #-}+transferringTestBlockM pred block =+ Block { blockProcess = \a ->+ do f <- pred a+ if f+ then return a+ else transferProcess (blockProcess block a)+ }
+ Simulation/Aivika/Trans/GPSS/Block/Transfer.hs view
@@ -0,0 +1,27 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Transfer+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines the GPSS block TRANSFER.+--+module Simulation.Aivika.Trans.GPSS.Block.Transfer+ (transferBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block++-- | This is the GPSS construct+--+-- @TRANSFER ,New_Place@+transferBlock :: MonadDES m+ => Block m a ()+ -- ^ a new place+ -> Block m a b+{-# INLINABLE transferBlock #-}+transferBlock x =+ Block { blockProcess = \a -> transferProcess (blockProcess x a) }
+ Simulation/Aivika/Trans/GPSS/Block/Unlink.hs view
@@ -0,0 +1,37 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Block.Unlink+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines an analog of the GPSS block UNLINK.+--+module Simulation.Aivika.Trans.GPSS.Block.Unlink+ (unlinkBlock) where++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Block+import Simulation.Aivika.Trans.GPSS.Transact++-- | This is an analog of the GPSS construct+--+-- @UNLINK O A,B,C,D,E,F@+unlinkBlock :: MonadDES m+ => Process m [(Transact m a, Maybe (Block m (Transact m a) ()))]+ -- ^ a computation of the list of transacts to reactivate,+ -- transfering them to the specified blocks if required+ -> Block m b b+{-# INLINABLE unlinkBlock #-}+unlinkBlock m =+ Block { blockProcess = \b ->+ do let f (a, Nothing) = (a, Nothing)+ f (a, Just transfer) = (a, Just $ blockProcess transfer a)+ xs <- m+ liftEvent $+ reactivateTransacts $+ map f xs+ return b+ }
+ Simulation/Aivika/Trans/GPSS/Facility.hs view
@@ -0,0 +1,760 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Facility+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.1+--+-- This module defines the GPSS Facility entity.+--+module Simulation.Aivika.Trans.GPSS.Facility+ (-- * Facility Type+ Facility,+ FacilityPreemptMode(..),+ FacilityPreemptTransfer,+ -- * Creating Facility+ newFacility,+ -- * Facility Properties+ facilityCount,+ facilityCountStats,+ facilityCaptureCount,+ facilityUtilisationCount,+ facilityUtilisationCountStats,+ facilityQueueCount,+ facilityQueueCountStats,+ facilityTotalWaitTime,+ facilityWaitTime,+ facilityTotalHoldingTime,+ facilityHoldingTime,+ facilityInterrupted,+ -- * Seizing-Releasing and Preempting-Returning Facility+ seizeFacility,+ releaseFacility,+ preemptFacility,+ returnFacility,+ -- * Statistics Reset+ resetFacility,+ -- * Signals+ facilityCountChanged,+ facilityCountChanged_,+ facilityCaptureCountChanged,+ facilityCaptureCountChanged_,+ facilityUtilisationCountChanged,+ facilityUtilisationCountChanged_,+ facilityQueueCountChanged,+ facilityQueueCountChanged_,+ facilityWaitTimeChanged,+ facilityWaitTimeChanged_,+ facilityHoldingTimeChanged,+ facilityHoldingTimeChanged_,+ facilityChanged_) where++import Data.Monoid+import Data.Maybe++import Control.Monad+import Control.Monad.Trans+import Control.Exception++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.Internal.Specs+import Simulation.Aivika.Trans.Internal.Simulation+import Simulation.Aivika.Trans.Internal.Event+import Simulation.Aivika.Trans.Internal.Cont+import Simulation.Aivika.Trans.Internal.Process+import Simulation.Aivika.Trans.QueueStrategy+import Simulation.Aivika.Trans.Statistics+import Simulation.Aivika.Trans.Signal++import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.TransactQueueStrategy++-- | Represents a GPSS Facility entity.+data Facility m a = + Facility { facilityCountRef :: Ref m Int,+ facilityCountStatsRef :: Ref m (TimingStats Int),+ facilityCountSource :: SignalSource m Int,+ facilityCaptureCountRef :: Ref m Int,+ facilityCaptureCountSource :: SignalSource m Int,+ facilityUtilisationCountRef :: Ref m Int,+ facilityUtilisationCountStatsRef :: Ref m (TimingStats Int),+ facilityUtilisationCountSource :: SignalSource m Int,+ facilityQueueCountRef :: Ref m Int,+ facilityQueueCountStatsRef :: Ref m (TimingStats Int),+ facilityQueueCountSource :: SignalSource m Int,+ facilityTotalWaitTimeRef :: Ref m Double,+ facilityWaitTimeRef :: Ref m (SamplingStats Double),+ facilityWaitTimeSource :: SignalSource m (),+ facilityTotalHoldingTimeRef :: Ref m Double,+ facilityHoldingTimeRef :: Ref m (SamplingStats Double),+ facilityHoldingTimeSource :: SignalSource m (),+ facilityOwnerRef :: Ref m (Maybe (FacilityOwnerItem m a)),+ facilityDelayChain :: StrategyQueue m (TransactQueueStrategy FCFS) (FacilityDelayedItem m a),+ facilityInterruptChain :: StrategyQueue m (TransactQueueStrategy LCFS) (FacilityInterruptedItem m a),+ facilityPendingChain :: StrategyQueue m (TransactQueueStrategy FCFS) (FacilityPendingItem m a) }++-- | Identifies a transact item that owns the facility.+data FacilityOwnerItem m a =+ FacilityOwnerItem { ownerItemTransact :: Transact m a,+ ownerItemTime :: Double,+ ownerItemPreempting :: Bool,+ ownerItemInterrupting :: Bool,+ ownerItemAccHoldingTime :: Double }++-- | Idenitifies a transact item that was delayed.+data FacilityDelayedItem m a =+ FacilityDelayedItem { delayedItemTransact :: Transact m a,+ delayedItemTime :: Double,+ delayedItemPreempting :: Bool,+ delayedItemInterrupting :: Bool,+ delayedItemCont :: FrozenCont m () }++-- | Idenitifies a transact item that was interrupted.+data FacilityInterruptedItem m a =+ FacilityInterruptedItem { interruptedItemTransact :: Transact m a,+ interruptedItemTime :: Double,+ interruptedItemPreempting :: Bool,+ interruptedItemInterrupting :: Bool,+ interruptedItemRemainingTime :: Maybe Double,+ interruptedItemTransfer :: Maybe (FacilityPreemptTransfer m a),+ interruptedItemAccHoldingTime :: Double }++-- | Idenitifies a transact item which is pending.+data FacilityPendingItem m a =+ FacilityPendingItem { pendingItemTransact :: Transact m a,+ pendingItemTime :: Double,+ pendingItemPreempting :: Bool,+ pendingItemInterrupting :: Bool,+ pendingItemCont :: FrozenCont m () }++instance MonadDES m => Eq (Facility m a) where+ x == y = facilityCountRef x == facilityCountRef y -- unique references++-- | The facility preemption mode.+data FacilityPreemptMode m a =+ FacilityPreemptMode { facilityPriorityMode :: Bool,+ -- ^ the Priority mode; otherwise, the Interrupt mode+ facilityTransfer :: Maybe (FacilityPreemptTransfer m a),+ -- ^ where to transfer the preempted transact,+ -- passing in the remaining time from the process holding+ -- computation such as the ADVANCE block+ facilityRemoveMode :: Bool+ -- ^ the Remove mode+ }++-- | Proceed with the computation by the specified preempted transact+-- and remaining time from the process holding computation such as the ADVANCE block.+type FacilityPreemptTransfer m a = Transact m a -> Maybe Double -> Process m ()++-- | The default facility preemption mode.+defaultFacilityPreemptMode :: FacilityPreemptMode m a+defaultFacilityPreemptMode =+ FacilityPreemptMode { facilityPriorityMode = False,+ facilityTransfer = Nothing,+ facilityRemoveMode = False+ }++-- | Create a new facility.+newFacility :: MonadDES m => Event m (Facility m a)+{-# INLINABLE newFacility #-}+newFacility =+ Event $ \p ->+ do let r = pointRun p+ t = pointTime p+ countRef <- invokeSimulation r $ newRef 1+ countStatsRef <- invokeSimulation r $ newRef $ returnTimingStats t 1+ countSource <- invokeSimulation r newSignalSource+ captureCountRef <- invokeSimulation r $ newRef 0+ captureCountSource <- invokeSimulation r newSignalSource+ utilCountRef <- invokeSimulation r $ newRef 0+ utilCountStatsRef <- invokeSimulation r $ newRef $ returnTimingStats t 0+ utilCountSource <- invokeSimulation r newSignalSource+ queueCountRef <- invokeSimulation r $ newRef 0+ queueCountStatsRef <- invokeSimulation r $ newRef $ returnTimingStats t 0+ queueCountSource <- invokeSimulation r newSignalSource+ totalWaitTimeRef <- invokeSimulation r $ newRef 0+ waitTimeRef <- invokeSimulation r $ newRef emptySamplingStats+ waitTimeSource <- invokeSimulation r newSignalSource+ totalHoldingTimeRef <- invokeSimulation r $ newRef 0+ holdingTimeRef <- invokeSimulation r $ newRef emptySamplingStats+ holdingTimeSource <- invokeSimulation r newSignalSource+ ownerRef <- invokeSimulation r $ newRef Nothing+ delayChain <- invokeSimulation r $ newStrategyQueue (TransactQueueStrategy FCFS)+ interruptChain <- invokeSimulation r $ newStrategyQueue (TransactQueueStrategy LCFS)+ pendingChain <- invokeSimulation r $ newStrategyQueue (TransactQueueStrategy FCFS)+ return Facility { facilityCountRef = countRef,+ facilityCountStatsRef = countStatsRef,+ facilityCountSource = countSource,+ facilityCaptureCountRef = captureCountRef,+ facilityCaptureCountSource = captureCountSource,+ facilityUtilisationCountRef = utilCountRef,+ facilityUtilisationCountStatsRef = utilCountStatsRef,+ facilityUtilisationCountSource = utilCountSource,+ facilityQueueCountRef = queueCountRef,+ facilityQueueCountStatsRef = queueCountStatsRef,+ facilityQueueCountSource = queueCountSource,+ facilityTotalWaitTimeRef = totalWaitTimeRef,+ facilityWaitTimeRef = waitTimeRef,+ facilityWaitTimeSource = waitTimeSource,+ facilityTotalHoldingTimeRef = totalHoldingTimeRef,+ facilityHoldingTimeRef = holdingTimeRef,+ facilityHoldingTimeSource = holdingTimeSource,+ facilityOwnerRef = ownerRef,+ facilityDelayChain = delayChain,+ facilityInterruptChain = interruptChain,+ facilityPendingChain = pendingChain }++-- | Return the current available count of the facility.+facilityCount :: MonadDES m => Facility m a -> Event m Int+{-# INLINABLE facilityCount #-}+facilityCount r =+ Event $ \p -> invokeEvent p $ readRef (facilityCountRef r)++-- | Return the statistics for the available count of the facility.+facilityCountStats :: MonadDES m => Facility m a -> Event m (TimingStats Int)+{-# INLINABLE facilityCountStats #-}+facilityCountStats r =+ Event $ \p -> invokeEvent p $ readRef (facilityCountStatsRef r)++-- | Signal triggered when the 'facilityCount' property changes.+facilityCountChanged :: MonadDES m => Facility m a -> Signal m Int+{-# INLINABLE facilityCountChanged #-}+facilityCountChanged r =+ publishSignal $ facilityCountSource r++-- | Signal triggered when the 'facilityCount' property changes.+facilityCountChanged_ :: MonadDES m => Facility m a -> Signal m ()+{-# INLINABLE facilityCountChanged_ #-}+facilityCountChanged_ r =+ mapSignal (const ()) $ facilityCountChanged r++-- | Return the current capture count of the facility.+facilityCaptureCount :: MonadDES m => Facility m a -> Event m Int+{-# INLINABLE facilityCaptureCount #-}+facilityCaptureCount r =+ Event $ \p -> invokeEvent p $ readRef (facilityCaptureCountRef r)++-- | Signal triggered when the 'facilityCaptureCount' property changes.+facilityCaptureCountChanged :: MonadDES m => Facility m a -> Signal m Int+{-# INLINABLE facilityCaptureCountChanged #-}+facilityCaptureCountChanged r =+ publishSignal $ facilityCaptureCountSource r++-- | Signal triggered when the 'facilityCaptureCount' property changes.+facilityCaptureCountChanged_ :: MonadDES m => Facility m a -> Signal m ()+{-# INLINABLE facilityCaptureCountChanged_ #-}+facilityCaptureCountChanged_ r =+ mapSignal (const ()) $ facilityCaptureCountChanged r++-- | Return the current utilisation count of the facility.+facilityUtilisationCount :: MonadDES m => Facility m a -> Event m Int+{-# INLINABLE facilityUtilisationCount #-}+facilityUtilisationCount r =+ Event $ \p -> invokeEvent p $ readRef (facilityUtilisationCountRef r)++-- | Return the statistics for the utilisation count of the facility.+facilityUtilisationCountStats :: MonadDES m => Facility m a -> Event m (TimingStats Int)+{-# INLINABLE facilityUtilisationCountStats #-}+facilityUtilisationCountStats r =+ Event $ \p -> invokeEvent p $ readRef (facilityUtilisationCountStatsRef r)++-- | Signal triggered when the 'facilityUtilisationCount' property changes.+facilityUtilisationCountChanged :: MonadDES m => Facility m a -> Signal m Int+{-# INLINABLE facilityUtilisationCountChanged #-}+facilityUtilisationCountChanged r =+ publishSignal $ facilityUtilisationCountSource r++-- | Signal triggered when the 'facilityUtilisationCount' property changes.+facilityUtilisationCountChanged_ :: MonadDES m => Facility m a -> Signal m ()+{-# INLINABLE facilityUtilisationCountChanged_ #-}+facilityUtilisationCountChanged_ r =+ mapSignal (const ()) $ facilityUtilisationCountChanged r++-- | Return the current queue length of the facility.+facilityQueueCount :: MonadDES m => Facility m a -> Event m Int+{-# INLINABLE facilityQueueCount #-}+facilityQueueCount r =+ Event $ \p -> invokeEvent p $ readRef (facilityQueueCountRef r)++-- | Return the statistics for the queue length of the facility.+facilityQueueCountStats :: MonadDES m => Facility m a -> Event m (TimingStats Int)+{-# INLINABLE facilityQueueCountStats #-}+facilityQueueCountStats r =+ Event $ \p -> invokeEvent p $ readRef (facilityQueueCountStatsRef r)++-- | Signal triggered when the 'facilityQueueCount' property changes.+facilityQueueCountChanged :: MonadDES m => Facility m a -> Signal m Int+{-# INLINABLE facilityQueueCountChanged #-}+facilityQueueCountChanged r =+ publishSignal $ facilityQueueCountSource r++-- | Signal triggered when the 'facilityQueueCount' property changes.+facilityQueueCountChanged_ :: MonadDES m => Facility m a -> Signal m ()+{-# INLINABLE facilityQueueCountChanged_ #-}+facilityQueueCountChanged_ r =+ mapSignal (const ()) $ facilityQueueCountChanged r++-- | Return the total wait time of the facility.+facilityTotalWaitTime :: MonadDES m => Facility m a -> Event m Double+{-# INLINABLE facilityTotalWaitTime #-}+facilityTotalWaitTime r =+ Event $ \p -> invokeEvent p $ readRef (facilityTotalWaitTimeRef r)++-- | Return the statistics for the wait time of the facility.+facilityWaitTime :: MonadDES m => Facility m a -> Event m (SamplingStats Double)+{-# INLINABLE facilityWaitTime #-}+facilityWaitTime r =+ Event $ \p -> invokeEvent p $ readRef (facilityWaitTimeRef r)++-- | Signal triggered when the 'facilityTotalWaitTime' and 'facilityWaitTime' properties change.+facilityWaitTimeChanged :: MonadDES m => Facility m a -> Signal m (SamplingStats Double)+{-# INLINABLE facilityWaitTimeChanged #-}+facilityWaitTimeChanged r =+ mapSignalM (\() -> facilityWaitTime r) $ facilityWaitTimeChanged_ r++-- | Signal triggered when the 'facilityTotalWaitTime' and 'facilityWaitTime' properties change.+facilityWaitTimeChanged_ :: MonadDES m => Facility m a -> Signal m ()+{-# INLINABLE facilityWaitTimeChanged_ #-}+facilityWaitTimeChanged_ r =+ publishSignal $ facilityWaitTimeSource r++-- | Return the total holding time of the facility.+facilityTotalHoldingTime :: MonadDES m => Facility m a -> Event m Double+{-# INLINABLE facilityTotalHoldingTime #-}+facilityTotalHoldingTime r =+ Event $ \p -> invokeEvent p $ readRef (facilityTotalHoldingTimeRef r)++-- | Return the statistics for the holding time of the facility.+facilityHoldingTime :: MonadDES m => Facility m a -> Event m (SamplingStats Double)+{-# INLINABLE facilityHoldingTime #-}+facilityHoldingTime r =+ Event $ \p -> invokeEvent p $ readRef (facilityHoldingTimeRef r)++-- | Signal triggered when the 'facilityTotalHoldingTime' and 'facilityHoldingTime' properties change.+facilityHoldingTimeChanged :: MonadDES m => Facility m a -> Signal m (SamplingStats Double)+{-# INLINABLE facilityHoldingTimeChanged #-}+facilityHoldingTimeChanged r =+ mapSignalM (\() -> facilityHoldingTime r) $ facilityHoldingTimeChanged_ r++-- | Signal triggered when the 'facilityTotalHoldingTime' and 'facilityHoldingTime' properties change.+facilityHoldingTimeChanged_ :: MonadDES m => Facility m a -> Signal m ()+{-# INLINABLE facilityHoldingTimeChanged_ #-}+facilityHoldingTimeChanged_ r =+ publishSignal $ facilityHoldingTimeSource r++-- | Whether the facility is currently interrupted.+facilityInterrupted :: MonadDES m => Facility m a -> Event m Bool+{-# INLINABLE facilityInterrupted #-}+facilityInterrupted r =+ Event $ \p ->+ do x <- invokeEvent p $ readRef (facilityOwnerRef r)+ case x of+ Nothing -> return False+ Just a -> return (ownerItemPreempting a)++-- | Seize the facility.+seizeFacility :: MonadDES m+ => Facility m a+ -- ^ the requested facility+ -> Transact m a+ -- ^ the transact that tries to seize the facility+ -> Process m ()+{-# INLINABLE seizeFacility #-}+seizeFacility r transact =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let t = pointTime p+ f <- do f1 <- invokeEvent p $ strategyQueueNull (facilityDelayChain r)+ if f1+ then do f2 <- invokeEvent p $ strategyQueueNull (facilityInterruptChain r)+ if f2+ then invokeEvent p $ strategyQueueNull (facilityPendingChain r)+ else return False+ else return False+ if f+ then invokeEvent p $+ invokeCont c $+ invokeProcess pid $+ seizeFacility' r transact+ else do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ seizeFacility r transact+ invokeEvent p $+ strategyEnqueueWithPriority+ (facilityDelayChain r)+ (transactPriority transact)+ (FacilityDelayedItem transact t False False c)+ invokeEvent p $ updateFacilityQueueCount r 1++-- | Seize the facility.+seizeFacility' :: MonadDES m+ => Facility m a+ -- ^ the requested facility+ -> Transact m a+ -- ^ the transact that tries to seize the facility+ -> Process m ()+{-# INLINABLE seizeFacility' #-}+seizeFacility' r transact =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let t = pointTime p+ a <- invokeEvent p $ readRef (facilityOwnerRef r)+ case a of+ Nothing ->+ do invokeEvent p $ writeRef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t False False 0)+ invokeEvent p $ updateFacilityWaitTime r 0+ invokeEvent p $ updateFacilityCount r (-1)+ invokeEvent p $ updateFacilityCaptureCount r 1+ invokeEvent p $ updateFacilityUtilisationCount r 1+ invokeEvent p $ resumeCont c ()+ Just owner ->+ do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ seizeFacility r transact+ invokeEvent p $+ strategyEnqueueWithPriority+ (facilityDelayChain r)+ (transactPriority transact)+ (FacilityDelayedItem transact t False False c)+ invokeEvent p $ updateFacilityQueueCount r 1++-- | Preempt the facility.+preemptFacility :: MonadDES m+ => Facility m a+ -- ^ the requested facility+ -> Transact m a+ -- ^ the transact that tries to preempt the facility+ -> FacilityPreemptMode m a+ -- ^ the Preempt mode+ -> Process m ()+{-# INLINABLE preemptFacility #-}+preemptFacility r transact mode =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let t = pointTime p+ a <- invokeEvent p $ readRef (facilityOwnerRef r)+ case a of+ Nothing ->+ do invokeEvent p $ writeRef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t True False 0)+ invokeEvent p $ updateFacilityWaitTime r 0+ invokeEvent p $ updateFacilityCount r (-1)+ invokeEvent p $ updateFacilityCaptureCount r 1+ invokeEvent p $ updateFacilityUtilisationCount r 1+ invokeEvent p $ resumeCont c ()+ Just owner@(FacilityOwnerItem transact0 t0 preempting0 interrupting0 acc0)+ | (not $ facilityPriorityMode mode) && interrupting0 ->+ do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ preemptFacility r transact mode+ invokeEvent p $+ strategyEnqueueWithPriority+ (facilityPendingChain r)+ (transactPriority transact)+ (FacilityPendingItem transact t True True c)+ invokeEvent p $ updateFacilityQueueCount r 1+ Just owner@(FacilityOwnerItem transact0 t0 preempting0 interrupting0 acc0)+ | facilityPriorityMode mode && (transactPriority transact <= transactPriority transact0) ->+ do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ preemptFacility r transact mode+ invokeEvent p $+ strategyEnqueueWithPriority+ (facilityDelayChain r)+ (transactPriority transact)+ (FacilityDelayedItem transact t True True c)+ invokeEvent p $ updateFacilityQueueCount r 1+ Just owner@(FacilityOwnerItem transact0 t0 preempting0 interrupting0 acc0)+ | (not $ facilityRemoveMode mode) ->+ do invokeEvent p $ writeRef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t True True 0)+ pid0 <- invokeEvent p $ requireTransactProcessId transact0+ t2 <- invokeEvent p $ processInterruptionTime pid0+ let dt0 = fmap (\x -> x - t) t2+ invokeEvent p $+ strategyEnqueueWithPriority+ (facilityInterruptChain r)+ (transactPriority transact0)+ (FacilityInterruptedItem transact0 t preempting0 interrupting0 dt0 (facilityTransfer mode) (acc0 + (t - t0)))+ invokeEvent p $ updateFacilityQueueCount r 1+ invokeEvent p $ updateFacilityWaitTime r 0+ invokeEvent p $ updateFacilityCaptureCount r 1+ invokeEvent p $ transactPreemptionBegin transact0+ invokeEvent p $ resumeCont c ()+ Just owner@(FacilityOwnerItem transact0 t0 preempting0 interruptin0 acc0)+ | facilityRemoveMode mode ->+ do invokeEvent p $ writeRef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t True True 0)+ pid0 <- invokeEvent p $ requireTransactProcessId transact0+ t2 <- invokeEvent p $ processInterruptionTime pid0+ let dt0 = fmap (\x -> x - t) t2+ invokeEvent p $ updateFacilityWaitTime r 0+ invokeEvent p $ updateFacilityCaptureCount r 1+ invokeEvent p $ updateFacilityHoldingTime r (acc0 + (t - t0))+ case facilityTransfer mode of+ Nothing ->+ throwComp $+ SimulationRetry+ "The transfer destination is not specified for the removed preempted transact: preemptFacility"+ Just transfer ->+ invokeEvent p $ transferTransact transact0 (transfer transact0 dt0)+ invokeEvent p $ resumeCont c ()++-- | Return the facility by the active transact.+returnFacility :: MonadDES m+ => Facility m a+ -- ^ the facility to return+ -> Transact m a+ -- ^ the active transact that tries to return the facility+ -> Process m ()+{-# INLINABLE returnFacility #-}+returnFacility r transact = releaseFacility' r transact True ++-- | Release the facility by the active transact.+releaseFacility :: MonadDES m+ => Facility m a+ -- ^ the facility to release+ -> Transact m a+ -- ^ the active transact that tries to release the facility+ -> Process m ()+{-# INLINABLE releaseFacility #-}+releaseFacility r transact = releaseFacility' r transact False ++-- | Release the facility by the active transact.+releaseFacility' :: MonadDES m+ => Facility m a+ -- ^ the facility to release+ -> Transact m a+ -- ^ the active transact that tries to release the facility+ -> Bool+ -- ^ whether the transact is preempting+ -> Process m ()+{-# INLINABLE releaseFacility' #-}+releaseFacility' r transact preempting = + Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let t = pointTime p+ a <- invokeEvent p $ readRef (facilityOwnerRef r)+ case a of+ Nothing ->+ throwComp $+ SimulationRetry+ "There is no owner of the facility: releaseFacility'"+ Just owner@(FacilityOwnerItem transact0 t0 preempting0 interrupting0 acc0) | transact0 == transact && preempting0 /= preempting ->+ throwComp $+ SimulationRetry+ "The mismatch use of releaseFacility and returnFacility: releaseFacility'"+ Just owner@(FacilityOwnerItem transact0 t0 preempting0 interrupting0 acc0) | transact0 == transact ->+ do invokeEvent p $ writeRef (facilityOwnerRef r) Nothing+ invokeEvent p $ updateFacilityUtilisationCount r (-1)+ invokeEvent p $ updateFacilityHoldingTime r (acc0 + (t - t0))+ invokeEvent p $ updateFacilityCount r 1+ invokeEvent p $ enqueueEvent t $ tryCaptureFacility r+ invokeEvent p $ resumeCont c ()+ Just owner ->+ throwComp $+ SimulationRetry+ "The facility has another owner: releaseFacility'"++-- | Try to capture the facility.+tryCaptureFacility :: MonadDES m => Facility m a -> Event m ()+{-# INLINABLE tryCaptureFacility #-}+tryCaptureFacility r =+ Event $ \p ->+ do let t = pointTime p+ a <- invokeEvent p $ readRef (facilityOwnerRef r)+ case a of+ Nothing ->+ invokeEvent p $ captureFacility r+ Just owner -> return ()++-- | Find another owner of the facility.+captureFacility :: MonadDES m => Facility m a -> Event m ()+{-# INLINABLE captureFacility #-}+captureFacility r =+ Event $ \p ->+ do let t = pointTime p+ f <- invokeEvent p $ strategyQueueNull (facilityPendingChain r)+ if not f+ then do FacilityPendingItem transact t0 preempting interrupting c0 <- invokeEvent p $ strategyDequeue (facilityPendingChain r)+ invokeEvent p $ updateFacilityQueueCount r (-1)+ c <- invokeEvent p $ unfreezeCont c0+ case c of+ Nothing ->+ invokeEvent p $ captureFacility r+ Just c ->+ do invokeEvent p $ writeRef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t preempting interrupting 0)+ invokeEvent p $ updateFacilityWaitTime r (t - t0)+ invokeEvent p $ updateFacilityUtilisationCount r 1+ invokeEvent p $ updateFacilityCaptureCount r 1+ invokeEvent p $ updateFacilityCount r (-1)+ invokeEvent p $ enqueueEvent t $ reenterCont c ()+ else do f <- invokeEvent p $ strategyQueueNull (facilityInterruptChain r)+ if not f+ then do FacilityInterruptedItem transact t0 preempting interrupting dt0 transfer0 acc0 <- invokeEvent p $ strategyDequeue (facilityInterruptChain r)+ pid <- invokeEvent p $ requireTransactProcessId transact+ invokeEvent p $ updateFacilityQueueCount r (-1)+ f <- invokeEvent p $ processCancelled pid+ case f of+ True ->+ invokeEvent p $ captureFacility r+ False ->+ do invokeEvent p $ writeRef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t preempting interrupting acc0)+ invokeEvent p $ updateFacilityWaitTime r (t - t0)+ invokeEvent p $ updateFacilityUtilisationCount r 1+ invokeEvent p $ updateFacilityCount r (-1)+ case transfer0 of+ Nothing -> return ()+ Just transfer ->+ invokeEvent p $ transferTransact transact (transfer transact dt0)+ invokeEvent p $ transactPreemptionEnd transact+ else do f <- invokeEvent p $ strategyQueueNull (facilityDelayChain r)+ if not f+ then do FacilityDelayedItem transact t0 preempting interrupting c0 <- invokeEvent p $ strategyDequeue (facilityDelayChain r)+ invokeEvent p $ updateFacilityQueueCount r (-1)+ c <- invokeEvent p $ unfreezeCont c0+ case c of+ Nothing ->+ invokeEvent p $ captureFacility r+ Just c ->+ do invokeEvent p $ writeRef (facilityOwnerRef r) $ Just (FacilityOwnerItem transact t preempting interrupting 0)+ invokeEvent p $ updateFacilityWaitTime r (t - t0)+ invokeEvent p $ updateFacilityUtilisationCount r 1+ invokeEvent p $ updateFacilityCaptureCount r 1+ invokeEvent p $ updateFacilityCount r (-1)+ invokeEvent p $ enqueueEvent t $ reenterCont c ()+ else return ()++-- | Signal triggered when one of the facility counters changes.+facilityChanged_ :: MonadDES m => Facility m a -> Signal m ()+{-# INLINABLE facilityChanged_ #-}+facilityChanged_ r =+ facilityCountChanged_ r <>+ facilityCaptureCountChanged_ r <>+ facilityUtilisationCountChanged_ r <>+ facilityQueueCountChanged_ r++-- | Update the facility count and its statistics.+updateFacilityCount :: MonadDES m => Facility m a -> Int -> Event m ()+{-# INLINABLE updateFacilityCount #-}+updateFacilityCount r delta =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (facilityCountRef r)+ let a' = a + delta+ invokeEvent p $+ writeRef (facilityCountRef r) a'+ invokeEvent p $+ modifyRef (facilityCountStatsRef r) $+ addTimingStats (pointTime p) a'+ invokeEvent p $+ triggerSignal (facilityCountSource r) a'++-- | Update the facility capture count.+updateFacilityCaptureCount :: MonadDES m => Facility m a -> Int -> Event m ()+{-# INLINABLE updateFacilityCaptureCount #-}+updateFacilityCaptureCount r delta =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (facilityCaptureCountRef r)+ let a' = a + delta+ invokeEvent p $+ writeRef (facilityCaptureCountRef r) a'+ invokeEvent p $+ triggerSignal (facilityCaptureCountSource r) a'++-- | Update the facility queue length and its statistics.+updateFacilityQueueCount :: MonadDES m => Facility m a -> Int -> Event m ()+{-# INLINABLE updateFacilityQueueCount #-}+updateFacilityQueueCount r delta =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (facilityQueueCountRef r)+ let a' = a + delta+ invokeEvent p $+ writeRef (facilityQueueCountRef r) a'+ invokeEvent p $+ modifyRef (facilityQueueCountStatsRef r) $+ addTimingStats (pointTime p) a'+ invokeEvent p $+ triggerSignal (facilityQueueCountSource r) a'++-- | Update the facility utilisation count and its statistics.+updateFacilityUtilisationCount :: MonadDES m => Facility m a -> Int -> Event m ()+{-# INLINABLE updateFacilityUtilisationCount #-}+updateFacilityUtilisationCount r delta =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (facilityUtilisationCountRef r)+ let a' = a + delta+ invokeEvent p $+ writeRef (facilityUtilisationCountRef r) a'+ invokeEvent p $+ modifyRef (facilityUtilisationCountStatsRef r) $+ addTimingStats (pointTime p) a'+ invokeEvent p $+ triggerSignal (facilityUtilisationCountSource r) a'++-- | Update the facility wait time and its statistics.+updateFacilityWaitTime :: MonadDES m => Facility m a -> Double -> Event m ()+{-# INLINABLE updateFacilityWaitTime #-}+updateFacilityWaitTime r delta =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (facilityTotalWaitTimeRef r)+ let a' = a + delta+ invokeEvent p $+ writeRef (facilityTotalWaitTimeRef r) a'+ invokeEvent p $+ modifyRef (facilityWaitTimeRef r) $+ addSamplingStats delta+ invokeEvent p $+ triggerSignal (facilityWaitTimeSource r) ()++-- | Update the facility holding time and its statistics.+updateFacilityHoldingTime :: MonadDES m => Facility m a -> Double -> Event m ()+{-# INLINABLE updateFacilityHoldingTime #-}+updateFacilityHoldingTime r delta =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (facilityTotalHoldingTimeRef r)+ let a' = a + delta+ invokeEvent p $+ writeRef (facilityTotalHoldingTimeRef r) a'+ invokeEvent p $+ modifyRef (facilityHoldingTimeRef r) $+ addSamplingStats delta+ invokeEvent p $+ triggerSignal (facilityHoldingTimeSource r) ()++-- | Reset the statistics.+resetFacility :: MonadDES m => Facility m a -> Event m ()+{-# INLINABLE resetFacility #-}+resetFacility r =+ Event $ \p ->+ do let t = pointTime p+ count <- invokeEvent p $ readRef (facilityCountRef r)+ invokeEvent p $ writeRef (facilityCountStatsRef r) $+ returnTimingStats t count+ invokeEvent p $ writeRef (facilityCaptureCountRef r) 0+ utilCount <- invokeEvent p $ readRef (facilityUtilisationCountRef r)+ invokeEvent p $ writeRef (facilityUtilisationCountStatsRef r) $+ returnTimingStats t utilCount+ queueCount <- invokeEvent p $ readRef (facilityQueueCountRef r)+ invokeEvent p $ writeRef (facilityQueueCountStatsRef r) $+ returnTimingStats t queueCount+ invokeEvent p $ writeRef (facilityTotalWaitTimeRef r) 0+ invokeEvent p $ writeRef (facilityWaitTimeRef r) emptySamplingStats+ invokeEvent p $ writeRef (facilityTotalHoldingTimeRef r) 0+ invokeEvent p $ writeRef (facilityHoldingTimeRef r) emptySamplingStats+ invokeEvent p $+ triggerSignal (facilityCaptureCountSource r) 0+ invokeEvent p $+ triggerSignal (facilityWaitTimeSource r) ()+ invokeEvent p $+ triggerSignal (facilityHoldingTimeSource r) ()
+ Simulation/Aivika/Trans/GPSS/MatchChain.hs view
@@ -0,0 +1,102 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.MatchChain+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines a GPSS Match Chain.+--+module Simulation.Aivika.Trans.GPSS.MatchChain+ (MatchChain,+ newMatchChain,+ matchTransact,+ transactMatching,+ transactMatchingChanged,+ transactMatchingChangedByTransact_,+ transactMatchingChangedByAssemblySet_) where++import Control.Monad+import Control.Monad.Trans++import qualified Data.HashMap.Lazy as HM++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.AssemblySet++-- | Represents a Match Chain.+data MatchChain m =+ MatchChain { matchChainMap :: Ref m (HM.HashMap (AssemblySet m) (ProcessId m)),+ matchChainSource :: SignalSource m (AssemblySet m)+ }++-- | Create a new Match Chain.+newMatchChain :: MonadDES m => Simulation m (MatchChain m)+{-# INLINABLE newMatchChain #-}+newMatchChain =+ do map <- newRef HM.empty+ src <- newSignalSource+ return MatchChain { matchChainMap = map,+ matchChainSource = src+ }++-- | Match the transact.+matchTransact :: MonadDES m => MatchChain m -> Transact m a -> Process m ()+{-# INLINABLE matchTransact #-}+matchTransact chain t =+ do (map, set) <-+ liftEvent $+ do map <- readRef (matchChainMap chain)+ set <- transactAssemblySet t+ return (map, set)+ case HM.lookup set map of+ Just pid ->+ liftEvent $+ do modifyRef (matchChainMap chain) $+ HM.delete set+ yieldEvent $+ triggerSignal (matchChainSource chain) set+ reactivateProcess pid+ Nothing ->+ do liftEvent $+ do pid <- requireTransactProcessId t+ modifyRef (matchChainMap chain) $+ HM.insert set pid+ yieldEvent $+ triggerSignal (matchChainSource chain) set+ passivateProcess++-- | Test whether there is a matching transact.+transactMatching :: MonadDES m => MatchChain m -> AssemblySet m -> Event m Bool+{-# INLINABLE transactMatching #-}+transactMatching chain set =+ do map <- readRef (matchChainMap chain)+ return (HM.member set map)++-- | Signal each time the 'transactMatching' flag changes.+transactMatchingChangedByAssemblySet_ :: MonadDES m => MatchChain m -> AssemblySet m -> Signal m ()+{-# INLINABLE transactMatchingChangedByAssemblySet_ #-}+transactMatchingChangedByAssemblySet_ chain set =+ mapSignal (const ()) $+ filterSignal (== set) $+ transactMatchingChanged chain++-- | Signal each time the 'transactMatching' flag changes.+transactMatchingChangedByTransact_ :: MonadDES m => MatchChain m -> Transact m a -> Signal m ()+{-# INLINABLE transactMatchingChangedByTransact_ #-}+transactMatchingChangedByTransact_ chain t =+ mapSignal (const ()) $+ filterSignalM pred $+ transactMatchingChanged chain+ where pred set =+ do set' <- transactAssemblySet t+ return (set == set')++-- | Signal each time the 'transactMatching' flag changes.+transactMatchingChanged :: MonadDES m => MatchChain m -> Signal m (AssemblySet m)+{-# INLINABLE transactMatchingChanged #-}+transactMatchingChanged chain =+ publishSignal (matchChainSource chain)
+ Simulation/Aivika/Trans/GPSS/Queue.hs view
@@ -0,0 +1,376 @@++{-# LANGUAGE FlexibleContexts #-}++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Queue+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines a GPSS queue entity.+--+module Simulation.Aivika.Trans.GPSS.Queue+ (-- * Queue Types+ Queue,+ QueueEntry(..),+ -- * Creating Queue+ newQueue,+ -- * Queue Properties and Activities+ queueNull,+ queueContent,+ queueContentStats,+ enqueueCount,+ enqueueZeroEntryCount,+ queueWaitTime,+ queueNonZeroEntryWaitTime,+ queueRate,+ -- * Dequeuing and Enqueuing+ enqueue,+ dequeue,+ -- * Statistics Reset+ resetQueue,+ -- * Derived Signals for Properties+ queueNullChanged,+ queueNullChanged_,+ queueContentChanged,+ queueContentChanged_,+ enqueueCountChanged,+ enqueueCountChanged_,+ enqueueZeroEntryCountChanged,+ enqueueZeroEntryCountChanged_,+ queueWaitTimeChanged,+ queueWaitTimeChanged_,+ queueNonZeroEntryWaitTimeChanged,+ queueNonZeroEntryWaitTimeChanged_,+ queueRateChanged,+ queueRateChanged_,+ -- * Basic Signals+ enqueued,+ dequeued,+ -- * Overall Signal+ queueChanged_) where++import Data.Monoid+import Data.Maybe+import Data.Hashable++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.Internal.Specs+import Simulation.Aivika.Trans.Internal.Simulation+import Simulation.Aivika.Trans.Internal.Dynamics+import Simulation.Aivika.Trans.Internal.Event+import Simulation.Aivika.Trans.Internal.Process+import Simulation.Aivika.Trans.Signal+import Simulation.Aivika.Trans.Statistics++import Simulation.Aivika.Trans.GPSS.Transact++-- | Represents the queue entity.+data Queue m =+ Queue { queueSequenceNo :: Int,+ queueContentRef :: Ref m Int,+ queueContentStatsRef :: Ref m (TimingStats Int),+ enqueueCountRef :: Ref m Int,+ enqueueZeroEntryCountRef :: Ref m Int,+ queueWaitTimeRef :: Ref m (SamplingStats Double),+ queueNonZeroEntryWaitTimeRef :: Ref m (SamplingStats Double),+ enqueuedSource :: SignalSource m (),+ dequeuedSource :: SignalSource m ()+ }++-- | The information about queue entry.+data QueueEntry m =+ QueueEntry { entryQueue :: Queue m,+ -- ^ the entry queue+ entryEnqueueTime :: Double+ -- ^ the time of registering the queue entry+ } deriving Eq++instance MonadDES m => Eq (Queue m) where+ x == y = (queueContentRef x) == (queueContentRef y)++instance Hashable (Queue m) where+ hashWithSalt salt x = hashWithSalt salt (queueSequenceNo x)++-- | Create a new queue.+newQueue :: MonadDES m => Event m (Queue m)+{-# INLINABLE newQueue #-}+newQueue =+ do t <- liftDynamics time+ g <- liftParameter generatorParameter+ no <- liftComp $ generateSequenceNo g+ i <- liftSimulation $ newRef 0+ is <- liftSimulation $ newRef $ returnTimingStats t 0+ e <- liftSimulation $ newRef 0+ z <- liftSimulation $ newRef 0 + w <- liftSimulation $ newRef mempty+ w2 <- liftSimulation $ newRef mempty+ s1 <- liftSimulation $ newSignalSource+ s2 <- liftSimulation $ newSignalSource+ return Queue { queueSequenceNo = no,+ queueContentRef = i,+ queueContentStatsRef = is,+ enqueueCountRef = e,+ enqueueZeroEntryCountRef = z,+ queueWaitTimeRef = w,+ queueNonZeroEntryWaitTimeRef = w2,+ enqueuedSource = s1,+ dequeuedSource = s2 }+ +-- | Test whether the queue is empty.+--+-- See also 'queueNullChanged' and 'queueNullChanged_'.+queueNull :: MonadDES m => Queue m -> Event m Bool+{-# INLINABLE queueNull #-}+queueNull q =+ Event $ \p ->+ do n <- invokeEvent p $ readRef (queueContentRef q)+ return (n == 0)+ +-- | Signal when the 'queueNull' property value has changed.+queueNullChanged :: MonadDES m => Queue m -> Signal m Bool+{-# INLINABLE queueNullChanged #-}+queueNullChanged q =+ mapSignalM (const $ queueNull q) (queueNullChanged_ q)+ +-- | Signal when the 'queueNull' property value has changed.+queueNullChanged_ :: MonadDES m => Queue m -> Signal m ()+{-# INLINABLE queueNullChanged_ #-}+queueNullChanged_ = queueContentChanged_++-- | Return the current queue content.+--+-- See also 'queueContentStats', 'queueContentChanged' and 'queueContentChanged_'.+queueContent :: MonadDES m => Queue m -> Event m Int+{-# INLINABLE queueContent #-}+queueContent q =+ Event $ \p -> invokeEvent p $ readRef (queueContentRef q)++-- | Return the queue content statistics.+queueContentStats :: MonadDES m => Queue m -> Event m (TimingStats Int)+{-# INLINABLE queueContentStats #-}+queueContentStats q =+ Event $ \p -> invokeEvent p $ readRef (queueContentStatsRef q)+ +-- | Signal when the 'queueContent' property value has changed.+queueContentChanged :: MonadDES m => Queue m -> Signal m Int+{-# INLINABLE queueContentChanged #-}+queueContentChanged q =+ mapSignalM (const $ queueContent q) (queueContentChanged_ q)+ +-- | Signal when the 'queueContent' property value has changed.+queueContentChanged_ :: MonadDES m => Queue m -> Signal m ()+{-# INLINABLE queueContentChanged_ #-}+queueContentChanged_ q =+ mapSignal (const ()) (enqueued q) <>+ mapSignal (const ()) (dequeued q)++-- | Return the total number of input items that were enqueued.+--+-- See also 'enqueueCountChanged' and 'enqueueCountChanged_'.+enqueueCount :: MonadDES m => Queue m -> Event m Int+{-# INLINABLE enqueueCount #-}+enqueueCount q =+ Event $ \p -> invokeEvent p $ readRef (enqueueCountRef q)+ +-- | Signal when the 'enqueueCount' property value has changed.+enqueueCountChanged :: MonadDES m => Queue m -> Signal m Int+{-# INLINABLE enqueueCountChanged #-}+enqueueCountChanged q =+ mapSignalM (const $ enqueueCount q) (enqueueCountChanged_ q)+ +-- | Signal when the 'enqueueCount' property value has changed.+enqueueCountChanged_ :: MonadDES m => Queue m -> Signal m ()+{-# INLINABLE enqueueCountChanged_ #-}+enqueueCountChanged_ q =+ mapSignal (const ()) (enqueued q)++-- | Return the total number of zero entry items.+--+-- See also 'enqueueZeroEntryCountChanged' and 'enqueueZeroEntryCountChanged_'.+enqueueZeroEntryCount :: MonadDES m => Queue m -> Event m Int+{-# INLINABLE enqueueZeroEntryCount #-}+enqueueZeroEntryCount q =+ Event $ \p -> invokeEvent p $ readRef (enqueueZeroEntryCountRef q)+ +-- | Signal when the 'enqueueZeroEntryCount' property value has changed.+enqueueZeroEntryCountChanged :: MonadDES m => Queue m -> Signal m Int+{-# INLINABLE enqueueZeroEntryCountChanged #-}+enqueueZeroEntryCountChanged q =+ mapSignalM (const $ enqueueZeroEntryCount q) (enqueueZeroEntryCountChanged_ q)+ +-- | Signal when the 'enqueueZeroEntryCount' property value has changed.+enqueueZeroEntryCountChanged_ :: MonadDES m => Queue m -> Signal m ()+{-# INLINABLE enqueueZeroEntryCountChanged_ #-}+enqueueZeroEntryCountChanged_ q =+ mapSignal (const ()) (dequeued q)++-- | Return the wait (or residence) time.+--+-- See also 'queueWaitTimeChanged' and 'queueWaitTimeChanged_'.+queueWaitTime :: MonadDES m => Queue m -> Event m (SamplingStats Double)+{-# INLINABLE queueWaitTime #-}+queueWaitTime q =+ Event $ \p -> invokeEvent p $ readRef (queueWaitTimeRef q)+ +-- | Signal when the 'queueWaitTime' property value has changed.+queueWaitTimeChanged :: MonadDES m => Queue m -> Signal m (SamplingStats Double)+{-# INLINABLE queueWaitTimeChanged #-}+queueWaitTimeChanged q =+ mapSignalM (const $ queueWaitTime q) (queueWaitTimeChanged_ q)+ +-- | Signal when the 'queueWaitTime' property value has changed.+queueWaitTimeChanged_ :: MonadDES m => Queue m -> Signal m ()+{-# INLINABLE queueWaitTimeChanged_ #-}+queueWaitTimeChanged_ q =+ mapSignal (const ()) (dequeued q)+ +-- | Return the wait (or residence) time excluding zero entries.+--+-- See also 'queueNonZeroEntryWaitTimeChanged' and 'queueNonZeroEntryWaitTimeChanged_'.+queueNonZeroEntryWaitTime :: MonadDES m => Queue m -> Event m (SamplingStats Double)+{-# INLINABLE queueNonZeroEntryWaitTime #-}+queueNonZeroEntryWaitTime q =+ Event $ \p -> invokeEvent p $ readRef (queueNonZeroEntryWaitTimeRef q)+ +-- | Signal when the 'queueNonZeroEntryWaitTime' property value has changed.+queueNonZeroEntryWaitTimeChanged :: MonadDES m => Queue m -> Signal m (SamplingStats Double)+{-# INLINABLE queueNonZeroEntryWaitTimeChanged #-}+queueNonZeroEntryWaitTimeChanged q =+ mapSignalM (const $ queueNonZeroEntryWaitTime q) (queueNonZeroEntryWaitTimeChanged_ q)+ +-- | Signal when the 'queueNonZeroEntryWaitTime' property value has changed.+queueNonZeroEntryWaitTimeChanged_ :: MonadDES m => Queue m -> Signal m ()+{-# INLINABLE queueNonZeroEntryWaitTimeChanged_ #-}+queueNonZeroEntryWaitTimeChanged_ q =+ mapSignal (const ()) (dequeued q)++-- | Return a long-term average queue rate calculated as+-- the average queue size divided by the average wait time.+--+-- See also 'queueRateChanged' and 'queueRateChanged_'.+queueRate :: MonadDES m => Queue m -> Event m Double+{-# INLINABLE queueRate #-}+queueRate q =+ Event $ \p ->+ do x <- invokeEvent p $ readRef (queueContentStatsRef q)+ y <- invokeEvent p $ readRef (queueWaitTimeRef q)+ return (timingStatsMean x / samplingStatsMean y) + +-- | Signal when the 'queueRate' property value has changed.+queueRateChanged :: MonadDES m => Queue m -> Signal m Double+{-# INLINABLE queueRateChanged #-}+queueRateChanged q =+ mapSignalM (const $ queueRate q) (queueRateChanged_ q)+ +-- | Signal when the 'queueRate' property value has changed.+queueRateChanged_ :: MonadDES m => Queue m -> Signal m ()+{-# INLINABLE queueRateChanged_ #-}+queueRateChanged_ q =+ mapSignal (const ()) (enqueued q) <>+ mapSignal (const ()) (dequeued q)++-- | Return a signal that notifies when enqueuing an item.+enqueued:: MonadDES m => Queue m -> Signal m ()+{-# INLINABLE enqueued #-}+enqueued q = publishSignal (enqueuedSource q)++-- | Return a signal that notifies when the dequeuing the item.+dequeued :: MonadDES m => Queue m -> Signal m ()+{-# INLINABLE dequeued #-}+dequeued q = publishSignal (dequeuedSource q)++-- | Enqueue the item.+enqueue :: MonadDES m+ => Queue m+ -- ^ the queue+ -> Transact m a+ -- ^ the item to be enqueued+ -> Int+ -- ^ the content increment+ -> Event m ()+{-# INLINABLE enqueue #-}+enqueue q transact increment =+ Event $ \p ->+ do let t = pointTime p+ e = QueueEntry { entryQueue = q,+ entryEnqueueTime = t }+ n <- invokeEvent p $ readRef (enqueueCountRef q)+ let n' = n + 1+ invokeEvent p $+ writeRef (enqueueCountRef q) n'+ c <- invokeEvent p $ readRef (queueContentRef q)+ let c' = c + increment+ invokeEvent p $+ writeRef (queueContentRef q) c'+ invokeEvent p $+ modifyRef (queueContentStatsRef q) (addTimingStats t c')+ invokeEvent p $+ registerTransactQueueEntry transact e+ invokeEvent p $+ triggerSignal (enqueuedSource q) ()++-- | Dequeue the item.+dequeue :: MonadDES m+ => Queue m+ -- ^ the queue+ -> Transact m a+ -- ^ the item to be dequeued+ -> Int+ -- ^ the content decrement+ -> Event m ()+{-# INLINABLE dequeue #-}+dequeue q transact decrement =+ Event $ \p ->+ do e <- invokeEvent p $+ unregisterTransactQueueEntry transact q+ let t = pointTime p+ t0 = entryEnqueueTime e+ dt = t - t0+ c <- invokeEvent p $ readRef (queueContentRef q)+ let c' = c - decrement+ invokeEvent p $+ writeRef (queueContentRef q) c'+ invokeEvent p $+ modifyRef (queueContentStatsRef q) (addTimingStats t c')+ invokeEvent p $+ modifyRef (queueWaitTimeRef q) $+ addSamplingStats dt+ if t == t0+ then invokeEvent p $+ modifyRef (enqueueZeroEntryCountRef q) (+ 1)+ else invokeEvent p $+ modifyRef (queueNonZeroEntryWaitTimeRef q) $+ addSamplingStats dt+ invokeEvent p $+ triggerSignal (dequeuedSource q) ()++-- | Signal whenever any property of the queue changes.+--+-- The property must have the corresponded signal. There are also characteristics+-- similar to the properties but that have no signals. As a rule, such characteristics+-- already depend on the simulation time and therefore they may change at any+-- time point.+queueChanged_ :: MonadDES m => Queue m -> Signal m ()+{-# INLINABLE queueChanged_ #-}+queueChanged_ q =+ mapSignal (const ()) (enqueued q) <>+ mapSignal (const ()) (dequeued q)++-- | Reset the statistics.+resetQueue :: MonadDES m => Queue m -> Event m ()+{-# INLINABLE resetQueue #-}+resetQueue q =+ do t <- liftDynamics time+ content <- readRef (queueContentRef q)+ writeRef (queueContentStatsRef q) $+ returnTimingStats t content+ writeRef (enqueueCountRef q) 0+ writeRef (enqueueZeroEntryCountRef q) 0+ writeRef (queueWaitTimeRef q) mempty+ writeRef (queueNonZeroEntryWaitTimeRef q) mempty
+ Simulation/Aivika/Trans/GPSS/Queue.hs-boot view
@@ -0,0 +1,43 @@++{-# LANGUAGE KindSignatures #-}++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Queue+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This is an hs-boot file.+--+module Simulation.Aivika.Trans.GPSS.Queue+ (Queue,+ QueueEntry,+ entryQueue) where++import Data.Hashable++import Simulation.Aivika.Trans++data Queue m =+ Queue { queueSequenceNo :: Int,+ queueContentRef :: Ref m Int,+ queueContentStatsRef :: Ref m (TimingStats Int),+ enqueueCountRef :: Ref m Int,+ enqueueZeroEntryCountRef :: Ref m Int,+ queueWaitTimeRef :: Ref m (SamplingStats Double),+ queueNonZeroEntryWaitTimeRef :: Ref m (SamplingStats Double),+ enqueuedSource :: SignalSource m (),+ dequeuedSource :: SignalSource m ()+ }++data QueueEntry m =+ QueueEntry { entryQueue :: Queue m,+ entryEnqueueTime :: Double+ }++instance MonadDES m => Eq (Queue m)+instance Hashable (Queue m)++instance MonadDES m => Eq (QueueEntry m)
+ Simulation/Aivika/Trans/GPSS/Results.hs view
@@ -0,0 +1,183 @@++{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Results+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- The module allows exporting the simulation results from the model.+--+module Simulation.Aivika.Trans.GPSS.Results () where++import Control.Monad+import Control.Monad.Trans++import Simulation.Aivika.Trans++import qualified Simulation.Aivika.Trans.GPSS.Queue as Q+import Simulation.Aivika.Trans.GPSS.Facility+import Simulation.Aivika.Trans.GPSS.Storage+import Simulation.Aivika.Trans.GPSS.Results.Locale+ +-- | Return a source by the specified queue.+queueResultSource :: MonadDES m+ => ResultContainer (Q.Queue m) m+ -- ^ the queue container+ -> ResultSource m+queueResultSource c =+ ResultObjectSource $+ ResultObject {+ resultObjectName = resultContainerName c,+ resultObjectId = resultContainerId c,+ resultObjectTypeId = queueId,+ resultObjectSignal = resultContainerSignal c,+ resultObjectSummary = queueResultSummary c,+ resultObjectProperties = [+ resultContainerProperty c "queueNull" queueNullId Q.queueNull Q.queueNullChanged_,+ resultContainerProperty c "queueContent" queueContentId Q.queueContent Q.queueContentChanged_,+ resultContainerProperty c "queueContentStats" queueContentStatsId Q.queueContentStats Q.queueContentChanged_,+ resultContainerProperty c "enqueueCount" enqueueCountId Q.enqueueCount Q.enqueueCountChanged_,+ resultContainerProperty c "enqueueZeroEntryCount" enqueueZeroEntryCountId Q.enqueueZeroEntryCount Q.enqueueZeroEntryCountChanged_,+ resultContainerProperty c "queueWaitTime" queueWaitTimeId Q.queueWaitTime Q.queueWaitTimeChanged_,+ resultContainerProperty c "queueNonZeroEntryWaitTime" queueNonZeroEntryWaitTimeId Q.queueNonZeroEntryWaitTime Q.queueNonZeroEntryWaitTimeChanged_,+ resultContainerProperty c "queueRate" queueRateId Q.queueRate Q.queueRateChanged_ ] }++-- | Return the summary by the specified queue.+queueResultSummary :: MonadDES m =>+ ResultContainer (Q.Queue m) m+ -- ^ the queue container+ -> ResultSource m+queueResultSummary c =+ ResultObjectSource $+ ResultObject {+ resultObjectName = resultContainerName c,+ resultObjectId = resultContainerId c,+ resultObjectTypeId = queueId,+ resultObjectSignal = resultContainerSignal c,+ resultObjectSummary = queueResultSummary c,+ resultObjectProperties = [+ resultContainerProperty c "queueContentStats" queueContentStatsId Q.queueContentStats Q.queueContentChanged_,+ resultContainerProperty c "enqueueCount" enqueueCountId Q.enqueueCount Q.enqueueCountChanged_,+ resultContainerProperty c "enqueueZeroEntryCount" enqueueZeroEntryCountId Q.enqueueZeroEntryCount Q.enqueueZeroEntryCountChanged_,+ resultContainerProperty c "queueWaitTime" queueWaitTimeId Q.queueWaitTime Q.queueWaitTimeChanged_,+ resultContainerProperty c "queueNonZeroEntryWaitTime" queueNonZeroEntryWaitTimeId Q.queueNonZeroEntryWaitTime Q.queueNonZeroEntryWaitTimeChanged_,+ resultContainerProperty c "queueRate" queueRateId Q.queueRate Q.queueRateChanged_ ] }++-- | Return a source by the specified facility.+facilityResultSource :: MonadDES m =>+ ResultContainer (Facility m a) m+ -- ^ the facility container+ -> ResultSource m+facilityResultSource c =+ ResultObjectSource $+ ResultObject {+ resultObjectName = resultContainerName c,+ resultObjectId = resultContainerId c,+ resultObjectTypeId = facilityId,+ resultObjectSignal = resultContainerSignal c,+ resultObjectSummary = facilityResultSummary c,+ resultObjectProperties = [+ resultContainerProperty c "queueCount" facilityQueueCountId facilityQueueCount facilityQueueCountChanged_,+ resultContainerProperty c "queueCountStats" facilityQueueCountStatsId facilityQueueCountStats facilityQueueCountChanged_,+ resultContainerProperty c "totalWaitTime" facilityTotalWaitTimeId facilityTotalWaitTime facilityWaitTimeChanged_,+ resultContainerProperty c "waitTime" facilityWaitTimeId facilityWaitTime facilityWaitTimeChanged_,+ resultContainerProperty c "totalHoldingTime" facilityTotalHoldingTimeId facilityTotalHoldingTime facilityHoldingTimeChanged_,+ resultContainerProperty c "holdingTime" facilityHoldingTimeId facilityHoldingTime facilityHoldingTimeChanged_,+ resultContainerIntegProperty c "interrupted" facilityInterruptedId facilityInterrupted,+ resultContainerProperty c "count" facilityCountId facilityCount facilityCountChanged_,+ resultContainerProperty c "countStats" facilityCountStatsId facilityCountStats facilityCountChanged_,+ resultContainerProperty c "captureCount" facilityCaptureCountId facilityCaptureCount facilityCaptureCountChanged_,+ resultContainerProperty c "utilisationCount" facilityUtilisationCountId facilityUtilisationCount facilityUtilisationCountChanged_,+ resultContainerProperty c "utilisationCountStats" facilityUtilisationCountStatsId facilityUtilisationCountStats facilityUtilisationCountChanged_ ] }++-- | Return a summary by the specified facility.+facilityResultSummary :: MonadDES m =>+ ResultContainer (Facility m a) m+ -- ^ the facility container+ -> ResultSource m+facilityResultSummary c =+ ResultObjectSource $+ ResultObject {+ resultObjectName = resultContainerName c,+ resultObjectId = resultContainerId c,+ resultObjectTypeId = facilityId,+ resultObjectSignal = resultContainerSignal c,+ resultObjectSummary = facilityResultSummary c,+ resultObjectProperties = [+ resultContainerProperty c "queueCountStats" facilityQueueCountStatsId facilityQueueCountStats facilityQueueCountChanged_,+ resultContainerProperty c "waitTime" facilityWaitTimeId facilityWaitTime facilityWaitTimeChanged_,+ resultContainerProperty c "holdingTime" facilityHoldingTimeId facilityHoldingTime facilityHoldingTimeChanged_,+ resultContainerProperty c "countStats" facilityCountStatsId facilityCountStats facilityCountChanged_,+ resultContainerProperty c "captureCount" facilityCaptureCountId facilityCaptureCount facilityCaptureCountChanged_,+ resultContainerProperty c "utilisationCountStats" facilityUtilisationCountStatsId facilityUtilisationCountStats facilityUtilisationCountChanged_ ] }++-- | Return a source by the specified storage.+storageResultSource :: MonadDES m+ => ResultContainer (Storage m) m+ -- ^ the storage container+ -> ResultSource m+storageResultSource c =+ ResultObjectSource $+ ResultObject {+ resultObjectName = resultContainerName c,+ resultObjectId = resultContainerId c,+ resultObjectTypeId = storageId,+ resultObjectSignal = resultContainerSignal c,+ resultObjectSummary = storageResultSummary c,+ resultObjectProperties = [+ resultContainerConstProperty c "capacity" storageCapacityId storageCapacity,+ resultContainerIntegProperty c "empty" storageEmptyId storageEmpty,+ resultContainerIntegProperty c "full" storageFullId storageFull,+ resultContainerProperty c "queueCount" storageQueueCountId storageQueueCount storageQueueCountChanged_,+ resultContainerProperty c "queueCountStats" storageQueueCountStatsId storageQueueCountStats storageQueueCountChanged_,+ resultContainerProperty c "totalWaitTime" storageTotalWaitTimeId storageTotalWaitTime storageWaitTimeChanged_,+ resultContainerProperty c "waitTime" storageWaitTimeId storageWaitTime storageWaitTimeChanged_,+ resultContainerIntegProperty c "averageHoldingTime" storageAverageHoldingTimeId storageAverageHoldingTime,+ resultContainerProperty c "content" storageContentId storageContent storageContentChanged_,+ resultContainerProperty c "contentStats" storageContentStatsId storageContentStats storageContentChanged_,+ resultContainerProperty c "useCount" storageUseCountId storageUseCount storageUseCountChanged_,+ resultContainerProperty c "usedContent" storageUsedContentId storageUsedContent storageUsedContentChanged_,+ resultContainerProperty c "utilisationCount" storageUtilisationCountId storageUtilisationCount storageUtilisationCountChanged_,+ resultContainerProperty c "utilisationCountStats" storageUtilisationCountStatsId storageUtilisationCountStats storageUtilisationCountChanged_ ] }++-- | Return a summary by the specified storage.+storageResultSummary :: MonadDES m+ => ResultContainer (Storage m) m+ -- ^ the storage container+ -> ResultSource m+storageResultSummary c =+ ResultObjectSource $+ ResultObject {+ resultObjectName = resultContainerName c,+ resultObjectId = resultContainerId c,+ resultObjectTypeId = storageId,+ resultObjectSignal = resultContainerSignal c,+ resultObjectSummary = storageResultSummary c,+ resultObjectProperties = [+ resultContainerConstProperty c "capacity" storageCapacityId storageCapacity,+ resultContainerProperty c "queueCountStats" storageQueueCountStatsId storageQueueCountStats storageQueueCountChanged_,+ resultContainerProperty c "waitTime" storageWaitTimeId storageWaitTime storageWaitTimeChanged_,+ resultContainerIntegProperty c "averageHoldingTime" storageAverageHoldingTimeId storageAverageHoldingTime,+ resultContainerProperty c "contentStats" storageContentStatsId storageContentStats storageContentChanged_,+ resultContainerProperty c "useCount" storageUseCountId storageUseCount storageUseCountChanged_,+ resultContainerProperty c "usedContent" storageUsedContentId storageUsedContent storageUsedContentChanged_,+ resultContainerProperty c "utilisationCountStats" storageUtilisationCountStatsId storageUtilisationCountStats storageUtilisationCountChanged_ ] }++instance MonadDES m => ResultProvider (Q.Queue m) m where++ resultSource' name i m =+ queueResultSource $ ResultContainer name i m (ResultSignal $ Q.queueChanged_ m)++instance MonadDES m => ResultProvider (Facility m a) m where++ resultSource' name i m =+ facilityResultSource $ ResultContainer name i m (ResultSignal $ facilityChanged_ m)++instance MonadDES m => ResultProvider (Storage m) m where++ resultSource' name i m =+ storageResultSource $ ResultContainer name i m (ResultSignal $ storageChanged_ m)
+ Simulation/Aivika/Trans/GPSS/Results/Locale.hs view
@@ -0,0 +1,316 @@++-- |+-- Module : Simulation.Aivika.GPSS.Trans.Results.Locale+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- The module defines locales for the simulation results.+--+module Simulation.Aivika.Trans.GPSS.Results.Locale where++import qualified Data.Map as M++import Simulation.Aivika.Trans++import qualified Simulation.Aivika.Trans.GPSS.Queue as Q+import Simulation.Aivika.Trans.GPSS.Facility+import Simulation.Aivika.Trans.GPSS.Storage++-- | The queue identifier.+queueId :: ResultId+queueId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "очередь"),+ (englishResultLocale, "the queue")]++-- | Property 'Q.queueNull'.+queueNullId :: ResultId+queueNullId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "очередь пуста?"),+ (englishResultLocale, "is the queue empty?")]++-- | Property 'Q.queueContent'.+queueContentId :: ResultId+queueContentId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущее содержимое очереди"),+ (englishResultLocale, "the current queue content")]++-- | Property 'Q.queueContentStats'.+queueContentStatsId :: ResultId+queueContentStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистика по содержимому очереди"),+ (englishResultLocale, "the queue content statistics")]++-- | Property 'Q.enqueueCount'.+enqueueCountId :: ResultId+enqueueCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "количество добавленных транзактов"),+ (englishResultLocale, "the number of enqueued transacts")]++-- | Property 'Q.enqueueZeroEntryCount'.+enqueueZeroEntryCountId :: ResultId+enqueueZeroEntryCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "количество добавленных транзактов без фактического ожидания"),+ (englishResultLocale, "the number of zero entry enqueued transacts")]++-- | Property 'Q.queueWaitTime'.+queueWaitTimeId :: ResultId+queueWaitTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "время ожидания"),+ (englishResultLocale, "the wait time")]++-- | Property 'Q.queueNonZeroEntryWaitTime'.+queueNonZeroEntryWaitTimeId :: ResultId+queueNonZeroEntryWaitTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "время ожидания без учета фактически неожидавших транзактов"),+ (englishResultLocale, "the wait time without zero entries")]++-- | Property 'Q.queueRate'.+queueRateId :: ResultId+queueRateId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "усредненная скорость (как средняя длина очереди на среднее время ожидания)"),+ (englishResultLocale, "the average queue rate (= queue size / wait time)")]++-- | The facility identifier.+facilityId :: ResultId+facilityId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "прибор"),+ (englishResultLocale, "the facility")]++-- | Property 'facilityCount'.+facilityCountId :: ResultId+facilityCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущее доступное количество прибора"),+ (englishResultLocale, "the current available count")]++-- | Property 'facilityCountStats'.+facilityCountStatsId :: ResultId+facilityCountStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистика доступного количества прибора"),+ (englishResultLocale, "the available count statistics")]++-- | Property 'facilityCaptureCount'.+facilityCaptureCountId :: ResultId+facilityCaptureCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущее количество захвата прибора"),+ (englishResultLocale, "the current capture count")]++-- | Property 'facilityUtilisationCount'.+facilityUtilisationCountId :: ResultId+facilityUtilisationCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущее используемое количество прибора"),+ (englishResultLocale, "the current utilisation count")]++-- | Property 'facilityUtilisationCountStats'.+facilityUtilisationCountStatsId :: ResultId+facilityUtilisationCountStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистка по используемому количеству прибора"),+ (englishResultLocale, "the utilisation count statistics")]++-- | Property 'facilityQueueCount'.+facilityQueueCountId :: ResultId+facilityQueueCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущая длина очереди к прибору"),+ (englishResultLocale, "the current queue size")]++-- | Property 'facilityQueueCountStats'.+facilityQueueCountStatsId :: ResultId+facilityQueueCountStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистика длины очереди к прибору"),+ (englishResultLocale, "the queue size statistics")]++-- | Property 'facilityTotalWaitTime'.+facilityTotalWaitTimeId :: ResultId+facilityTotalWaitTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "общее время ожидания прибора"),+ (englishResultLocale, "the total wait time")]++-- | Property 'facilityWaitTime'.+facilityWaitTimeId :: ResultId+facilityWaitTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "время ожидания прибора"),+ (englishResultLocale, "the wait time")]++-- | Property 'facilityTotalHoldingTime'.+facilityTotalHoldingTimeId :: ResultId+facilityTotalHoldingTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "общее время удержания прибора"),+ (englishResultLocale, "the total holding time")]++-- | Property 'facilityHoldingTime'.+facilityHoldingTimeId :: ResultId+facilityHoldingTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "время удержания прибора"),+ (englishResultLocale, "the holding time")]++-- | Property 'facilityInterrupted'.+facilityInterruptedId :: ResultId+facilityInterruptedId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "удержание прибора сейчас прервано?"),+ (englishResultLocale, "is the facility interrupted now?")]++-- | The storage identifier.+storageId :: ResultId+storageId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "многоканальное устройство"),+ (englishResultLocale, "the storage")]++-- | Property 'storageCapacity'.+storageCapacityId :: ResultId+storageCapacityId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "емкость многоканального устройства"),+ (englishResultLocale, "the storage capacity")]++-- | Property 'storageEmpty'.+storageEmptyId :: ResultId+storageEmptyId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "не используется ли совсем устройство сейчас?"),+ (englishResultLocale, "is the storage unused completely now?")]++-- | Property 'storageFull'.+storageFullId :: ResultId+storageFullId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "используется ли устройство полностью сейчас?"),+ (englishResultLocale, "is the storage used completely now?")]++-- | Property 'storageContent'.+storageContentId :: ResultId+storageContentId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "доступное содержимое устройства"),+ (englishResultLocale, "the current available content")]++-- | Property 'storageContentStats'.+storageContentStatsId :: ResultId+storageContentStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистика доступного содержимого устройства"),+ (englishResultLocale, "the available content statistics")]++-- | Property 'storageUseCount'.+storageUseCountId :: ResultId+storageUseCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "общее количество использований устройства"),+ (englishResultLocale, "the total use count")]++-- | Property 'storageUsedContent'.+storageUsedContentId :: ResultId+storageUsedContentId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "общее использованное количество устройства"),+ (englishResultLocale, "the total used content")]++-- | Property 'storageUtilisationCount'.+storageUtilisationCountId :: ResultId+storageUtilisationCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущее используемое количество устройства"),+ (englishResultLocale, "the current utilisation count")]++-- | Property 'storageUtilisationCountStats'.+storageUtilisationCountStatsId :: ResultId+storageUtilisationCountStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистка по используемому количеству устройства"),+ (englishResultLocale, "the utilisation count statistics")]++-- | Property 'storageQueueCount'.+storageQueueCountId :: ResultId+storageQueueCountId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "текущая длина очереди к устройству"),+ (englishResultLocale, "the current queue size")]++-- | Property 'storageQueueCountStats'.+storageQueueCountStatsId :: ResultId+storageQueueCountStatsId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "статистика длины очереди к устройству"),+ (englishResultLocale, "the queue size statistics")]++-- | Property 'storageTotalWaitTime'.+storageTotalWaitTimeId :: ResultId+storageTotalWaitTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "общее время ожидания устройства"),+ (englishResultLocale, "the total wait time")]++-- | Property 'storageWaitTime'.+storageWaitTimeId :: ResultId+storageWaitTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "время ожидания устройства"),+ (englishResultLocale, "the wait time")]++-- | Property 'storageAverageHoldingTime'.+storageAverageHoldingTimeId :: ResultId+storageAverageHoldingTimeId =+ LocalisedResultId $+ M.fromList+ [(russianResultLocale, "среднее время удержания устройства на единицу устройства"),+ (englishResultLocale, "the average holding time per unit")]
+ Simulation/Aivika/Trans/GPSS/Results/Transform.hs view
@@ -0,0 +1,216 @@++{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Results.Transform+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- The module defines transformations for the simulation results.+--+module Simulation.Aivika.Trans.GPSS.Results.Transform where++import Control.Category++import Simulation.Aivika.Trans+import qualified Simulation.Aivika.Trans.Results.Transform as T++import qualified Simulation.Aivika.Trans.GPSS.Queue as Q+import qualified Simulation.Aivika.Trans.GPSS.Facility as F+import qualified Simulation.Aivika.Trans.GPSS.Storage as S+import Simulation.Aivika.Trans.GPSS.Results+import Simulation.Aivika.Trans.GPSS.Results.Locale++-- | Represents the 'Q.Queue'.+newtype Queue m = Queue (ResultTransform m)++-- | An instance of the result transformer.+instance T.ResultTransformer Queue m where+ tr (Queue a) = a++-- | Property 'Q.queueNull'.+queueNull :: Queue m -> ResultTransform m+queueNull (Queue a) =+ a >>> resultById queueNullId++-- | Property 'Q.queueContent'.+queueContent :: Queue m -> ResultTransform m+queueContent (Queue a) =+ a >>> resultById queueContentId++-- | Property 'Q.queueContentStats'.+queueContentStats :: Queue m -> T.TimingStats m+queueContentStats (Queue a) =+ T.TimingStats (a >>> resultById queueContentStatsId)++-- | Property 'Q.enqueueCount'.+enqueueCount :: Queue m -> ResultTransform m+enqueueCount (Queue a) =+ a >>> resultById enqueueCountId++-- | Property 'Q.enqueueZeroEntryCount'.+enqueueZeroEntryCount :: Queue m -> ResultTransform m+enqueueZeroEntryCount (Queue a) =+ a >>> resultById enqueueZeroEntryCountId++-- | Property 'Q.queueWaitTime'.+queueWaitTime :: Queue m -> T.SamplingStats m+queueWaitTime (Queue a) =+ T.SamplingStats (a >>> resultById queueWaitTimeId)++-- | Property 'Q.queueNonZeroEntryWaitTime'.+queueNonZeroEntryWaitTime :: Queue m -> T.SamplingStats m+queueNonZeroEntryWaitTime (Queue a) =+ T.SamplingStats (a >>> resultById queueNonZeroEntryWaitTimeId)++-- | Property 'Q.queueRate'.+queueRate :: Queue m -> ResultTransform m+queueRate (Queue a) =+ a >>> resultById queueRateId++-- | Represents the 'F.Facility'.+newtype Facility m = Facility (ResultTransform m)++-- | An instance of the result transformer.+instance T.ResultTransformer Facility m where+ tr (Facility a) = a++-- | Property 'F.facilityCount'.+facilityCount :: Facility m -> ResultTransform m+facilityCount (Facility a) =+ a >>> resultById facilityCountId++-- | Property 'F.facilityCountStats'.+facilityCountStats :: Facility m -> T.TimingStats m+facilityCountStats (Facility a) =+ T.TimingStats (a >>> resultById facilityCountStatsId)++-- | Property 'F.facilityCaptureCount'.+facilityCaptureCount :: Facility m -> ResultTransform m+facilityCaptureCount (Facility a) =+ a >>> resultById facilityCaptureCountId++-- | Property 'F.facilityUtilisationCount'.+facilityUtilisationCount :: Facility m -> ResultTransform m+facilityUtilisationCount (Facility a) =+ a >>> resultById facilityUtilisationCountId++-- | Property 'F.facilityUtilisationCountStats'.+facilityUtilisationCountStats :: Facility m -> T.TimingStats m+facilityUtilisationCountStats (Facility a) =+ T.TimingStats (a >>> resultById facilityUtilisationCountStatsId)++-- | Property 'F.facilityQueueCount'.+facilityQueueCount :: Facility m -> ResultTransform m+facilityQueueCount (Facility a) =+ a >>> resultById facilityQueueCountId++-- | Property 'F.facilityQueueCountStats'.+facilityQueueCountStats :: Facility m -> T.TimingStats m+facilityQueueCountStats (Facility a) =+ T.TimingStats (a >>> resultById facilityQueueCountStatsId)++-- | Property 'F.facilityTotalWaitTime'.+facilityTotalWaitTime :: Facility m -> ResultTransform m+facilityTotalWaitTime (Facility a) =+ a >>> resultById facilityTotalWaitTimeId++-- | Property 'F.facilityWaitTime'.+facilityWaitTime :: Facility m -> T.SamplingStats m+facilityWaitTime (Facility a) =+ T.SamplingStats (a >>> resultById facilityWaitTimeId)++-- | Property 'F.facilityTotalHoldingTime'.+facilityTotalHoldingTime :: Facility m -> ResultTransform m+facilityTotalHoldingTime (Facility a) =+ a >>> resultById facilityTotalHoldingTimeId++-- | Property 'F.facilityHoldingTime'.+facilityHoldingTime :: Facility m -> T.SamplingStats m+facilityHoldingTime (Facility a) =+ T.SamplingStats (a >>> resultById facilityHoldingTimeId)++-- | Property 'F.facilityInterrupted'.+facilityInterrupted :: Facility m -> ResultTransform m+facilityInterrupted (Facility a) =+ a >>> resultById facilityInterruptedId++-- | Represents the 'S.Storage'.+newtype Storage m = Storage (ResultTransform m)++-- | An instance of the result transformer.+instance T.ResultTransformer Storage m where+ tr (Storage a) = a++-- | Property 'S.storageCapacity'.+storageCapacity :: Storage m -> ResultTransform m+storageCapacity (Storage a) =+ a >>> resultById storageCapacityId++-- | Property 'S.storageEmpty'.+storageEmpty :: Storage m -> ResultTransform m+storageEmpty (Storage a) =+ a >>> resultById storageEmptyId++-- | Property 'S.storageFull'.+storageFull :: Storage m -> ResultTransform m+storageFull (Storage a) =+ a >>> resultById storageFullId++-- | Property 'S.storageContent'.+storageContent :: Storage m -> ResultTransform m+storageContent (Storage a) =+ a >>> resultById storageContentId++-- | Property 'S.storageContentStats'.+storageContentStats :: Storage m -> T.TimingStats m+storageContentStats (Storage a) =+ T.TimingStats (a >>> resultById storageContentStatsId)++-- | Property 'S.storageUseCount'.+storageUseCount :: Storage m -> ResultTransform m+storageUseCount (Storage a) =+ a >>> resultById storageUseCountId++-- | Property 'S.storageUsedContent'.+storageUsedContent :: Storage m -> ResultTransform m+storageUsedContent (Storage a) =+ a >>> resultById storageUsedContentId++-- | Property 'S.storageUtilisationCount'.+storageUtilisationCount :: Storage m -> ResultTransform m+storageUtilisationCount (Storage a) =+ a >>> resultById storageUtilisationCountId++-- | Property 'S.storageUtilisationCountStats'.+storageUtilisationCountStats :: Storage m -> T.TimingStats m+storageUtilisationCountStats (Storage a) =+ T.TimingStats (a >>> resultById storageUtilisationCountStatsId)++-- | Property 'S.storageQueueCount'.+storageQueueCount :: Storage m -> ResultTransform m+storageQueueCount (Storage a) =+ a >>> resultById storageQueueCountId++-- | Property 'S.storageQueueCountStats'.+storageQueueCountStats :: Storage m -> T.TimingStats m+storageQueueCountStats (Storage a) =+ T.TimingStats (a >>> resultById storageQueueCountStatsId)++-- | Property 'S.storageTotalWaitTime'.+storageTotalWaitTime :: Storage m -> ResultTransform m+storageTotalWaitTime (Storage a) =+ a >>> resultById storageTotalWaitTimeId++-- | Property 'S.storageWaitTime'.+storageWaitTime :: Storage m -> T.SamplingStats m+storageWaitTime (Storage a) =+ T.SamplingStats (a >>> resultById storageWaitTimeId)++-- | Property 'S.storageAverageHoldingTime'.+storageAverageHoldingTime :: Storage m -> ResultTransform m+storageAverageHoldingTime (Storage a) =+ a >>> resultById storageAverageHoldingTimeId
+ Simulation/Aivika/Trans/GPSS/Storage.hs view
@@ -0,0 +1,570 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Storage+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.1+--+-- This module defines the GPSS Storage entity.+--+module Simulation.Aivika.Trans.GPSS.Storage+ (-- * Storage Type+ Storage,+ -- * Creating Storage+ newStorage,+ -- * Storage Properties+ storageCapacity,+ storageEmpty,+ storageFull,+ storageContent,+ storageContentStats,+ storageUseCount,+ storageUsedContent,+ storageUtilisationCount,+ storageUtilisationCountStats,+ storageQueueCount,+ storageQueueCountStats,+ storageTotalWaitTime,+ storageWaitTime,+ storageAverageHoldingTime,+ -- * Entering-Leaving Storage+ enterStorage,+ leaveStorage,+ leaveStorageWithinEvent,+ -- * Statistics Reset+ resetStorage,+ -- * Signals+ storageContentChanged,+ storageContentChanged_,+ storageUseCountChanged,+ storageUseCountChanged_,+ storageUsedContentChanged,+ storageUsedContentChanged_,+ storageUtilisationCountChanged,+ storageUtilisationCountChanged_,+ storageQueueCountChanged,+ storageQueueCountChanged_,+ storageWaitTimeChanged,+ storageWaitTimeChanged_,+ storageChanged_) where++import Data.Monoid+import Data.Maybe++import Control.Monad+import Control.Monad.Trans+import Control.Exception++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.Internal.Specs+import Simulation.Aivika.Trans.Internal.Simulation+import Simulation.Aivika.Trans.Internal.Event+import Simulation.Aivika.Trans.Internal.Cont+import Simulation.Aivika.Trans.Internal.Process+import Simulation.Aivika.Trans.QueueStrategy+import Simulation.Aivika.Trans.Statistics+import Simulation.Aivika.Trans.Signal++import Simulation.Aivika.Trans.GPSS.Transact+import Simulation.Aivika.Trans.GPSS.TransactQueueStrategy++-- | Represents a GPSS Storage entity.+data Storage m = + Storage { storageCapacity :: Int,+ -- ^ Return the storage capacity.+ storageContentRef :: Ref m Int,+ storageContentStatsRef :: Ref m (TimingStats Int),+ storageContentSource :: SignalSource m Int,+ storageUseCountRef :: Ref m Int,+ storageUseCountSource :: SignalSource m Int,+ storageUsedContentRef :: Ref m Int,+ storageUsedContentSource :: SignalSource m Int,+ storageUtilisationCountRef :: Ref m Int,+ storageUtilisationCountStatsRef :: Ref m (TimingStats Int),+ storageUtilisationCountSource :: SignalSource m Int,+ storageQueueCountRef :: Ref m Int,+ storageQueueCountStatsRef :: Ref m (TimingStats Int),+ storageQueueCountSource :: SignalSource m Int,+ storageTotalWaitTimeRef :: Ref m Double,+ storageWaitTimeRef :: Ref m (SamplingStats Double),+ storageWaitTimeSource :: SignalSource m (),+ storageDelayChain :: StrategyQueue m (TransactQueueStrategy FCFS) (StorageDelayedItem m) }++-- | Identifies an item that was delayed.+data StorageDelayedItem m =+ StorageDelayedItem { delayedItemTime :: Double,+ delayedItemDecrement :: Int,+ delayedItemCont :: FrozenCont m () }++instance MonadDES m => Eq (Storage m) where++ {-# INLINABLE (==) #-}+ x == y = storageContentRef x == storageContentRef y -- unique references++-- | Create a new storage by the specified capacity.+newStorage :: MonadDES m => Int -> Event m (Storage m)+{-# INLINABLE newStorage #-}+newStorage capacity =+ Event $ \p ->+ do let r = pointRun p+ t = pointTime p+ contentRef <- invokeSimulation r $ newRef capacity+ contentStatsRef <- invokeSimulation r $ newRef $ returnTimingStats t capacity+ contentSource <- invokeSimulation r newSignalSource+ useCountRef <- invokeSimulation r $ newRef 0+ useCountSource <- invokeSimulation r newSignalSource+ usedContentRef <- invokeSimulation r $ newRef 0+ usedContentSource <- invokeSimulation r newSignalSource+ utilCountRef <- invokeSimulation r $ newRef 0+ utilCountStatsRef <- invokeSimulation r $ newRef $ returnTimingStats t 0+ utilCountSource <- invokeSimulation r newSignalSource+ queueCountRef <- invokeSimulation r $ newRef 0+ queueCountStatsRef <- invokeSimulation r $ newRef $ returnTimingStats t 0+ queueCountSource <- invokeSimulation r newSignalSource+ totalWaitTimeRef <- invokeSimulation r $ newRef 0+ waitTimeRef <- invokeSimulation r $ newRef emptySamplingStats+ waitTimeSource <- invokeSimulation r newSignalSource+ delayChain <- invokeSimulation r $ newStrategyQueue (TransactQueueStrategy FCFS)+ return Storage { storageCapacity = capacity,+ storageContentRef = contentRef,+ storageContentStatsRef = contentStatsRef,+ storageContentSource = contentSource,+ storageUseCountRef = useCountRef,+ storageUseCountSource = useCountSource,+ storageUsedContentRef = usedContentRef,+ storageUsedContentSource = usedContentSource,+ storageUtilisationCountRef = utilCountRef,+ storageUtilisationCountStatsRef = utilCountStatsRef,+ storageUtilisationCountSource = utilCountSource,+ storageQueueCountRef = queueCountRef,+ storageQueueCountStatsRef = queueCountStatsRef,+ storageQueueCountSource = queueCountSource,+ storageTotalWaitTimeRef = totalWaitTimeRef,+ storageWaitTimeRef = waitTimeRef,+ storageWaitTimeSource = waitTimeSource,+ storageDelayChain = delayChain }++-- | Whether the storage is empty, i.e. completely unused.+storageEmpty :: MonadDES m => Storage m -> Event m Bool+{-# INLINABLE storageEmpty #-}+storageEmpty r =+ Event $ \p ->+ do n <- invokeEvent p $ readRef (storageContentRef r)+ return (n == storageCapacity r)++-- | Whether the storage is full, i.e. completely used.+storageFull :: MonadDES m => Storage m -> Event m Bool+{-# INLINABLE storageFull #-}+storageFull r =+ Event $ \p ->+ do n <- invokeEvent p $ readRef (storageContentRef r)+ return (n == 0)++-- | Return the current storage content available for use.+storageContent :: MonadDES m => Storage m -> Event m Int+{-# INLINABLE storageContent #-}+storageContent r =+ Event $ \p -> invokeEvent p $ readRef (storageContentRef r)++-- | Return the statistics of the storage content available for use.+storageContentStats :: MonadDES m => Storage m -> Event m (TimingStats Int)+{-# INLINABLE storageContentStats #-}+storageContentStats r =+ Event $ \p -> invokeEvent p $ readRef (storageContentStatsRef r)++-- | Signal triggered when the 'storageContent' property changes.+storageContentChanged :: MonadDES m => Storage m -> Signal m Int+{-# INLINABLE storageContentChanged #-}+storageContentChanged r =+ publishSignal $ storageContentSource r++-- | Signal triggered when the 'storageContent' property changes.+storageContentChanged_ :: MonadDES m => Storage m -> Signal m ()+{-# INLINABLE storageContentChanged_ #-}+storageContentChanged_ r =+ mapSignal (const ()) $ storageContentChanged r++-- | Return the total use count of the storage.+storageUseCount :: MonadDES m => Storage m -> Event m Int+{-# INLINABLE storageUseCount #-}+storageUseCount r =+ Event $ \p -> invokeEvent p $ readRef (storageUseCountRef r)++-- | Signal triggered when the 'storageUseCount' property changes.+storageUseCountChanged :: MonadDES m => Storage m -> Signal m Int+{-# INLINABLE storageUseCountChanged #-}+storageUseCountChanged r =+ publishSignal $ storageUseCountSource r++-- | Signal triggered when the 'storageUseCount' property changes.+storageUseCountChanged_ :: MonadDES m => Storage m -> Signal m ()+{-# INLINABLE storageUseCountChanged_ #-}+storageUseCountChanged_ r =+ mapSignal (const ()) $ storageUseCountChanged r++-- | Return the total used content of the storage.+storageUsedContent :: MonadDES m => Storage m -> Event m Int+{-# INLINABLE storageUsedContent #-}+storageUsedContent r =+ Event $ \p -> invokeEvent p $ readRef (storageUsedContentRef r)++-- | Signal triggered when the 'storageUsedContent' property changes.+storageUsedContentChanged :: MonadDES m => Storage m -> Signal m Int+{-# INLINABLE storageUsedContentChanged #-}+storageUsedContentChanged r =+ publishSignal $ storageUsedContentSource r++-- | Signal triggered when the 'storageUsedContent' property changes.+storageUsedContentChanged_ :: MonadDES m => Storage m -> Signal m ()+{-# INLINABLE storageUsedContentChanged_ #-}+storageUsedContentChanged_ r =+ mapSignal (const ()) $ storageUsedContentChanged r++-- | Return the current utilisation count of the storage.+storageUtilisationCount :: MonadDES m => Storage m -> Event m Int+{-# INLINABLE storageUtilisationCount #-}+storageUtilisationCount r =+ Event $ \p -> invokeEvent p $ readRef (storageUtilisationCountRef r)++-- | Return the statistics for the utilisation count of the storage.+storageUtilisationCountStats :: MonadDES m => Storage m -> Event m (TimingStats Int)+{-# INLINABLE storageUtilisationCountStats #-}+storageUtilisationCountStats r =+ Event $ \p -> invokeEvent p $ readRef (storageUtilisationCountStatsRef r)++-- | Signal triggered when the 'storageUtilisationCount' property changes.+storageUtilisationCountChanged :: MonadDES m => Storage m -> Signal m Int+{-# INLINABLE storageUtilisationCountChanged #-}+storageUtilisationCountChanged r =+ publishSignal $ storageUtilisationCountSource r++-- | Signal triggered when the 'storageUtilisationCount' property changes.+storageUtilisationCountChanged_ :: MonadDES m => Storage m -> Signal m ()+{-# INLINABLE storageUtilisationCountChanged_ #-}+storageUtilisationCountChanged_ r =+ mapSignal (const ()) $ storageUtilisationCountChanged r++-- | Return the current queue length of the storage.+storageQueueCount :: MonadDES m => Storage m -> Event m Int+{-# INLINABLE storageQueueCount #-}+storageQueueCount r =+ Event $ \p -> invokeEvent p $ readRef (storageQueueCountRef r)++-- | Return the statistics for the queue length of the storage.+storageQueueCountStats :: MonadDES m => Storage m -> Event m (TimingStats Int)+{-# INLINABLE storageQueueCountStats #-}+storageQueueCountStats r =+ Event $ \p -> invokeEvent p $ readRef (storageQueueCountStatsRef r)++-- | Signal triggered when the 'storageQueueCount' property changes.+storageQueueCountChanged :: MonadDES m => Storage m -> Signal m Int+{-# INLINABLE storageQueueCountChanged #-}+storageQueueCountChanged r =+ publishSignal $ storageQueueCountSource r++-- | Signal triggered when the 'storageQueueCount' property changes.+storageQueueCountChanged_ :: MonadDES m => Storage m -> Signal m ()+{-# INLINABLE storageQueueCountChanged_ #-}+storageQueueCountChanged_ r =+ mapSignal (const ()) $ storageQueueCountChanged r++-- | Return the total wait time of the storage.+storageTotalWaitTime :: MonadDES m => Storage m -> Event m Double+{-# INLINABLE storageTotalWaitTime #-}+storageTotalWaitTime r =+ Event $ \p -> invokeEvent p $ readRef (storageTotalWaitTimeRef r)++-- | Return the statistics for the wait time of the storage.+storageWaitTime :: MonadDES m => Storage m -> Event m (SamplingStats Double)+{-# INLINABLE storageWaitTime #-}+storageWaitTime r =+ Event $ \p -> invokeEvent p $ readRef (storageWaitTimeRef r)++-- | Signal triggered when the 'storageTotalWaitTime' and 'storageWaitTime' properties change.+storageWaitTimeChanged :: MonadDES m => Storage m -> Signal m (SamplingStats Double)+{-# INLINABLE storageWaitTimeChanged #-}+storageWaitTimeChanged r =+ mapSignalM (\() -> storageWaitTime r) $ storageWaitTimeChanged_ r++-- | Signal triggered when the 'storageTotalWaitTime' and 'storageWaitTime' properties change.+storageWaitTimeChanged_ :: MonadDES m => Storage m -> Signal m ()+{-# INLINABLE storageWaitTimeChanged_ #-}+storageWaitTimeChanged_ r =+ publishSignal $ storageWaitTimeSource r++-- | Return the average holding time per unit.+storageAverageHoldingTime :: MonadDES m => Storage m -> Event m Double+{-# INLINABLE storageAverageHoldingTime #-}+storageAverageHoldingTime r =+ Event $ \p ->+ do s <- invokeEvent p $ readRef (storageUtilisationCountStatsRef r)+ n <- invokeEvent p $ readRef (storageUtilisationCountRef r)+ m <- invokeEvent p $ readRef (storageUsedContentRef r)+ let t = pointTime p+ s' = addTimingStats t n s+ k = timingStatsSum s' / (fromRational $ toRational m)+ return k++-- | Enter the storage.+enterStorage :: MonadDES m+ => Storage m+ -- ^ the requested storage+ -> Transact m a+ -- ^ a transact that makes the request+ -> Int+ -- ^ the content decrement+ -> Process m ()+{-# INLINABLE enterStorage #-}+enterStorage r transact decrement =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let t = pointTime p+ f <- invokeEvent p $ strategyQueueNull (storageDelayChain r)+ if f+ then invokeEvent p $+ invokeCont c $+ invokeProcess pid $+ enterStorage' r transact decrement+ else do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ enterStorage r transact decrement+ invokeEvent p $+ strategyEnqueueWithPriority+ (storageDelayChain r)+ (transactPriority transact)+ (StorageDelayedItem t decrement c)+ invokeEvent p $ updateStorageQueueCount r 1++-- | Enter the storage.+enterStorage' :: MonadDES m+ => Storage m+ -- ^ the requested storage+ -> Transact m a+ -- ^ a transact that makes the request+ -> Int+ -- ^ the content decrement+ -> Process m ()+{-# INLINABLE enterStorage' #-}+enterStorage' r transact decrement =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do let t = pointTime p+ a <- invokeEvent p $ readRef (storageContentRef r)+ if a < decrement+ then do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ enterStorage r transact decrement+ invokeEvent p $+ strategyEnqueueWithPriority+ (storageDelayChain r)+ (transactPriority transact)+ (StorageDelayedItem t decrement c)+ invokeEvent p $ updateStorageQueueCount r 1+ else do invokeEvent p $ updateStorageWaitTime r 0+ invokeEvent p $ updateStorageContent r (- decrement)+ invokeEvent p $ updateStorageUseCount r 1+ invokeEvent p $ updateStorageUsedContent r decrement+ invokeEvent p $ updateStorageUtilisationCount r decrement+ invokeEvent p $ resumeCont c ()++-- | Leave the storage.+leaveStorage :: MonadDES m+ => Storage m+ -- ^ the storage to leave+ -> Int+ -- ^ the content increment+ -> Process m ()+{-# INLINABLE leaveStorage #-}+leaveStorage r increment =+ Process $ \_ ->+ Cont $ \c ->+ Event $ \p ->+ do invokeEvent p $ leaveStorageWithinEvent r increment+ invokeEvent p $ resumeCont c ()++-- | Leave the storage.+leaveStorageWithinEvent :: MonadDES m+ => Storage m+ -- ^ the storage to leave+ -> Int+ -- ^ the content increment+ -> Event m ()+{-# INLINABLE leaveStorageWithinEvent #-}+leaveStorageWithinEvent r increment =+ Event $ \p ->+ do let t = pointTime p+ invokeEvent p $ updateStorageUtilisationCount r (- increment)+ invokeEvent p $ updateStorageContent r increment+ invokeEvent p $ enqueueEvent t $ tryEnterStorage r++-- | Try to enter the storage.+tryEnterStorage :: MonadDES m => Storage m -> Event m ()+{-# INLINABLE tryEnterStorage #-}+tryEnterStorage r =+ Event $ \p ->+ do let t = pointTime p+ a <- invokeEvent p $ readRef (storageContentRef r)+ if a > 0+ then invokeEvent p $ letEnterStorage r+ else return ()++-- | Let enter the storage.+letEnterStorage :: MonadDES m => Storage m -> Event m ()+{-# INLINABLE letEnterStorage #-}+letEnterStorage r =+ Event $ \p ->+ do let t = pointTime p+ a <- invokeEvent p $ readRef (storageContentRef r)+ when (a > storageCapacity r) $+ throwComp $+ SimulationRetry $+ "The storage content cannot exceed the limited capacity: leaveStorage'"+ x <- invokeEvent p $+ strategyQueueDeleteBy+ (storageDelayChain r)+ (\i -> delayedItemDecrement i <= a)+ case x of+ Nothing -> return ()+ Just (StorageDelayedItem t0 decrement0 c0) ->+ do invokeEvent p $ updateStorageQueueCount r (-1)+ c <- invokeEvent p $ unfreezeCont c0+ case c of+ Nothing ->+ invokeEvent p $ letEnterStorage r+ Just c ->+ do invokeEvent p $ updateStorageContent r (- decrement0)+ invokeEvent p $ updateStorageWaitTime r (t - t0)+ invokeEvent p $ updateStorageUtilisationCount r decrement0+ invokeEvent p $ updateStorageUseCount r 1+ invokeEvent p $ updateStorageUsedContent r decrement0+ invokeEvent p $ enqueueEvent t $ reenterCont c ()++-- | Signal triggered when one of the storage counters changes.+storageChanged_ :: MonadDES m => Storage m -> Signal m ()+{-# INLINABLE storageChanged_ #-}+storageChanged_ r =+ storageContentChanged_ r <>+ storageUsedContentChanged_ r <>+ storageUtilisationCountChanged_ r <>+ storageQueueCountChanged_ r++-- | Update the storage content and its statistics.+updateStorageContent :: MonadDES m => Storage m -> Int -> Event m ()+{-# INLINABLE updateStorageContent #-}+updateStorageContent r delta =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (storageContentRef r)+ let a' = a + delta+ invokeEvent p $+ writeRef (storageContentRef r) a'+ invokeEvent p $+ modifyRef (storageContentStatsRef r) $+ addTimingStats (pointTime p) a'+ invokeEvent p $+ triggerSignal (storageContentSource r) a'++-- | Update the storage use count.+updateStorageUseCount :: MonadDES m => Storage m -> Int -> Event m ()+{-# INLINABLE updateStorageUseCount #-}+updateStorageUseCount r delta =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (storageUseCountRef r)+ let a' = a + delta+ invokeEvent p $+ writeRef (storageUseCountRef r) a'+ invokeEvent p $+ triggerSignal (storageUseCountSource r) a'++-- | Update the storage used content.+updateStorageUsedContent :: MonadDES m => Storage m -> Int -> Event m ()+{-# INLINABLE updateStorageUsedContent #-}+updateStorageUsedContent r delta =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (storageUsedContentRef r)+ let a' = a + delta+ invokeEvent p $+ writeRef (storageUsedContentRef r) a'+ invokeEvent p $+ triggerSignal (storageUsedContentSource r) a'++-- | Update the storage queue length and its statistics.+updateStorageQueueCount :: MonadDES m => Storage m -> Int -> Event m ()+{-# INLINABLE updateStorageQueueCount #-}+updateStorageQueueCount r delta =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (storageQueueCountRef r)+ let a' = a + delta+ invokeEvent p $+ writeRef (storageQueueCountRef r) a'+ invokeEvent p $+ modifyRef (storageQueueCountStatsRef r) $+ addTimingStats (pointTime p) a'+ invokeEvent p $+ triggerSignal (storageQueueCountSource r) a'++-- | Update the storage utilisation count and its statistics.+updateStorageUtilisationCount :: MonadDES m => Storage m -> Int -> Event m ()+{-# INLINABLE updateStorageUtilisationCount #-}+updateStorageUtilisationCount r delta =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (storageUtilisationCountRef r)+ let a' = a + delta+ invokeEvent p $+ writeRef (storageUtilisationCountRef r) a'+ invokeEvent p $+ modifyRef (storageUtilisationCountStatsRef r) $+ addTimingStats (pointTime p) a'+ invokeEvent p $+ triggerSignal (storageUtilisationCountSource r) a'++-- | Update the storage wait time and its statistics.+updateStorageWaitTime :: MonadDES m => Storage m -> Double -> Event m ()+{-# INLINABLE updateStorageWaitTime #-}+updateStorageWaitTime r delta =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (storageTotalWaitTimeRef r)+ let a' = a + delta+ invokeEvent p $+ writeRef (storageTotalWaitTimeRef r) a'+ invokeEvent p $+ modifyRef (storageWaitTimeRef r) $+ addSamplingStats delta+ invokeEvent p $+ triggerSignal (storageWaitTimeSource r) ()++-- | Reset the statistics.+resetStorage :: MonadDES m => Storage m -> Event m ()+{-# INLINABLE resetStorage #-}+resetStorage r =+ Event $ \p ->+ do let t = pointTime p+ content <- invokeEvent p $ readRef (storageContentRef r)+ invokeEvent p $ writeRef (storageContentStatsRef r) $+ returnTimingStats t content+ invokeEvent p $ writeRef (storageUseCountRef r) 0+ let usedContent = storageCapacity r - content+ invokeEvent p $ writeRef (storageUsedContentRef r) usedContent+ utilCount <- invokeEvent p $ readRef (storageUtilisationCountRef r)+ invokeEvent p $ writeRef (storageUtilisationCountStatsRef r) $+ returnTimingStats t utilCount+ queueCount <- invokeEvent p $ readRef (storageQueueCountRef r)+ invokeEvent p $ writeRef (storageQueueCountStatsRef r) $+ returnTimingStats t queueCount+ invokeEvent p $ writeRef (storageTotalWaitTimeRef r) 0+ invokeEvent p $ writeRef (storageWaitTimeRef r) emptySamplingStats+ invokeEvent p $+ triggerSignal (storageUseCountSource r) 0+ invokeEvent p $+ triggerSignal (storageUsedContentSource r) usedContent+ invokeEvent p $+ triggerSignal (storageUtilisationCountSource r) utilCount+ invokeEvent p $+ triggerSignal (storageWaitTimeSource r) ()
+ Simulation/Aivika/Trans/GPSS/Transact.hs view
@@ -0,0 +1,317 @@++-- |+-- Module : Simulation.Aivika.Trans.GPSS.Transact+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines a GPSS transact.+--+module Simulation.Aivika.Trans.GPSS.Transact+ (Transact,+ transactValue,+ transactArrivalDelay,+ transactArrivalTime,+ transactPriority,+ transactAssemblySet,+ newTransact,+ splitTransact,+ assignTransactValue,+ assignTransactValueM,+ assignTransactPriority,+ takeTransact,+ releaseTransact,+ transactPreemptionBegin,+ transactPreemptionEnd,+ requireTransactProcessId,+ transferTransact,+ reactivateTransacts,+ registerTransactQueueEntry,+ unregisterTransactQueueEntry) where++import Control.Monad+import Control.Monad.Trans+import Control.Exception++import qualified Data.HashMap.Lazy as HM++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.Internal.Specs+import Simulation.Aivika.Trans.Internal.Simulation+import Simulation.Aivika.Trans.Internal.Event+import Simulation.Aivika.Trans.Internal.Cont+import Simulation.Aivika.Trans.Internal.Process++import {-# SOURCE #-} Simulation.Aivika.Trans.GPSS.Queue+import {-# SOURCE #-} Simulation.Aivika.Trans.GPSS.AssemblySet++-- | Represents a GPSS transact.+data Transact m a =+ Transact { transactValue :: a,+ -- ^ The data of the transact.+ transactArrivalDelay :: Maybe Double,+ -- ^ The delay between the transacts generated.+ transactArrivalTime :: Double,+ -- ^ The time at which the transact was generated.+ transactPriority :: Int,+ -- ^ The transact priority.+ transactAssemblySetRef :: Ref m (Maybe (AssemblySet m)),+ -- ^ The assembly set.+ transactPreemptionCountRef :: Ref m Int,+ -- ^ How many times the transact is preempted.+ transactProcessIdRef :: Ref m (Maybe (ProcessId m)),+ -- ^ An identifier of the process that handles the transact at present+ transactProcessContRef :: Ref m (Maybe (FrozenCont m ())),+ -- ^ A continuation of the process that tried to handle the transact.+ transactQueueEntryRef :: Ref m (HM.HashMap (Queue m) (QueueEntry m))+ -- ^ The queue entries registered by the the transact.+ }++instance MonadDES m => Eq (Transact m a) where++ {-# INLINABLE (==) #-}+ x == y = (transactProcessIdRef x) == (transactProcessIdRef y)++-- | Create a new transact.+newTransact :: MonadDES m+ => Arrival a+ -- ^ the arrival data+ -> Int+ -- ^ the transact priority+ -> Simulation m (Transact m a)+{-# INLINABLE newTransact #-}+newTransact a priority =+ Simulation $ \r ->+ do r0 <- invokeSimulation r $ newRef 0+ r1 <- invokeSimulation r $ newRef Nothing+ r2 <- invokeSimulation r $ newRef Nothing+ r3 <- invokeSimulation r $ newRef HM.empty+ r4 <- invokeSimulation r $ newRef Nothing+ return Transact { transactValue = arrivalValue a,+ transactArrivalDelay = arrivalDelay a,+ transactArrivalTime = arrivalTime a,+ transactPriority = priority,+ transactAssemblySetRef = r4,+ transactPreemptionCountRef = r0,+ transactProcessIdRef = r1,+ transactProcessContRef = r2,+ transactQueueEntryRef = r3+ }++-- | Split the transact.+splitTransact :: MonadDES m => Transact m a -> Simulation m (Transact m a)+{-# INLINABLE splitTransact #-}+splitTransact t =+ Simulation $ \r ->+ do r0 <- invokeSimulation r $ newRef 0+ r1 <- invokeSimulation r $ newRef Nothing+ r2 <- invokeSimulation r $ newRef Nothing+ r3 <- invokeSimulation r $ newRef HM.empty+ return Transact { transactValue = transactValue t,+ transactArrivalDelay = transactArrivalDelay t,+ transactArrivalTime = transactArrivalTime t,+ transactPriority = transactPriority t,+ transactAssemblySetRef = transactAssemblySetRef t,+ transactPreemptionCountRef = r0,+ transactProcessIdRef = r1,+ transactProcessContRef = r2,+ transactQueueEntryRef = r3+ }++-- | Return the transact assembly set.+transactAssemblySet :: MonadDES m => Transact m a -> Event m (AssemblySet m)+{-# INLINABLE transactAssemblySet #-}+transactAssemblySet t =+ Event $ \p ->+ do let r = pointRun p+ x <- invokeEvent p $ readRef (transactAssemblySetRef t)+ case x of+ Just a -> return a+ Nothing ->+ do a <- invokeSimulation r newAssemblySet+ invokeEvent p $ writeRef (transactAssemblySetRef t) (Just a)+ return a++-- | Take the transact.+takeTransact :: MonadDES m => Transact m a -> Process m ()+{-# INLINABLE takeTransact #-}+takeTransact t =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do pid0 <- invokeEvent p $ readRef (transactProcessIdRef t)+ case pid0 of+ Just pid0 ->+ throwComp $+ SimulationRetry+ "The transact is acquired by another process: takeTransact"+ Nothing ->+ do invokeEvent p $ writeRef (transactProcessIdRef t) (Just pid)+ n <- invokeEvent p $ readRef (transactPreemptionCountRef t)+ if n == 0+ then invokeEvent p $ resumeCont c ()+ else do c <- invokeEvent p $+ freezeContReentering c () $+ invokeCont c $+ invokeProcess pid $+ takeTransact t+ invokeEvent p $+ writeRef (transactProcessContRef t) (Just c)+ forM_ [1 .. n] $ \_ ->+ invokeEvent p $+ processPreemptionBegin pid++-- | Release the transact.+releaseTransact :: MonadDES m => Transact m a -> Process m ()+{-# INLINABLE releaseTransact #-}+releaseTransact t =+ Process $ \pid ->+ Cont $ \c ->+ Event $ \p ->+ do pid0 <- invokeEvent p $ readRef (transactProcessIdRef t)+ case pid0 of+ Nothing ->+ throwComp $+ SimulationRetry+ "The transact is not acquired by any process: releaseTransact"+ Just pid0 | pid0 /= pid ->+ throwComp $+ SimulationRetry+ "The transact is acquired by another process: releaseTransact"+ Just pid0 ->+ do invokeEvent p $ writeRef (transactProcessIdRef t) Nothing+ invokeEvent p $ writeRef (transactProcessContRef t) Nothing+ invokeEvent p $ resumeCont c ()++-- | Preempt the computation that handles the transact.+transactPreemptionBegin :: MonadDES m => Transact m a -> Event m ()+{-# INLINABLE transactPreemptionBegin #-}+transactPreemptionBegin t =+ Event $ \p ->+ do n <- invokeEvent p $ readRef (transactPreemptionCountRef t)+ let n' = n + 1+ n' `seq` invokeEvent p $ writeRef (transactPreemptionCountRef t) n'+ pid <- invokeEvent p $ readRef (transactProcessIdRef t)+ case pid of+ Nothing -> return ()+ Just pid -> invokeEvent p $ processPreemptionBegin pid++-- | Proceed with the computation after the transact was preempted earlier.+transactPreemptionEnd :: MonadDES m => Transact m a -> Event m ()+{-# INLINABLE transactPreemptionEnd #-}+transactPreemptionEnd t =+ Event $ \p ->+ do n <- invokeEvent p $ readRef (transactPreemptionCountRef t)+ let n' = n - 1+ unless (n' >= 0) $+ throwComp $+ SimulationRetry+ "The transact preemption count cannot be negative: transactPreemptionEnd"+ n' `seq` invokeEvent p $ writeRef (transactPreemptionCountRef t) n'+ pid <- invokeEvent p $ readRef (transactProcessIdRef t)+ case pid of+ Nothing -> return ()+ Just pid ->+ do invokeEvent p $ processPreemptionEnd pid+ c <- invokeEvent p $ readRef (transactProcessContRef t)+ case c of+ Nothing -> return ()+ Just c ->+ do invokeEvent p $ writeRef (transactProcessContRef t) Nothing+ c <- invokeEvent p $ unfreezeCont c+ case c of+ Nothing -> return ()+ Just c -> invokeEvent p $ enqueueEvent (pointTime p) $ resumeCont c ()++-- | Require to return an identifier of the process associated with the transact.+requireTransactProcessId :: MonadDES m => Transact m a -> Event m (ProcessId m)+{-# INLINABLE requireTransactProcessId #-}+requireTransactProcessId t =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (transactProcessIdRef t)+ case a of+ Nothing ->+ throwComp $+ SimulationRetry+ "The transact must be associated with any process: requireTransactProcessId"+ Just pid ->+ return pid++-- | Like the GoTo statement, it associates the transact with another process.+transferTransact :: MonadDES m => Transact m a -> Process m () -> Event m ()+{-# INLINABLE transferTransact #-}+transferTransact t transfer =+ Event $ \p ->+ do a <- invokeEvent p $ readRef (transactProcessIdRef t)+ case a of+ Nothing -> return ()+ Just pid ->+ invokeEvent p $ cancelProcessWithId pid+ invokeEvent p $ writeRef (transactProcessIdRef t) Nothing+ invokeEvent p $ writeRef (transactProcessContRef t) Nothing+ invokeEvent p $+ runProcess $+ do takeTransact t+ transferProcess transfer++-- | Register the queue entry in the transact.+registerTransactQueueEntry :: MonadDES m => Transact m a -> QueueEntry m -> Event m ()+{-# INLINABLE registerTransactQueueEntry #-}+registerTransactQueueEntry t e =+ Event $ \p ->+ do let q = entryQueue e+ m <- invokeEvent p $ readRef (transactQueueEntryRef t)+ case HM.lookup q m of+ Just e0 ->+ throwComp $+ SimulationRetry+ "There is already another queue entry for the specified queue: registerTransactQueueEntry"+ Nothing ->+ invokeEvent p $ writeRef (transactQueueEntryRef t) (HM.insert q e m)++-- | Unregister the queue entry from the transact.+unregisterTransactQueueEntry :: MonadDES m => Transact m a -> Queue m -> Event m (QueueEntry m)+{-# INLINABLE unregisterTransactQueueEntry #-}+unregisterTransactQueueEntry t q =+ Event $ \p ->+ do m <- invokeEvent p $ readRef (transactQueueEntryRef t)+ case HM.lookup q m of+ Nothing ->+ throwComp $+ SimulationRetry+ "There is no queue entry for the specified queue: unregisterTransactQueueEntry"+ Just e ->+ do invokeEvent p $ writeRef (transactQueueEntryRef t) (HM.delete q m)+ return e++-- | Assign the transact value and return a new version of the same transact.+assignTransactValue :: Transact m a -> (a -> b) -> Transact m b+assignTransactValue t f =+ let b = f (transactValue t)+ in t { transactValue = b }++-- | Assign the transact value and return a new version of the same transact.+assignTransactValueM :: Monad c => Transact m a -> (a -> c b) -> c (Transact m b)+{-# INLINABLE assignTransactValue #-}+assignTransactValueM t f =+ do b <- f (transactValue t)+ return t { transactValue = b }++-- | Assign the priority and return a new version of the same transact.+assignTransactPriority :: Transact m a -> Int -> Transact m a+assignTransactPriority t priority =+ t { transactPriority = priority }++-- | Reactivate the transacts or transfer them to the specified computations.+reactivateTransacts :: MonadDES m => [(Transact m a, Maybe (Process m ()))] -> Event m ()+{-# INLINABLE reactivateTransacts #-}+reactivateTransacts [] = return ()+reactivateTransacts ((t, Nothing): xs) =+ do pid <- requireTransactProcessId t+ reactivateProcess pid+ reactivateTransacts xs+reactivateTransacts ((t, Just transfer): xs) =+ do transferTransact t transfer+ reactivateTransacts xs
+ Simulation/Aivika/Trans/GPSS/TransactQueueStrategy.hs view
@@ -0,0 +1,165 @@++{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}++-- |+-- Module : Simulation.Aivika.Trans.GPSS.TransactQueueStrategy+-- Copyright : Copyright (c) 2017, David Sorokin <david.sorokin@gmail.com>+-- License : BSD3+-- Maintainer : David Sorokin <david.sorokin@gmail.com>+-- Stability : experimental+-- Tested with: GHC 8.0.2+--+-- This module defines a GPSS transact queue strategy.+--+module Simulation.Aivika.Trans.GPSS.TransactQueueStrategy+ (TransactQueueStrategy(..),+ transactStrategyQueueDeleteBy,+ transactStrategyQueueContainsBy) where++import Control.Monad+import Control.Monad.Trans++import Data.IORef+import qualified Data.IntMap as M++import Simulation.Aivika.Trans+import qualified Simulation.Aivika.Trans.DoubleLinkedList as DLL++-- | The transact queue strategy.+data TransactQueueStrategy s = TransactQueueStrategy s++-- | An implementation of the 'QueueStrategy' class.+instance MonadDES m => QueueStrategy m (TransactQueueStrategy s) where++ -- | A queue used by the 'TransactQueueStrategy' strategy.+ data StrategyQueue m (TransactQueueStrategy s) a =+ TransactStrategyQueue { transactStrategy :: TransactQueueStrategy s,+ -- ^ the strategy itself+ transactStrategyQueue :: Ref m (M.IntMap (DLL.DoubleLinkedList m a))+ -- ^ the transact queue+ }++ {-# INLINABLE newStrategyQueue #-}+ newStrategyQueue s =+ do r <- newRef M.empty+ return $ TransactStrategyQueue s r++ {-# INLINABLE strategyQueueNull #-}+ strategyQueueNull q =+ do m <- readRef (transactStrategyQueue q)+ return $ M.null m++instance MonadDES m => DequeueStrategy m (TransactQueueStrategy FCFS) where++ {-# INLINABLE strategyDequeue #-}+ strategyDequeue q =+ do m <- readRef (transactStrategyQueue q)+ let (k, xs) = M.findMin m+ i <- DLL.listFirst xs+ DLL.listRemoveFirst xs+ empty <- DLL.listNull xs+ when empty $+ modifyRef (transactStrategyQueue q) $+ M.delete k+ return i++instance MonadDES m => DequeueStrategy m (TransactQueueStrategy LCFS) where++ {-# INLINABLE strategyDequeue #-}+ strategyDequeue q =+ do m <- readRef (transactStrategyQueue q)+ let (k, xs) = M.findMin m+ i <- DLL.listLast xs+ DLL.listRemoveLast xs+ empty <- DLL.listNull xs+ when empty $+ modifyRef (transactStrategyQueue q) $+ M.delete k+ return i++instance (MonadDES m, DequeueStrategy m (TransactQueueStrategy s)) => PriorityQueueStrategy m (TransactQueueStrategy s) Int where++ {-# INLINABLE strategyEnqueueWithPriority #-}+ strategyEnqueueWithPriority q priority i =+ do m <- readRef (transactStrategyQueue q)+ let k = - priority+ xs = M.lookup k m+ case xs of+ Nothing ->+ do xs <- liftSimulation DLL.newList+ DLL.listAddLast xs i+ modifyRef (transactStrategyQueue q) $+ M.insert k xs+ Just xs ->+ DLL.listAddLast xs i++instance MonadDES m => DeletingQueueStrategy m (TransactQueueStrategy FCFS) where++ {-# INLINABLE strategyQueueDeleteBy #-}+ strategyQueueDeleteBy q pred =+ do m <- readRef (transactStrategyQueue q)+ let loop [] = return Nothing+ loop ((k, xs): tail) =+ do a <- DLL.listRemoveBy xs pred+ case a of+ Nothing -> loop tail+ Just _ ->+ do empty <- DLL.listNull xs+ when empty $+ modifyRef (transactStrategyQueue q) $+ M.delete k+ return a+ loop (M.assocs m)++ {-# INLINABLE strategyQueueContainsBy #-}+ strategyQueueContainsBy q pred =+ do m <- readRef (transactStrategyQueue q)+ let loop [] = return Nothing+ loop ((k, xs): tail) =+ do a <- DLL.listContainsBy xs pred+ case a of+ Nothing -> loop tail+ Just _ -> return a+ loop (M.assocs m)++-- | Try to delete the transact by the specified priority and satisfying to the provided predicate.+transactStrategyQueueDeleteBy :: MonadDES m+ => StrategyQueue m (TransactQueueStrategy s) a+ -- ^ the queue+ -> Int+ -- ^ the transact priority+ -> (a -> Bool)+ -- ^ the predicate+ -> Event m (Maybe a)+{-# INLINABLE transactStrategyQueueDeleteBy #-}+transactStrategyQueueDeleteBy q priority pred =+ do m <- readRef (transactStrategyQueue q)+ let k = - priority+ xs = M.lookup k m+ case xs of+ Nothing -> return Nothing+ Just xs ->+ do a <- DLL.listRemoveBy xs pred+ empty <- DLL.listNull xs+ when empty $+ modifyRef (transactStrategyQueue q) $+ M.delete k+ return a++-- | Test whether the queue contains a transact with the specified priority satisfying the provided predicate.+transactStrategyQueueContainsBy :: MonadDES m+ => StrategyQueue m (TransactQueueStrategy s) a+ -- ^ the queue+ -> Int+ -- ^ the transact priority+ -> (a -> Bool)+ -- ^ the predicate+ -> Event m (Maybe a)+{-# INLINABLE transactStrategyQueueContainsBy #-}+transactStrategyQueueContainsBy q priority pred =+ do m <- readRef (transactStrategyQueue q)+ let k = - priority+ xs = M.lookup k m+ case xs of+ Nothing -> return Nothing+ Just xs -> DLL.listContainsBy xs pred
+ aivika-gpss.cabal view
@@ -0,0 +1,158 @@+name: aivika-gpss+version: 0.3+synopsis: GPSS-like DSL for Aivika+description:+ This package allows defining simulation models in terms of a GPSS-like+ domain specific language, which can be useful if you are going to + translate your models from GPSS into Aivika [1, 2].+ .+ Note that the GPSS-like domain specific language is not equivalent to + the original GPSS language, but it may return very similar results in + some cases, while it can also return quite different results in other cases.+ .+ The package implements the most of GPSS simulation blocks, but the main difference+ is as follows.+ .+ Like GPSS, the package tries to treat the transact priorities properly within each block.+ Here it works in a very similar way even for such non-trivial blocks as PREEMPT, GATHER+ and ASSEMBLE. But unlike GPSS, the blocks behave independently from each other, where + the transact priorities are not used when deciding which of the blocks will be activated next.+ The order of activating blocks is unpredictable.+ .+ There is a layer of emulation. The package creates a new discontinuous process for each new transact. + Then that process becomes fully responsible for processing the transact by the blocks. Therefore, + the speed of simulation is slower that it could be when implementing the same model but based on + using the standard Aivika facilities. + .+ The rough estimation is that this particular package is slower in 2-3 times than the existent GPSS + simulators in case of sequential simulation. But the point is that you can combine GPSS with + discontinuous processes, events and even agents within the same model. Moreover, you can use GPSS+ in parallel and distributed simulation models. + .+ \[1] <http://hackage.haskell.org/package/aivika>+ .+ \[2] <http://hackage.haskell.org/package/aivika-transformers>+ .+category: Simulation+license: BSD3+license-file: LICENSE+copyright: (c) 2017. David Sorokin <david.sorokin@gmail.com>+author: David Sorokin+maintainer: David Sorokin <david.sorokin@gmail.com>+homepage: http://www.aivikasoft.com+cabal-version: >= 1.6+build-type: Simple+tested-with: GHC == 8.0.1++extra-source-files: examples/Example2A.hs+ examples/Example2ATrans.hs+ examples/Example2B.hs+ examples/Example2BTrans.hs+ examples/Example2C.hs+ examples/Example2CTrans.hs+ examples/Example2D.hs+ examples/Example2DTrans.hs+ examples/Example2E.hs+ examples/Example2ETrans.hs+ examples/Example7-26.hs+ examples/Example7-26Distributed.hs+ examples/Example7-26Trans.hs+ examples/Example7-28.hs+ examples/Example7-28Trans.hs+ examples/Example7-31.hs+ examples/Example7-31Trans.hs+ examples/Example7-35.hs+ examples/Example7-35Trans.hs++library++ exposed-modules: Simulation.Aivika.GPSS+ Simulation.Aivika.GPSS.Block+ Simulation.Aivika.GPSS.Block.Advance+ Simulation.Aivika.GPSS.Block.Assemble+ Simulation.Aivika.GPSS.Block.Assign+ Simulation.Aivika.GPSS.Block.Depart+ Simulation.Aivika.GPSS.Block.Enter+ Simulation.Aivika.GPSS.Block.Gather+ Simulation.Aivika.GPSS.Block.Generate+ Simulation.Aivika.GPSS.Block.Leave+ Simulation.Aivika.GPSS.Block.Link+ Simulation.Aivika.GPSS.Block.Loop+ Simulation.Aivika.GPSS.Block.Match+ Simulation.Aivika.GPSS.Block.Preempt+ Simulation.Aivika.GPSS.Block.Priority+ Simulation.Aivika.GPSS.Block.Queue+ Simulation.Aivika.GPSS.Block.Release+ Simulation.Aivika.GPSS.Block.Return+ Simulation.Aivika.GPSS.Block.Seize+ Simulation.Aivika.GPSS.Block.Split+ Simulation.Aivika.GPSS.Block.Terminate+ Simulation.Aivika.GPSS.Block.Test+ Simulation.Aivika.GPSS.Block.Transfer+ Simulation.Aivika.GPSS.Block.Unlink+ Simulation.Aivika.GPSS.AssemblySet+ Simulation.Aivika.GPSS.Facility+ Simulation.Aivika.GPSS.MatchChain+ Simulation.Aivika.GPSS.Queue+ Simulation.Aivika.GPSS.Results+ Simulation.Aivika.GPSS.Results.Locale+ Simulation.Aivika.GPSS.Results.Transform+ Simulation.Aivika.GPSS.Storage+ Simulation.Aivika.GPSS.Transact+ Simulation.Aivika.GPSS.TransactQueueStrategy+ Simulation.Aivika.Trans.GPSS+ Simulation.Aivika.Trans.GPSS.Block+ Simulation.Aivika.Trans.GPSS.Block.Advance+ Simulation.Aivika.Trans.GPSS.Block.Assemble+ Simulation.Aivika.Trans.GPSS.Block.Assign+ Simulation.Aivika.Trans.GPSS.Block.Depart+ Simulation.Aivika.Trans.GPSS.Block.Enter+ Simulation.Aivika.Trans.GPSS.Block.Gather+ Simulation.Aivika.Trans.GPSS.Block.Generate+ Simulation.Aivika.Trans.GPSS.Block.Leave+ Simulation.Aivika.Trans.GPSS.Block.Link+ Simulation.Aivika.Trans.GPSS.Block.Loop+ Simulation.Aivika.Trans.GPSS.Block.Match+ Simulation.Aivika.Trans.GPSS.Block.Preempt+ Simulation.Aivika.Trans.GPSS.Block.Priority+ Simulation.Aivika.Trans.GPSS.Block.Queue+ Simulation.Aivika.Trans.GPSS.Block.Release+ Simulation.Aivika.Trans.GPSS.Block.Return+ Simulation.Aivika.Trans.GPSS.Block.Seize+ Simulation.Aivika.Trans.GPSS.Block.Split+ Simulation.Aivika.Trans.GPSS.Block.Terminate+ Simulation.Aivika.Trans.GPSS.Block.Test+ Simulation.Aivika.Trans.GPSS.Block.Transfer+ Simulation.Aivika.Trans.GPSS.Block.Unlink+ Simulation.Aivika.Trans.GPSS.AssemblySet+ Simulation.Aivika.Trans.GPSS.Facility+ Simulation.Aivika.Trans.GPSS.MatchChain+ Simulation.Aivika.Trans.GPSS.Queue+ Simulation.Aivika.Trans.GPSS.Results+ Simulation.Aivika.Trans.GPSS.Results.Locale+ Simulation.Aivika.Trans.GPSS.Results.Transform+ Simulation.Aivika.Trans.GPSS.Storage+ Simulation.Aivika.Trans.GPSS.Transact+ Simulation.Aivika.Trans.GPSS.TransactQueueStrategy+ + build-depends: base >= 4.2 && < 6,+ mtl >= 1.1.0.2,+ containers >= 0.4.0.0,+ hashable >= 1.2.0.0,+ unordered-containers >= 0.1.0.0,+ aivika >= 5.2,+ aivika-transformers >= 5.2++ extensions: MultiParamTypeClasses,+ TypeFamilies,+ FlexibleInstances,+ FlexibleContexts,+ KindSignatures+ + ghc-options: -O2+ +source-repository head++ type: git+ location: https://github.com/dsorokin/aivika-gpss+
+ examples/Example2A.hs view
@@ -0,0 +1,57 @@++{-+ GENERATE 18,6+ QUEUE JOEQ+ SEIZE JOE+ DEPART JOEQ+ ADVANCE 16,4+ RELEASE JOE+ TERMINATE++ GENERATE 480+ TERMINATE 1++ START 1+ -}++import Control.Category+import Control.Monad.Trans++import Simulation.Aivika+import Simulation.Aivika.GPSS+import qualified Simulation.Aivika.GPSS.Queue as Q++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 480.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation Results+model =+ do joeq <- runEventInStartTime Q.newQueue+ joe <- runEventInStartTime newFacility++ let clientStream = randomUniformStream (18 - 6) (18 + 6)++ let clients = streamGeneratorBlock0 clientStream+ chain =+ queueBlock joeq 1 >>>+ seizeBlock joe >>>+ departBlock joeq 1 >>>+ advanceBlock (randomUniformProcess_ (16 - 4) (16 + 4)) >>>+ releaseBlock joe >>>+ terminateBlock++ runProcessInStartTime $+ runGeneratorBlock clients chain + + return $+ results+ [resultSource "joeq" "The Joe queue" joeq,+ resultSource "joe" "Joe" joe]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example2ATrans.hs view
@@ -0,0 +1,61 @@++{-+ GENERATE 18,6+ QUEUE JOEQ+ SEIZE JOE+ DEPART JOEQ+ ADVANCE 16,4+ RELEASE JOE+ TERMINATE++ GENERATE 480+ TERMINATE 1++ START 1+ -}++import Control.Category+import Control.Monad.Trans++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS+import qualified Simulation.Aivika.Trans.GPSS.Queue as Q++import Simulation.Aivika.IO++type DES = IO++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 480.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation DES (Results DES)+model =+ do joeq <- runEventInStartTime Q.newQueue+ joe <- runEventInStartTime newFacility++ let clientStream = randomUniformStream (18 - 6) (18 + 6)++ let clients = streamGeneratorBlock0 clientStream+ chain =+ queueBlock joeq 1 >>>+ seizeBlock joe >>>+ departBlock joeq 1 >>>+ advanceBlock (randomUniformProcess_ (16 - 4) (16 + 4)) >>>+ releaseBlock joe >>>+ terminateBlock++ runProcessInStartTime $+ runGeneratorBlock clients chain + + return $+ results+ [resultSource "joeq" "The Joe queue" joeq,+ resultSource "joe" "Joe" joe]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example2B.hs view
@@ -0,0 +1,80 @@++{-+ GENERATE 35,10+ QUEUE JOEQ+ SEIZE JOE+ DEPART JOEQ+ ADVANCE 18,6+ RELEASE JOE+ TERMINATE++ GENERATE 60,20+ QUEUE JOEQ+ SEIZE JOE+ DEPART JOEQ+ ADVANCE 10,2+ ADVANCE 18,6+ RELEASE JOE+ TERMINATE++ GENERATE 480+ TERMINATE 1++ START 1+ -}++import Control.Category+import Control.Monad.Trans++import Simulation.Aivika+import Simulation.Aivika.GPSS+import qualified Simulation.Aivika.GPSS.Queue as Q++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 480.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation Results+model =+ do joeq <- runEventInStartTime Q.newQueue+ joe <- runEventInStartTime newFacility++ let clientStream1 = randomUniformStream (35 - 10) (35 + 10)+ clientStream2 = randomUniformStream (60 - 20) (60 + 20)++ let clients1 = streamGeneratorBlock0 clientStream1+ chain1 =+ queueBlock joeq 1 >>>+ seizeBlock joe >>>+ departBlock joeq 1 >>>+ advanceBlock (randomUniformProcess_ (18 - 6) (18 + 6)) >>>+ releaseBlock joe >>>+ terminateBlock++ let clients2 = streamGeneratorBlock0 clientStream2+ chain2 =+ queueBlock joeq 1 >>>+ seizeBlock joe >>>+ departBlock joeq 1 >>>+ advanceBlock (randomUniformProcess_ (10 - 2) (10 + 2)) >>>+ advanceBlock (randomUniformProcess_ (18 - 6) (18 + 6)) >>>+ releaseBlock joe >>>+ terminateBlock++ runProcessInStartTime $+ runGeneratorBlock clients1 chain1 ++ runProcessInStartTime $+ runGeneratorBlock clients2 chain2 + + return $+ results+ [resultSource "joeq" "The Joe queue" joeq,+ resultSource "joe" "Joe" joe]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example2BTrans.hs view
@@ -0,0 +1,84 @@++{-+ GENERATE 35,10+ QUEUE JOEQ+ SEIZE JOE+ DEPART JOEQ+ ADVANCE 18,6+ RELEASE JOE+ TERMINATE++ GENERATE 60,20+ QUEUE JOEQ+ SEIZE JOE+ DEPART JOEQ+ ADVANCE 10,2+ ADVANCE 18,6+ RELEASE JOE+ TERMINATE++ GENERATE 480+ TERMINATE 1++ START 1+ -}++import Control.Category+import Control.Monad.Trans++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS+import qualified Simulation.Aivika.Trans.GPSS.Queue as Q++import Simulation.Aivika.IO++type DES = IO++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 480.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation DES (Results DES)+model =+ do joeq <- runEventInStartTime Q.newQueue+ joe <- runEventInStartTime newFacility++ let clientStream1 = randomUniformStream (35 - 10) (35 + 10)+ clientStream2 = randomUniformStream (60 - 20) (60 + 20)++ let clients1 = streamGeneratorBlock0 clientStream1+ chain1 =+ queueBlock joeq 1 >>>+ seizeBlock joe >>>+ departBlock joeq 1 >>>+ advanceBlock (randomUniformProcess_ (18 - 6) (18 + 6)) >>>+ releaseBlock joe >>>+ terminateBlock++ let clients2 = streamGeneratorBlock0 clientStream2+ chain2 =+ queueBlock joeq 1 >>>+ seizeBlock joe >>>+ departBlock joeq 1 >>>+ advanceBlock (randomUniformProcess_ (10 - 2) (10 + 2)) >>>+ advanceBlock (randomUniformProcess_ (18 - 6) (18 + 6)) >>>+ releaseBlock joe >>>+ terminateBlock++ runProcessInStartTime $+ runGeneratorBlock clients1 chain1 ++ runProcessInStartTime $+ runGeneratorBlock clients2 chain2 + + return $+ results+ [resultSource "joeq" "The Joe queue" joeq,+ resultSource "joe" "Joe" joe]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example2C.hs view
@@ -0,0 +1,78 @@++{-+ GENERATE 420,360,,,1+ QUEUE LINE+ SEIZE CLERK+ DEPART LINE+ ADVANCE 300,90+ RELEASE CLERK+ TERMINATE++ GENERATE 360,240,,,2+ QUEUE LINE+ SEIZE CLERK+ DEPART LINE+ ADVANCE 100,30+ RELEASE CLERK+ TERMINATE++ GENERATE 28800+ TERMINATE 1++ START 1+ -}++import Control.Category+import Control.Monad.Trans++import Simulation.Aivika+import Simulation.Aivika.GPSS+import qualified Simulation.Aivika.GPSS.Queue as Q++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 28800.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation Results+model =+ do line <- runEventInStartTime Q.newQueue+ clerk <- runEventInStartTime newFacility++ let workerStream1 = randomUniformStream (420 - 360) (420 + 360)+ workerStream2 = randomUniformStream (360 - 240) (360 + 240)++ let workers1 = streamGeneratorBlock workerStream1 1+ chain1 =+ queueBlock line 1 >>>+ seizeBlock clerk >>>+ departBlock line 1 >>>+ advanceBlock (randomUniformProcess_ (300 - 90) (300 + 90)) >>>+ releaseBlock clerk >>>+ terminateBlock++ let workers2 = streamGeneratorBlock workerStream2 2+ chain2 =+ queueBlock line 1 >>>+ seizeBlock clerk >>>+ departBlock line 1 >>>+ advanceBlock (randomUniformProcess_ (100 - 30) (100 + 30)) >>>+ releaseBlock clerk >>>+ terminateBlock++ runProcessInStartTime $+ runGeneratorBlock workers1 chain1 ++ runProcessInStartTime $+ runGeneratorBlock workers2 chain2 + + return $+ results+ [resultSource "line" "Line" line,+ resultSource "clerk" "Clerk" clerk]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example2CTrans.hs view
@@ -0,0 +1,82 @@++{-+ GENERATE 420,360,,,1+ QUEUE LINE+ SEIZE CLERK+ DEPART LINE+ ADVANCE 300,90+ RELEASE CLERK+ TERMINATE++ GENERATE 360,240,,,2+ QUEUE LINE+ SEIZE CLERK+ DEPART LINE+ ADVANCE 100,30+ RELEASE CLERK+ TERMINATE++ GENERATE 28800+ TERMINATE 1++ START 1+ -}++import Control.Category+import Control.Monad.Trans++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS+import qualified Simulation.Aivika.Trans.GPSS.Queue as Q++import Simulation.Aivika.IO++type DES = IO++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 28800.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation DES (Results DES)+model =+ do line <- runEventInStartTime Q.newQueue+ clerk <- runEventInStartTime newFacility++ let workerStream1 = randomUniformStream (420 - 360) (420 + 360)+ workerStream2 = randomUniformStream (360 - 240) (360 + 240)++ let workers1 = streamGeneratorBlock workerStream1 1+ chain1 =+ queueBlock line 1 >>>+ seizeBlock clerk >>>+ departBlock line 1 >>>+ advanceBlock (randomUniformProcess_ (300 - 90) (300 + 90)) >>>+ releaseBlock clerk >>>+ terminateBlock++ let workers2 = streamGeneratorBlock workerStream2 2+ chain2 =+ queueBlock line 1 >>>+ seizeBlock clerk >>>+ departBlock line 1 >>>+ advanceBlock (randomUniformProcess_ (100 - 30) (100 + 30)) >>>+ releaseBlock clerk >>>+ terminateBlock++ runProcessInStartTime $+ runGeneratorBlock workers1 chain1 ++ runProcessInStartTime $+ runGeneratorBlock workers2 chain2 + + return $+ results+ [resultSource "line" "Line" line,+ resultSource "clerk" "Clerk" clerk]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example2D.hs view
@@ -0,0 +1,55 @@++{-+ GENERATE ,,,4+Back1 ADVANCE 30,5+ SEIZE OVEN+ ADVANCE 8,2+ RELEASE OVEN+ TRANSFER ,Back1++ GENERATE 2400+ TERMINATE 1++ START 1+ -}++import Control.Category+import Control.Monad.Trans++import Simulation.Aivika+import Simulation.Aivika.GPSS+import qualified Simulation.Aivika.GPSS.Queue as Q++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 2400.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation Results+model =+ do oven <- runEventInStartTime newFacility++ let assemblerStream = takeStream 4 $+ randomUniformIntStream 0 0++ let assemblers = streamGeneratorBlock0 assemblerStream+ chain = back+ back =+ advanceBlock (randomUniformProcess_ (30 - 5) (30 + 5)) >>>+ seizeBlock oven >>>+ advanceBlock (randomUniformProcess_ (8 - 2) (8 + 2)) >>>+ releaseBlock oven >>>+ transferBlock back++ runProcessInStartTime $+ runGeneratorBlock assemblers chain + + return $+ results+ [resultSource "oven" "Oven" oven]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example2DTrans.hs view
@@ -0,0 +1,59 @@++{-+ GENERATE ,,,4+Back1 ADVANCE 30,5+ SEIZE OVEN+ ADVANCE 8,2+ RELEASE OVEN+ TRANSFER ,Back1++ GENERATE 2400+ TERMINATE 1++ START 1+ -}++import Control.Category+import Control.Monad.Trans++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS+import qualified Simulation.Aivika.Trans.GPSS.Queue as Q++import Simulation.Aivika.IO++type DES = IO++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 2400.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation DES (Results DES)+model =+ do oven <- runEventInStartTime newFacility++ let assemblerStream = takeStream 4 $+ randomUniformIntStream 0 0++ let assemblers = streamGeneratorBlock0 assemblerStream+ chain = back+ back =+ advanceBlock (randomUniformProcess_ (30 - 5) (30 + 5)) >>>+ seizeBlock oven >>>+ advanceBlock (randomUniformProcess_ (8 - 2) (8 + 2)) >>>+ releaseBlock oven >>>+ transferBlock back++ runProcessInStartTime $+ runGeneratorBlock assemblers chain + + return $+ results+ [resultSource "oven" "Oven" oven]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example2E.hs view
@@ -0,0 +1,65 @@++{-++MEN STORAGE 3+NOWON STORAGE 50++ GENERATE ,,,53+Back1 ENTER NOWON+ ADVANCE 157,25+ LEAVE NOWON+ ENTER MEN+ ADVANCE 7,3+ LEAVE MEN+ TRANSFER ,Back1++ GENERATE 6240+ TERMINATE 1++ START 1+ -}++import Control.Category+import Control.Monad.Trans++import Simulation.Aivika+import Simulation.Aivika.GPSS+import qualified Simulation.Aivika.GPSS.Queue as Q++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 6240.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation Results+model =+ do men <- runEventInStartTime $ newStorage 3+ nowon <- runEventInStartTime $ newStorage 50++ let machineStream = takeStream 53 $+ randomUniformIntStream 0 0++ let machines = streamGeneratorBlock0 machineStream+ chain = back+ back =+ enterBlock nowon 1 >>>+ advanceBlock (randomUniformProcess_ (157 - 25) (157 + 25)) >>>+ leaveBlock nowon 1 >>>+ enterBlock men 1 >>>+ advanceBlock (randomUniformProcess_ (7 - 3) (7 + 3)) >>>+ leaveBlock men 1 >>>+ transferBlock back++ runProcessInStartTime $+ runGeneratorBlock machines chain + + return $+ results+ [resultSource "men" "Men" men,+ resultSource "nowon" "NowOn" nowon]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example2ETrans.hs view
@@ -0,0 +1,69 @@++{-++MEN STORAGE 3+NOWON STORAGE 50++ GENERATE ,,,53+Back1 ENTER NOWON+ ADVANCE 157,25+ LEAVE NOWON+ ENTER MEN+ ADVANCE 7,3+ LEAVE MEN+ TRANSFER ,Back1++ GENERATE 6240+ TERMINATE 1++ START 1+ -}++import Control.Category+import Control.Monad.Trans++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS+import qualified Simulation.Aivika.Trans.GPSS.Queue as Q++import Simulation.Aivika.IO++type DES = IO++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 6240.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation IO (Results IO)+model =+ do men <- runEventInStartTime $ newStorage 3+ nowon <- runEventInStartTime $ newStorage 50++ let machineStream = takeStream 53 $+ randomUniformIntStream 0 0++ let machines = streamGeneratorBlock0 machineStream+ chain = back+ back =+ enterBlock nowon 1 >>>+ advanceBlock (randomUniformProcess_ (157 - 25) (157 + 25)) >>>+ leaveBlock nowon 1 >>>+ enterBlock men 1 >>>+ advanceBlock (randomUniformProcess_ (7 - 3) (7 + 3)) >>>+ leaveBlock men 1 >>>+ transferBlock back++ runProcessInStartTime $+ runGeneratorBlock machines chain + + return $+ results+ [resultSource "men" "Men" men,+ resultSource "nowon" "NowOn" nowon]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example7-26.hs view
@@ -0,0 +1,99 @@++{-+ GENERATE 2000,500,,,1+ GATE NI PROF,Busy+ PREEMPT PROF,PR,Add,5+ ADVANCE (Exponential(1,0,200))+ RETURN PROF+Busy TERMINATE++ GENERATE 2000,500+ QUEUE LINE+ SEIZE PROF+ DEPART LINE+ ADVANCE (Exponential(1,0,1000))+LetGo RELEASE PROF+ TERMINATE++Add ASSIGN 5+,300+ ADVANCE P5+ TRANSFER ,LetGo++ GENERATE 10000000+ TERMINATE 1++ START 1+ -}++import Prelude hiding (id)++import Control.Category+import Control.Monad.Trans++import Data.Maybe++import Simulation.Aivika+import Simulation.Aivika.GPSS+import qualified Simulation.Aivika.GPSS.Queue as Q++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 10000000.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation Results+model =+ do line <- runEventInStartTime Q.newQueue+ prof <- runEventInStartTime newFacility++ let phoneCallStream = randomUniformStream (2000 - 500) (2000 + 500)+ studentStream = randomUniformStream (2000 - 500) (2000 + 500)++ let phoneCalls = streamGeneratorBlock phoneCallStream 1+ phoneCallChain =+ Block (\a ->+ do f <- liftEvent (facilityInterrupted prof)+ if f+ then blockProcess (transferBlock busy) a+ else return a) >>>+ preemptBlock prof+ (PreemptBlockMode { preemptBlockPriorityMode = True,+ preemptBlockTransfer = Just add,+ -- preemptBlockTransfer = Nothing,+ preemptBlockRemoveMode = False }) >>>+ advanceBlock (randomExponentialProcess_ 200) >>>+ returnBlock prof >>>+ busy+ busy = terminateBlock+ + students = streamGeneratorBlock studentStream 0+ studentChain =+ queueBlock line 1 >>>+ seizeBlock prof >>>+ departBlock line 1 >>>+ advanceBlock (randomExponentialProcess_ 1000) >>>+ letGo+ letGo =+ releaseBlock prof >>>+ terminateBlock+ add dt0 =+ let dt = maybe 0 id dt0+ in advanceBlock (holdProcess (dt + 300)) >>>+ transferBlock letGo++ runProcessInStartTime $+ runGeneratorBlock phoneCalls phoneCallChain ++ runProcessInStartTime $+ runGeneratorBlock students studentChain + + return $+ results+ [resultSource "line" "Line" line,+ resultSource "prof" "Prof" prof]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example7-26Distributed.hs view
@@ -0,0 +1,130 @@++{-+ GENERATE 2000,500,,,1+ GATE NI PROF,Busy+ PREEMPT PROF,PR,Add,5+ ADVANCE (Exponential(1,0,200))+ RETURN PROF+Busy TERMINATE++ GENERATE 2000,500+ QUEUE LINE+ SEIZE PROF+ DEPART LINE+ ADVANCE (Exponential(1,0,1000))+LetGo RELEASE PROF+ TERMINATE++Add ASSIGN 5+,300+ ADVANCE P5+ TRANSFER ,LetGo++ GENERATE 10000000+ TERMINATE 1++ START 1+ -}++import Prelude hiding (id, (.))++import Control.Category+import Control.Monad.Trans+import qualified Control.Distributed.Process as DP+import Control.Distributed.Process.Node (initRemoteTable)+import Control.Distributed.Process.Backend.SimpleLocalnet++import Data.Maybe++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS+import qualified Simulation.Aivika.Trans.GPSS.Queue as Q++import Simulation.Aivika.Distributed++type DES = DIO++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 10000000.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation DES ()+model =+ do line <- runEventInStartTime Q.newQueue+ prof <- runEventInStartTime newFacility++ let phoneCallStream = randomUniformStream (2000 - 500) (2000 + 500)+ studentStream = randomUniformStream (2000 - 500) (2000 + 500)++ let phoneCalls = streamGeneratorBlock phoneCallStream 1+ phoneCallChain =+ Block (\a ->+ do f <- liftEvent (facilityInterrupted prof)+ if f+ then blockProcess (transferBlock busy) a+ else return a) >>>+ preemptBlock prof+ (PreemptBlockMode { preemptBlockPriorityMode = True,+ preemptBlockTransfer = Just add,+ -- preemptBlockTransfer = Nothing,+ preemptBlockRemoveMode = False }) >>>+ advanceBlock (randomExponentialProcess_ 200) >>>+ returnBlock prof >>>+ busy+ busy = terminateBlock+ + students = streamGeneratorBlock studentStream 0+ studentChain =+ queueBlock line 1 >>>+ seizeBlock prof >>>+ departBlock line 1 >>>+ advanceBlock (randomExponentialProcess_ 1000) >>>+ letGo+ letGo =+ releaseBlock prof >>>+ terminateBlock+ add dt0 =+ let dt = maybe 0 id dt0+ in advanceBlock (holdProcess (dt + 300)) >>>+ transferBlock letGo++ runProcessInStartTime $+ runGeneratorBlock phoneCalls phoneCallChain ++ runProcessInStartTime $+ runGeneratorBlock students studentChain + + let rs =+ results+ [resultSource "line" "Line" line,+ resultSource "prof" "Prof" prof]++ printResultsInStopTime printResultSourceInEnglish rs+ +runModel :: DP.ProcessId -> DP.Process ()+runModel timeServerId =+ do DP.say "Started simulating..."+ let ps = defaultDIOParams { dioLoggingPriority = NOTICE }+ m =+ do registerDIO+ a <- runSimulation model specs+ terminateDIO+ return a+ (modelId, modelProcess) <- runDIO m ps timeServerId+ modelProcess++master = \backend nodes ->+ do liftIO . putStrLn $ "Slaves: " ++ show nodes+ let timeServerParams = defaultTimeServerParams { tsLoggingPriority = NOTICE }+ timeServerId <- DP.spawnLocal $ timeServer 1 timeServerParams+ runModel timeServerId++main :: IO ()+main = do+ backend <- initializeBackend "localhost" "8080" rtable+ startMaster backend (master backend)+ where+ rtable :: DP.RemoteTable+ -- rtable = __remoteTable initRemoteTable+ rtable = initRemoteTable
+ examples/Example7-26Trans.hs view
@@ -0,0 +1,103 @@++{-+ GENERATE 2000,500,,,1+ GATE NI PROF,Busy+ PREEMPT PROF,PR,Add,5+ ADVANCE (Exponential(1,0,200))+ RETURN PROF+Busy TERMINATE++ GENERATE 2000,500+ QUEUE LINE+ SEIZE PROF+ DEPART LINE+ ADVANCE (Exponential(1,0,1000))+LetGo RELEASE PROF+ TERMINATE++Add ASSIGN 5+,300+ ADVANCE P5+ TRANSFER ,LetGo++ GENERATE 10000000+ TERMINATE 1++ START 1+ -}++import Prelude hiding (id)++import Control.Category+import Control.Monad.Trans++import Data.Maybe++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS+import qualified Simulation.Aivika.Trans.GPSS.Queue as Q++import Simulation.Aivika.IO++type DES = IO++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 10000000.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation DES (Results DES)+model =+ do line <- runEventInStartTime Q.newQueue+ prof <- runEventInStartTime newFacility++ let phoneCallStream = randomUniformStream (2000 - 500) (2000 + 500)+ studentStream = randomUniformStream (2000 - 500) (2000 + 500)++ let phoneCalls = streamGeneratorBlock phoneCallStream 1+ phoneCallChain =+ Block (\a ->+ do f <- liftEvent (facilityInterrupted prof)+ if f+ then blockProcess (transferBlock busy) a+ else return a) >>>+ preemptBlock prof+ (PreemptBlockMode { preemptBlockPriorityMode = True,+ preemptBlockTransfer = Just add,+ -- preemptBlockTransfer = Nothing,+ preemptBlockRemoveMode = False }) >>>+ advanceBlock (randomExponentialProcess_ 200) >>>+ returnBlock prof >>>+ busy+ busy = terminateBlock+ + students = streamGeneratorBlock studentStream 0+ studentChain =+ queueBlock line 1 >>>+ seizeBlock prof >>>+ departBlock line 1 >>>+ advanceBlock (randomExponentialProcess_ 1000) >>>+ letGo+ letGo =+ releaseBlock prof >>>+ terminateBlock+ add dt0 =+ let dt = maybe 0 id dt0+ in advanceBlock (holdProcess (dt + 300)) >>>+ transferBlock letGo++ runProcessInStartTime $+ runGeneratorBlock phoneCalls phoneCallChain ++ runProcessInStartTime $+ runGeneratorBlock students studentChain + + return $+ results+ [resultSource "line" "Line" line,+ resultSource "prof" "Prof" prof]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example7-28.hs view
@@ -0,0 +1,75 @@++{-+TATYM TABLE M1,200,10,10++ GENERATE ,,,1+Back1 MARK+ SEIZE WORKR+ SPLIT 1 Back1+ ADVANCE 16,3+ RELEASE WORKR+ ASSEMBLE 12+ TABULATE TATYM+ TERMINATE++ GENERATE 10000+ TERMINATE 1++ START 1+ -}++import Prelude hiding (id)++import Control.Category+import Control.Monad.Trans++import Data.Maybe++import Simulation.Aivika+import Simulation.Aivika.GPSS++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 10000.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation Results+model =+ do workr <- runEventInStartTime newFacility+ stats <- newRef emptySamplingStats++ let bottleStream = takeStream 1 $+ randomUniformStream 0 0++ let bottles = streamGeneratorBlock bottleStream 0+ bottleChain =+ Block (\a ->+ do t <- liftDynamics time+ return $ assignTransactValue a (const t)) >>>+ seizeBlock workr >>>+ splitBlock [bottleChain] >>>+ advanceBlock (randomUniformProcess_ (16 - 3) (16 + 3)) >>>+ releaseBlock workr >>>+ assembleBlock 12 >>>+ Block (\a ->+ do t <- liftDynamics time+ let t0 = transactValue a+ liftEvent $+ modifyRef stats $+ addSamplingStats (t - t0)+ return a) >>>+ terminateBlock++ runProcessInStartTime $+ runGeneratorBlock bottles bottleChain + + return $+ results+ [resultSource "workr" "WORKR" workr,+ resultSource "stats" "TATYM" stats]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example7-28Trans.hs view
@@ -0,0 +1,79 @@++{-+TATYM TABLE M1,200,10,10++ GENERATE ,,,1+Back1 MARK+ SEIZE WORKR+ SPLIT 1 Back1+ ADVANCE 16,3+ RELEASE WORKR+ ASSEMBLE 12+ TABULATE TATYM+ TERMINATE++ GENERATE 10000+ TERMINATE 1++ START 1+ -}++import Prelude hiding (id)++import Control.Category+import Control.Monad.Trans++import Data.Maybe++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS++import Simulation.Aivika.IO++type DES = IO++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 10000.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation DES (Results DES)+model =+ do workr <- runEventInStartTime newFacility+ stats <- newRef emptySamplingStats++ let bottleStream = takeStream 1 $+ randomUniformStream 0 0++ let bottles = streamGeneratorBlock bottleStream 0+ bottleChain =+ Block (\a ->+ do t <- liftDynamics time+ return $ assignTransactValue a (const t)) >>>+ seizeBlock workr >>>+ splitBlock [bottleChain] >>>+ advanceBlock (randomUniformProcess_ (16 - 3) (16 + 3)) >>>+ releaseBlock workr >>>+ assembleBlock 12 >>>+ Block (\a ->+ do t <- liftDynamics time+ let t0 = transactValue a+ liftEvent $+ modifyRef stats $+ addSamplingStats (t - t0)+ return a) >>>+ terminateBlock++ runProcessInStartTime $+ runGeneratorBlock bottles bottleChain + + return $+ results+ [resultSource "workr" "WORKR" workr,+ resultSource "stats" "TATYM" stats]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example7-31.hs view
@@ -0,0 +1,87 @@++{-+TATYM TABLE M1,450,20,20++ GENERATE ,,,1+Back1 MARK+ SEIZE WORKR+ ADVANCE 8,3+ SPLIT 1,Back1+ RELEASE WORKR+ PRIORITY 1+ GATHER 24++ SEIZE WORKR+ ADVANCE 16,3+ RELEASE WORKR+ ASSEMBLE 12+ TABULATE TATYM+ TERMINATE++ GENERATE 100000+ TERMINATE 1++ START 1+ -}++import Prelude hiding (id)++import Control.Category+import Control.Monad.Trans++import Data.Maybe++import Simulation.Aivika+import Simulation.Aivika.GPSS++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 100000.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation Results+model =+ do workr <- runEventInStartTime newFacility+ stats <- newRef emptySamplingStats++ let bottleStream = takeStream 1 $+ randomUniformStream 0 0++ let bottles = streamGeneratorBlock bottleStream 0+ bottleChain =+ Block (\a ->+ do t <- liftDynamics time+ return $ assignTransactValue a (const t)) >>>+ seizeBlock workr >>>+ advanceBlock (randomUniformProcess_ (8 - 3) (8 + 3)) >>>+ splitBlock [bottleChain] >>>+ releaseBlock workr >>>+ priorityBlock 1 >>>+ gatherBlock 24 >>>+ + seizeBlock workr >>>+ advanceBlock (randomUniformProcess_ (16 - 3) (16 + 3)) >>>+ releaseBlock workr >>>+ assembleBlock 12 >>>+ Block (\a ->+ do t <- liftDynamics time+ let t0 = transactValue a+ liftEvent $+ modifyRef stats $+ addSamplingStats (t - t0)+ return a) >>>+ terminateBlock++ runProcessInStartTime $+ runGeneratorBlock bottles bottleChain + + return $+ results+ [resultSource "workr" "WORKR" workr,+ resultSource "stats" "TATYM" stats]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example7-31Trans.hs view
@@ -0,0 +1,91 @@++{-+TATYM TABLE M1,450,20,20++ GENERATE ,,,1+Back1 MARK+ SEIZE WORKR+ ADVANCE 8,3+ SPLIT 1,Back1+ RELEASE WORKR+ PRIORITY 1+ GATHER 24++ SEIZE WORKR+ ADVANCE 16,3+ RELEASE WORKR+ ASSEMBLE 12+ TABULATE TATYM+ TERMINATE++ GENERATE 100000+ TERMINATE 1++ START 1+ -}++import Prelude hiding (id)++import Control.Category+import Control.Monad.Trans++import Data.Maybe++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS++import Simulation.Aivika.IO++type DES = IO++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 100000.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation DES (Results DES)+model =+ do workr <- runEventInStartTime newFacility+ stats <- newRef emptySamplingStats++ let bottleStream = takeStream 1 $+ randomUniformStream 0 0++ let bottles = streamGeneratorBlock bottleStream 0+ bottleChain =+ Block (\a ->+ do t <- liftDynamics time+ return $ assignTransactValue a (const t)) >>>+ seizeBlock workr >>>+ advanceBlock (randomUniformProcess_ (8 - 3) (8 + 3)) >>>+ splitBlock [bottleChain] >>>+ releaseBlock workr >>>+ priorityBlock 1 >>>+ gatherBlock 24 >>>+ + seizeBlock workr >>>+ advanceBlock (randomUniformProcess_ (16 - 3) (16 + 3)) >>>+ releaseBlock workr >>>+ assembleBlock 12 >>>+ Block (\a ->+ do t <- liftDynamics time+ let t0 = transactValue a+ liftEvent $+ modifyRef stats $+ addSamplingStats (t - t0)+ return a) >>>+ terminateBlock++ runProcessInStartTime $+ runGeneratorBlock bottles bottleChain + + return $+ results+ [resultSource "workr" "WORKR" workr,+ resultSource "stats" "TATYM" stats]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example7-35.hs view
@@ -0,0 +1,98 @@++{-+TATYM TABLE M1,150,20,20++ GENERATE 300,200+ MARK+ SPLIT 1,Route2+ SEIZE MAN1+ ADVANCE 100,20+Block1 MATCH Block2+ ADVANCE 50,5+Merge ASSEMBLE 2+ RELEASE MAN1+ TABULATE TATYM+ TERMINATE++Route2 SEIZE MAN2+ ADVANCE 110,25+Block2 MATCH Block1+ ADVANCE 70,10+ RELEASE MAN2+ TRANSFER ,Merge ++ GENERATE 100000+ TERMINATE 1++ START 1+ -}++import Prelude hiding (id)++import Control.Category+import Control.Monad.Trans++import Data.Maybe++import Simulation.Aivika+import Simulation.Aivika.GPSS++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 100000.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation Results+model =+ do man1 <- runEventInStartTime newFacility+ man2 <- runEventInStartTime newFacility+ match <- newMatchChain+ stats <- newRef emptySamplingStats++ let orderStream = randomUniformStream (300 - 200) (300 + 200)++ let orders = streamGeneratorBlock0 orderStream+ orderChain =+ Block (\a ->+ do t <- liftDynamics time+ return $ assignTransactValue a (const t)) >>>+ splitBlock [route2] >>>+ seizeBlock man1 >>>+ advanceBlock (randomUniformProcess_ (100 - 20) (100 + 20)) >>>+ matchBlock match >>>+ advanceBlock (randomUniformProcess_ (50 - 5) (50 + 5)) >>>+ merge+ merge =+ assembleBlock 2 >>>+ releaseBlock man1 >>>+ Block (\a ->+ do t <- liftDynamics time+ let t0 = transactValue a+ liftEvent $+ modifyRef stats $+ addSamplingStats (t - t0)+ return a) >>>+ terminateBlock++ route2 =+ seizeBlock man2 >>>+ advanceBlock (randomUniformProcess_ (110 - 25) (110 + 25)) >>>+ matchBlock match >>>+ advanceBlock (randomUniformProcess_ (70 - 10) (70 + 10)) >>>+ releaseBlock man2 >>>+ transferBlock merge+ + runProcessInStartTime $+ runGeneratorBlock orders orderChain + + return $+ results+ [resultSource "man1" "MAN1" man1,+ resultSource "man2" "MAN2" man2,+ resultSource "stats" "TATYM" stats]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs
+ examples/Example7-35Trans.hs view
@@ -0,0 +1,102 @@++{-+TATYM TABLE M1,150,20,20++ GENERATE 300,200+ MARK+ SPLIT 1,Route2+ SEIZE MAN1+ ADVANCE 100,20+Block1 MATCH Block2+ ADVANCE 50,5+Merge ASSEMBLE 2+ RELEASE MAN1+ TABULATE TATYM+ TERMINATE++Route2 SEIZE MAN2+ ADVANCE 110,25+Block2 MATCH Block1+ ADVANCE 70,10+ RELEASE MAN2+ TRANSFER ,Merge ++ GENERATE 100000+ TERMINATE 1++ START 1+ -}++import Prelude hiding (id)++import Control.Category+import Control.Monad.Trans++import Data.Maybe++import Simulation.Aivika.Trans+import Simulation.Aivika.Trans.GPSS++import Simulation.Aivika.IO++type DES = IO++specs = Specs { spcStartTime = 0.0,+ spcStopTime = 100000.0,+ spcDT = 1.0,+ spcMethod = RungeKutta4,+ spcGeneratorType = SimpleGenerator }+ +model :: Simulation IO (Results IO)+model =+ do man1 <- runEventInStartTime newFacility+ man2 <- runEventInStartTime newFacility+ match <- newMatchChain+ stats <- newRef emptySamplingStats++ let orderStream = randomUniformStream (300 - 200) (300 + 200)++ let orders = streamGeneratorBlock0 orderStream+ orderChain =+ Block (\a ->+ do t <- liftDynamics time+ return $ assignTransactValue a (const t)) >>>+ splitBlock [route2] >>>+ seizeBlock man1 >>>+ advanceBlock (randomUniformProcess_ (100 - 20) (100 + 20)) >>>+ matchBlock match >>>+ advanceBlock (randomUniformProcess_ (50 - 5) (50 + 5)) >>>+ merge+ merge =+ assembleBlock 2 >>>+ releaseBlock man1 >>>+ Block (\a ->+ do t <- liftDynamics time+ let t0 = transactValue a+ liftEvent $+ modifyRef stats $+ addSamplingStats (t - t0)+ return a) >>>+ terminateBlock++ route2 =+ seizeBlock man2 >>>+ advanceBlock (randomUniformProcess_ (110 - 25) (110 + 25)) >>>+ matchBlock match >>>+ advanceBlock (randomUniformProcess_ (70 - 10) (70 + 10)) >>>+ releaseBlock man2 >>>+ transferBlock merge+ + runProcessInStartTime $+ runGeneratorBlock orders orderChain + + return $+ results+ [resultSource "man1" "MAN1" man1,+ resultSource "man2" "MAN2" man2,+ resultSource "stats" "TATYM" stats]+ +main =+ printSimulationResultsInStopTime+ printResultSourceInEnglish+ model specs