Yampa-0.15: examples/Elevator/Elevator.hs
{-# LANGUAGE Arrows #-}
-- |
-- Module : Elevator
-- Description : Elevator simulation based on the Fran version by Thompson.
-- Copyright : (c) Ivan Perez, 2014-2022
-- (c) George Giorgidze, 2007-2012
-- (c) Henrik Nilsson, The University of Nottingham, 2004-2006
-- License : BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer : ivan.perez@keera.co.uk
-- Stability : provisional
-- Portability : non-portable (GHC extensions)
--
-- Elevator simulation based on the Fran version from Simon Thompson's paper "A
-- functional reactive animation of a lift using Fran".
module Elevator where
import FRP.Yampa
-- * Auxiliary definitions
type Position = Double -- [m]
type Distance = Double -- [m]
type Velocity = Double -- [m/s]
-- * Elevator simulator
lower, upper :: Position
lower = 0
upper = 5
upRate, downRate :: Velocity
upRate = 1
downRate = 1.1
elevator :: SF (Event (), Event ()) Position
elevator = proc (lbp,rbp) -> do
rec
-- This delayed hold can be thought of as modelling acceleration.
-- It is not "physical" to expect a desire to travel at a certain
-- velocity to be immediately reflected in the actual velocity.
-- (The reason we get into trouble here is that the stop/go events
-- depends instantaneously on "stopped" which in turn depends
-- instantaneously on "v".)
v <- dHold 0 -< stop `tag` 0
`lMerge` goUp `tag` upRate
`lMerge` goDown `tag` (-downRate)
y <- (lower +) ^<< integral -< v
let atBottom = y <= lower
atTop = y >= upper
stopped = v == 0 -- Somewhat dubious ...
waitingBottom = atBottom && stopped
waitingTop = atTop && stopped
arriveBottom <- edge -< atBottom
arriveTop <- edge -< atTop
let setUp = lbp `tag` True
setDown = rbp `tag` True
-- This does not work. The reset events would be generated as soon
-- as the corresponding go event was generated, but the latter
-- depend instantaneusly on the reset signals.
-- resetUp = goUp `tag` False
-- resetDown = goDown `tag` False
-- One approach would be to wait for "physical confiramtion"
-- that the elevator actually is moving in the desired direction:
-- resetUp <- (`tag` True) ^<< edge -< v > 0
-- resetDown <- (`tag` False) ^<< edge -< v < 0
-- Another approach is to simply delay the reset events to avoid
-- suppressing the very event that generates the reset event.
resetUp <- iPre noEvent -< goUp `tag` False
resetDown <- iPre noEvent -< goDown `tag` False
-- Of course, a third approach would be to just use dHold below.
-- But that does not seem to be the right solution to me.
upPending <- hold False -< setUp `lMerge` resetUp
downPending <- hold False -< setDown `lMerge` resetDown
let pending = upPending || downPending
eitherButton = lbp `lMerge` rbp
goDown = arriveTop `gate` pending
`lMerge` eitherButton `gate` waitingTop
goUp = arriveBottom `gate` pending
`lMerge` eitherButton `gate` waitingBottom
stop = (arriveTop `lMerge` arriveBottom) `gate` not pending
returnA -< y