module Simulation.Aivika.Agent
(Agent,
AgentState,
newAgent,
newState,
newSubstate,
selectedState,
selectedStateChanged,
selectedStateChanged_,
selectState,
stateAgent,
stateParent,
addTimeout,
addTimer,
setStateActivation,
setStateDeactivation,
setStateTransition) where
import Data.IORef
import Control.Monad
import Simulation.Aivika.Internal.Specs
import Simulation.Aivika.Internal.Simulation
import Simulation.Aivika.Internal.Event
import Simulation.Aivika.Signal
data Agent = Agent { agentModeRef :: IORef AgentMode,
agentStateRef :: IORef (Maybe AgentState),
agentStateChangedSource :: SignalSource (Maybe AgentState) }
data AgentState = AgentState { stateAgent :: Agent,
stateParent :: Maybe AgentState,
stateActivateRef :: IORef (Event ()),
stateDeactivateRef :: IORef (Event ()),
stateTransitRef :: IORef (Event (Maybe AgentState)),
stateVersionRef :: IORef Int }
data AgentMode = CreationMode
| TransientMode
| ProcessingMode
instance Eq Agent where
x == y = agentStateRef x == agentStateRef y
instance Eq AgentState where
x == y = stateVersionRef x == stateVersionRef y
fullPath :: AgentState -> [AgentState] -> [AgentState]
fullPath st acc =
case stateParent st of
Nothing -> st : acc
Just st' -> fullPath st' (st : acc)
partitionPath :: [AgentState] -> [AgentState] -> ([AgentState], [AgentState])
partitionPath path1 path2 =
case (path1, path2) of
(h1 : t1, [h2]) | h1 == h2 ->
(reverse path1, path2)
(h1 : t1, h2 : t2) | h1 == h2 ->
partitionPath t1 t2
_ ->
(reverse path1, path2)
findPath :: Maybe AgentState -> AgentState -> ([AgentState], [AgentState])
findPath Nothing target = ([], fullPath target [])
findPath (Just source) target
| stateAgent source /= stateAgent target =
error "Different agents: findPath."
| otherwise =
partitionPath path1 path2
where
path1 = fullPath source []
path2 = fullPath target []
traversePath :: Maybe AgentState -> AgentState -> Event ()
traversePath source target =
let (path1, path2) = findPath source target
agent = stateAgent target
activate st p = invokeEvent p =<< readIORef (stateActivateRef st)
deactivate st p = invokeEvent p =<< readIORef (stateDeactivateRef st)
transit st p = invokeEvent p =<< readIORef (stateTransitRef st)
continue st p = invokeEvent p $ traversePath (Just target) st
in Event $ \p ->
unless (null path1 && null path2) $
do writeIORef (agentModeRef agent) TransientMode
forM_ path1 $ \st ->
do writeIORef (agentStateRef agent) (Just st)
deactivate st p
modifyIORef (stateVersionRef st) (1 +)
forM_ path2 $ \st ->
do writeIORef (agentStateRef agent) (Just st)
activate st p
st' <- transit target p
case st' of
Nothing ->
do writeIORef (agentModeRef agent) ProcessingMode
triggerAgentStateChanged p agent
Just st' ->
continue st' p
addTimeout :: AgentState -> Double -> Event () -> Event ()
addTimeout st dt action =
Event $ \p ->
do v <- readIORef (stateVersionRef st)
let m1 = Event $ \p ->
do v' <- readIORef (stateVersionRef st)
when (v == v') $
invokeEvent p action
m2 = enqueueEvent (pointTime p + dt) m1
invokeEvent p m2
addTimer :: AgentState -> Event Double -> Event () -> Event ()
addTimer st dt action =
Event $ \p ->
do v <- readIORef (stateVersionRef st)
let m1 = Event $ \p ->
do v' <- readIORef (stateVersionRef st)
when (v == v') $
do invokeEvent p m2
invokeEvent p action
m2 = Event $ \p ->
do dt' <- invokeEvent p dt
invokeEvent p $ enqueueEvent (pointTime p + dt') m1
invokeEvent p m2
newState :: Agent -> Simulation AgentState
newState agent =
Simulation $ \r ->
do aref <- newIORef $ return ()
dref <- newIORef $ return ()
tref <- newIORef $ return Nothing
vref <- newIORef 0
return AgentState { stateAgent = agent,
stateParent = Nothing,
stateActivateRef = aref,
stateDeactivateRef = dref,
stateTransitRef = tref,
stateVersionRef = vref }
newSubstate :: AgentState -> Simulation AgentState
newSubstate parent =
Simulation $ \r ->
do let agent = stateAgent parent
aref <- newIORef $ return ()
dref <- newIORef $ return ()
tref <- newIORef $ return Nothing
vref <- newIORef 0
return AgentState { stateAgent = agent,
stateParent = Just parent,
stateActivateRef= aref,
stateDeactivateRef = dref,
stateTransitRef = tref,
stateVersionRef = vref }
newAgent :: Simulation Agent
newAgent =
Simulation $ \r ->
do modeRef <- newIORef CreationMode
stateRef <- newIORef Nothing
stateChangedSource <- invokeSimulation r newSignalSource
return Agent { agentModeRef = modeRef,
agentStateRef = stateRef,
agentStateChangedSource = stateChangedSource }
selectedState :: Agent -> Event (Maybe AgentState)
selectedState agent =
Event $ \p -> readIORef (agentStateRef agent)
selectState :: AgentState -> Event ()
selectState st =
Event $ \p ->
do let agent = stateAgent st
mode <- readIORef (agentModeRef agent)
case mode of
CreationMode ->
do x0 <- readIORef (agentStateRef agent)
invokeEvent p $ traversePath x0 st
TransientMode ->
error $
"Use the setStateTransition function to define " ++
"the transition state: activateState."
ProcessingMode ->
do x0 @ (Just st0) <- readIORef (agentStateRef agent)
invokeEvent p $ traversePath x0 st
setStateActivation :: AgentState -> Event () -> Event ()
setStateActivation st action =
Event $ \p ->
writeIORef (stateActivateRef st) action
setStateDeactivation :: AgentState -> Event () -> Event ()
setStateDeactivation st action =
Event $ \p ->
writeIORef (stateDeactivateRef st) action
setStateTransition :: AgentState -> Event (Maybe AgentState) -> Event ()
setStateTransition st action =
Event $ \p ->
writeIORef (stateTransitRef st) action
triggerAgentStateChanged :: Point -> Agent -> IO ()
triggerAgentStateChanged p agent =
do st <- readIORef (agentStateRef agent)
invokeEvent p $ triggerSignal (agentStateChangedSource agent) st
selectedStateChanged :: Agent -> Signal (Maybe AgentState)
selectedStateChanged agent =
publishSignal (agentStateChangedSource agent)
selectedStateChanged_ :: Agent -> Signal ()
selectedStateChanged_ agent =
mapSignal (const ()) $ selectedStateChanged agent