module Simulation.Aivika.IO.Event () where
import Control.Monad
import Control.Monad.Trans
import Data.IORef
import qualified Simulation.Aivika.PriorityQueue as PQ
import Simulation.Aivika.Trans.Ref.Base
import Simulation.Aivika.Trans.DES
import Simulation.Aivika.Trans.Comp
import Simulation.Aivika.Trans.Template
import Simulation.Aivika.Trans.Internal.Types
instance (Monad m, MonadIO m, MonadTemplate m) => EventQueueing m where
data EventQueue m =
EventQueue { queuePQ :: PQ.PriorityQueue (Point m -> m ()),
queueBusy :: IORef Bool,
queueTime :: IORef Double
}
newEventQueue specs =
liftIO $
do f <- newIORef False
t <- newIORef $ spcStartTime specs
pq <- PQ.newQueue
return EventQueue { queuePQ = pq,
queueBusy = f,
queueTime = t }
enqueueEvent t (Event m) =
Event $ \p ->
let pq = queuePQ $ runEventQueue $ pointRun p
in liftIO $ PQ.enqueue pq t m
runEventWith processing (Event e) =
Dynamics $ \p ->
do invokeDynamics p $ processEvents processing
e p
eventQueueCount =
Event $
liftIO . PQ.queueCount . queuePQ . runEventQueue . pointRun
processPendingEventsCore :: (MonadIO m, MonadTemplate m) => Bool -> Dynamics m ()
processPendingEventsCore includingCurrentEvents = Dynamics r where
r p =
do let q = runEventQueue $ pointRun p
f = queueBusy q
f' <- liftIO $ readIORef f
unless f' $
do liftIO $ writeIORef f True
call q p
liftIO $ writeIORef f False
call q p =
do let pq = queuePQ q
r = pointRun p
f <- liftIO $ PQ.queueNull pq
unless f $
do (t2, c2) <- liftIO $ PQ.queueFront pq
let t = queueTime q
t' <- liftIO $ readIORef t
when (t2 < t') $
error "The time value is too small: processPendingEventsCore"
when ((t2 < pointTime p) ||
(includingCurrentEvents && (t2 == pointTime p))) $
do liftIO $ writeIORef t t2
liftIO $ PQ.dequeue pq
let sc = pointSpecs p
t0 = spcStartTime sc
dt = spcDT sc
n2 = fromIntegral $ floor ((t2 t0) / dt)
c2 $ p { pointTime = t2,
pointIteration = n2,
pointPhase = 1 }
call q p
processPendingEvents :: (MonadIO m, MonadTemplate m) => Bool -> Dynamics m ()
processPendingEvents includingCurrentEvents = Dynamics r where
r p =
do let q = runEventQueue $ pointRun p
t = queueTime q
t' <- liftIO $ readIORef t
if pointTime p < t'
then error $
"The current time is less than " ++
"the time in the queue: processPendingEvents"
else invokeDynamics p m
m = processPendingEventsCore includingCurrentEvents
processEventsIncludingCurrent :: (MonadIO m, MonadTemplate m) => Dynamics m ()
processEventsIncludingCurrent = processPendingEvents True
processEventsIncludingEarlier :: (MonadIO m, MonadTemplate m) => Dynamics m ()
processEventsIncludingEarlier = processPendingEvents False
processEventsIncludingCurrentCore :: (MonadIO m, MonadTemplate m) => Dynamics m ()
processEventsIncludingCurrentCore = processPendingEventsCore True
processEventsIncludingEarlierCore :: (MonadIO m, MonadTemplate m) => Dynamics m ()
processEventsIncludingEarlierCore = processPendingEventsCore True
processEvents :: (MonadIO m, MonadTemplate m) => EventProcessing -> Dynamics m ()
processEvents CurrentEvents = processEventsIncludingCurrent
processEvents EarlierEvents = processEventsIncludingEarlier
processEvents CurrentEventsOrFromPast = processEventsIncludingCurrentCore
processEvents EarlierEventsOrFromPast = processEventsIncludingEarlierCore