module Mealstrom.FSMEngine(patchPhase1,patchPhase2) where
import Mealstrom.FSM
import Mealstrom.FSMTable
import Control.Monad (filterM, liftM)
import Data.List
import Data.Time.Clock
patchPhase1 :: (Eq s, Eq e) => FSMTable s e a -> [Msg e] -> Machine s e a -> IO (Machine s e a)
patchPhase1 tab es m = getCurrentTime >>= \ts -> eval tab ts (sendMultiple m es)
patchPhase2 :: (Eq a) => FSMTable s e a -> Machine s e a -> IO (Machine s e a)
patchPhase2 = apply
sendMultiple :: Machine s e a -> [Msg e] -> Machine s e a
sendMultiple = foldr (flip send)
send :: Machine s e a -> Msg e -> Machine s e a
send m e =
let
msgId (Msg (Just i) _) = i
ibox = inbox m
in
if elem (msgId e) $ map msgId ibox ++ committed m
then m
else m {inbox = ibox ++ [e]}
eval :: (Eq s, Eq e) => FSMTable s e a -> UTCTime -> Machine s e a -> IO (Machine s e a)
eval FSMTable{..} ts m =
let
ibox = inbox m
obox = outbox m
comm = committed m
(ids,events) = foldr (\(Msg (Just i) e) (is,es) -> (i:is,e:es)) ([],[]) ibox
(newm,as) = closure transitions ts m events
asmsgs = map mkMsg as
in do
s <- sequence asmsgs
return $ newm {inbox = [], outbox = obox ++ s, committed = comm ++ ids}
apply :: (Eq a) => FSMTable s e a -> Machine s e a -> IO (Machine s e a)
apply FSMTable{..} m = do
newas <- filterM (liftM not . effects) (outbox m)
return $ m {outbox = newas}
closure :: (Eq s, Eq e) => Transitions s e a -> UTCTime -> Machine s e a -> [e] -> (Machine s e a, [a])
closure trans ts m@Machine{..} =
foldl' (\(mm,oldas) e ->
let (newm, newas) = step trans ts mm e in
(newm, oldas ++ newas)
) (m,[])
step :: (Eq s, Eq e) => Transitions s e a -> UTCTime -> Machine s e a -> e -> (Machine s e a, [a])
step trans ts Machine{..} e =
let
(newState,as) = trans (currState,e)
newHist = histAppend (Step ts currState e newState as) hist
in
(Machine inbox outbox committed initState newState newHist, as)