nettle-frp-0.1: src/Nettle/FRPControl/AFRPUtils.hs
{-# LANGUAGE
GADTs,
EmptyDataDecls,
TypeFamilies,
TypeOperators
#-}
-- | This module provides some utilities for writing drivers of
-- signal functions.
module Nettle.FRPControl.AFRPUtils
(
-- * Type-level lists
Nil,
(:::),
-- * Value-level representation of type-level lists
Rep (..),
-- * Event-source vector
SFInput(..),
-- * List of sensor channels
SensorChans,
-- * Output vector
SFOutput(..),
-- * List of actuator actions
Actuators,
-- * Driver
sfDriver
) where
import Control.Concurrent
import Data.Time.Clock (getCurrentTime, diffUTCTime)
import Control.Monad
import Nettle.FRPControl.AFRP
import Data.Monoid hiding (All)
-- Heterogeneous lists of types
-- | Empty list.
data Nil
-- | Cons list
data (:::) x xs = (:::) (x, xs)
-- | Infix operator; synonymous with TCons.
infixr 5 :::
-- | Value level representation of type level lists.
data Rep a where
RNil :: Rep Nil
RCons :: Rep b -> Rep (a ::: b)
-- |Input types for each heterogeneous type list.
type family SFInput t
type instance SFInput Nil = ()
type instance SFInput ((:::) x xs) = (Maybe x, SFInput xs)
-- | Injects the missing value into an SFInput type.
missing :: Rep a -> SFInput a
missing RNil = ()
missing (RCons rb) = (Nothing, missing rb)
-- | Vector of Sensor channels of the right types
type family SensorChans t
type instance SensorChans Nil = ()
type instance SensorChans (a ::: bs) = (Chan a, SensorChans bs)
-- | Make a sensor for SFInput s values, given a collection of SensorChans.
mkSensor :: Rep s -> SensorChans s -> IO (Chan (SFInput s))
mkSensor RNil () = newChan
mkSensor (RCons rb) (aCh, bChs) =
do bCh <- mkSensor rb bChs
multiplexChanPair rb aCh bCh
-- Helper function, used in mkSensor above.
multiplexChanPair :: Rep b ->
Chan a ->
Chan (SFInput b) ->
IO (Chan (SFInput (a ::: b)))
multiplexChanPair RNil aCh _ = do
abCh <- newChan
forkIO $ forever (readChan aCh >>= writeChan abCh . (\a -> (Just a, ())))
return abCh
multiplexChanPair rb aCh bCh = do
abCh <- newChan
forkIO $ readWriter aCh (\a -> (Just a, missing rb)) abCh
forkIO $ readWriter bCh (\b -> (Nothing, b)) abCh
return abCh
where readWriter srcCh f destCh =
forever (readChan srcCh >>= writeChan destCh . f)
-- |Output type for each heterogeneous list
type family SFOutput t
type instance SFOutput Nil = ()
type instance SFOutput (a ::: bs) = (a, SFOutput bs)
-- Vector of Actuator actions of the right types
type family Actuators t
type instance Actuators Nil = ()
type instance Actuators (a ::: bs) = (a -> IO (), Actuators bs)
-- Make an actuator given a representation of type s, and
-- an actuator list of shape s.
actuateVector :: Rep s -> Actuators s -> SFOutput s -> IO ()
actuateVector RNil () ()
= return ()
actuateVector (RCons rb) (actA, actsB) (a, b)
= actA a >> actuateVector rb actsB b
-- | Drives signal functions with the given collection of sensor channels
-- and actuator actions. It multiplexes the sensors into a single channel of events.
sfDriver :: Rep s -- ^ Representation of the shape of the @SensorChans@ argument
-> SensorChans s -- ^ Sensor channels
-> Rep t -- ^ Representation of the shape of the actuator argument
-> Actuators t -- ^ Actuators
-> SF (SFInput s) (SFOutput t) -- ^ Signal function to drive
-> IO ()
sfDriver repS chS repT chT sf =
do
sensorCh <- mkSensor repS chS
let actuator = actuateVector repT chT
let sense = readChan sensorCh
let a0 = missing repS
let aMissing = Just (missing repS)
sfDriverAux a0 sense aMissing actuator sf
------------- INTERNAL ----------------
sfDriverAux :: a -> IO a -> Maybe a -> (b -> IO ()) -> SF a b -> IO ()
sfDriverAux a0 sensor clockInput actuator sf = do
lastSenseTimeVar <- newEmptyMVar
inCh <- newChan :: IO (Chan (Maybe a))
let initiator = initReact lastSenseTimeVar
let sensor'' = sensor' inCh lastSenseTimeVar
forkIO $ genClockSignal inCh
forkIO $ genNormalSignal inCh
reactimate initiator sensor'' actuator' sf
where
initReact lastSenseTimeVar =
do
t <- getCurrentTime
putMVar lastSenseTimeVar t
return a0
sensor' ch lastSenseTimeVar _ =
do
ma <- readChan ch
t' <- getCurrentTime
t <- swapMVar lastSenseTimeVar t'
let delta = fromRational (toRational (diffUTCTime t' t))
return (delta, ma)
actuator' changed b = actuator b >> return False
genClockSignal ch =
forever (threadDelay clockCycle >> writeChan ch clockInput)
where clockCycle = 1000
genNormalSignal ch = forever (sensor >>= writeChan ch . Just)