moffy-0.1.0.0: src/Control/Moffy/Handle/Time.hs
{-# LANGUAGE LambdaCase, TupleSections #-}
{-# LANGUAGE PatternSynonyms, ConstraintKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts, FlexibleInstances #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}
module Control.Moffy.Handle.Time (
-- * TimeState
TimeState(..), Mode(InitialMode),
-- * IO Mimicable
TaiTimeM(..), DelayM(..),
-- * Handle
Timable, handleTimeEvPlus ) where
import Control.Arrow (first, second, (>>>))
import Control.Moffy.Event.Time (
TimeEv, pattern OccDeltaTime, TryWait(..), pattern OccTryWait )
import Control.Moffy.Handle (
ExpandableHandle, MergeableOccurred, HandleIo', expandIo, mergeIo )
import Control.Concurrent (threadDelay)
import Data.Type.Set ((:+:))
import Data.OneOrMore (project)
import Data.Time (DiffTime)
import Data.Time.Clock.TAI (AbsoluteTime, diffAbsoluteTime, addAbsoluteTime)
import Data.Time.Clock.System (getSystemTime, systemToTAITime)
import Data.OneOrMoreApp (expand, pattern Singleton, (>-))
---------------------------------------------------------------------------
-- * TIME STATE
-- * IO MIMICABLE
-- * HANDLE
---------------------------------------------------------------------------
---------------------------------------------------------------------------
-- TIME STATE
---------------------------------------------------------------------------
class TimeState s where
getMode :: s -> Mode; putMode :: s -> Mode -> s
getLatestTime :: s -> AbsoluteTime
putLatestTime :: s -> AbsoluteTime -> s
instance TimeState (Mode, AbsoluteTime) where
getMode = fst; putMode s = flip first s . const
getLatestTime = snd; putLatestTime s = flip second s . const
updateTimeState :: TimeState s => s -> (Mode, AbsoluteTime) -> s
updateTimeState s (m, t) = s `putMode` m `putLatestTime` t
data Mode = InitialMode | FlushWaitMode AbsoluteTime deriving Show
mode :: a -> (AbsoluteTime -> a) -> Mode -> a
mode im fwm = \case InitialMode -> im; FlushWaitMode t -> fwm t
---------------------------------------------------------------------------
-- IO MIMICABLE
---------------------------------------------------------------------------
class TaiTimeM m where getCurrentTime :: m AbsoluteTime
instance TaiTimeM IO where getCurrentTime = systemToTAITime <$> getSystemTime
class DelayM m where delay :: DiffTime -> m ()
instance DelayM IO where delay = threadDelay . round . (* 10 ^ (6 :: Int))
---------------------------------------------------------------------------
-- HANDLE
---------------------------------------------------------------------------
type Timable es = (
ExpandableHandle es (es :+: TimeEv),
ExpandableHandle TimeEv (es :+: TimeEv),
MergeableOccurred es TimeEv (es :+: TimeEv) )
handleTimeEvPlus :: (TimeState s, Monad m, TaiTimeM m, DelayM m, Timable es) =>
HandleIo' ((DiffTime, a), s) s m es ->
HandleIo' ((DiffTime, a), s) s m (es :+: TimeEv)
handleTimeEvPlus hdl rqs i@(_, s) = ($ s) $ getMode
>>> mode (handleI hdl rqs i) (handleF rqs . (, s) . (, getLatestTime s))
handleI :: (TimeState s, Monad m, TaiTimeM m, DelayM m, Timable es) =>
HandleIo' ((DiffTime, a), s) s m es ->
HandleIo' ((DiffTime, a), s) s m (es :+: TimeEv)
handleI hdl = mergeIo
hdl (\((d, _), s) -> s <$ delay d)
(\rqs s -> (updateTimeState s `second`)
<$> (handleTime rqs . (, getLatestTime s) =<< getCurrentTime))
((<$> getCurrentTime) . putLatestTime)
handleF :: (TimeState s, Monad m, ExpandableHandle TimeEv es) =>
HandleIo' ((AbsoluteTime, AbsoluteTime), s) s m es
handleF rqs (nl, s) = (updateTimeState s `second`)
<$> expandIo handleTime (pure . (InitialMode ,) . fst) rqs nl
handleTime :: Monad m =>
HandleIo' (AbsoluteTime, AbsoluteTime) (Mode, AbsoluteTime) m TimeEv
handleTime rqs (now, lst) = case project rqs of
Just (TryWaitReq t)
| t < dt -> pure (
Just $ OccTryWait t >- Singleton (OccDeltaTime t),
(FlushWaitMode now, t `addAbsoluteTime` lst) )
| otherwise -> pure (
Just $ OccTryWait dt >- Singleton (OccDeltaTime dt),
(InitialMode, now) )
Nothing -> pure (
Just . expand . Singleton $ OccDeltaTime dt,
(InitialMode, now) )
where dt = now `diffAbsoluteTime` lst