summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoeyHess <>2018-05-05 23:28:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2018-05-05 23:28:00 (GMT)
commitae8d203f6b587b93aa1c6c4857382d0f7aa41a3f (patch)
tree1d02310744cc2707618a5b298f68e921c6a8311a
parent6a7f7cb1e73894b4f31b542a9c6e2c262f235616 (diff)
version 0.3.00.3.0
-rw-r--r--CHANGELOG9
-rw-r--r--Reactive/Banana/Automation.hs65
-rw-r--r--Reactive/Banana/Automation/Examples.hs17
-rw-r--r--reactive-banana-automation.cabal2
4 files changed, 64 insertions, 29 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 0784288..0b8d026 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,12 @@
+reactive-banana-automation (0.3.0) upstream; urgency=medium
+
+ * EventSource is now a data type with an extra value for expansion.
+ (API change)
+ * Added newEventSource, sensorUnavailable, sensedEventBehavior,
+ and automationStepper.
+
+ -- Joey Hess <id@joeyh.name> Sat, 05 May 2018 19:27:56 -0400
+
reactive-banana-automation (0.2.0) upstream; urgency=medium
* Automation is now a newtype. (API change)
diff --git a/Reactive/Banana/Automation.hs b/Reactive/Banana/Automation.hs
index e90fd26..450b529 100644
--- a/Reactive/Banana/Automation.hs
+++ b/Reactive/Banana/Automation.hs
@@ -23,6 +23,8 @@ module Reactive.Banana.Automation (
observeAutomation,
-- * Events
EventSource,
+ newEventSource,
+ fromEventSource,
gotEvent,
getEventFrom,
onEvent,
@@ -32,6 +34,9 @@ module Reactive.Banana.Automation (
sensedBehavior,
sensed,
(=:),
+ sensorUnavailable,
+ sensedEventBehavior,
+ automationStepper,
-- * Time
Timestamped(..),
Timestamp(..),
@@ -72,7 +77,7 @@ import Data.Time.LocalTime
-- run as needed to keep the temperature in a safe range, while
-- minimizing compressor starts.
--
--- > data Sensors = Sensors { fridgeTemperature :: EventSource (Sensed Double) }
+-- > data Sensors = Sensors { fridgeTemperature :: EventSource (Sensed Double) () }
-- > data Actuators = FridgePower PowerChange deriving (Show)
-- >
-- > fridge :: Automation Sensors Actuators
@@ -138,7 +143,7 @@ setupAutomation (Automation automation) mksensors actutators = do
-- Continuing the above example of a fridge, here's how to run it:
--
-- > mkSensors :: IO Sensors
--- > mkSensors = Sensors <$> newAddHandler
+-- > mkSensors = Sensors <$> newEventSource ()
-- >
-- > driveActuators :: Actuators -> IO ()
-- > driveActuators = print
@@ -196,17 +201,29 @@ observeAutomation automation mksensors = do
return runner
-- | A source of events.
-type EventSource a = (AddHandler a, a -> IO ())
+--
+-- `v` is unused by this library, but is provided in case you
+-- need a way to track some extra data about an EventSource such as, for
+-- example, the timestamp of the most recent event.
+data EventSource a v = EventSource
+ { getEventSource :: (AddHandler a, a -> IO ())
+ , fromEventSource :: v
+ -- ^ Get extra data from an EventSource.
+ }
+
+-- | Construct a new EventSource.
+newEventSource :: v -> IO (EventSource a v)
+newEventSource v = EventSource <$> newAddHandler <*> pure v
-addHandler :: EventSource a -> AddHandler a
-addHandler = fst
+addHandler :: EventSource a v -> AddHandler a
+addHandler = fst . getEventSource
-- | Call this to trigger an event.
-gotEvent :: EventSource a -> a -> IO ()
-gotEvent = snd
+gotEvent :: EventSource a v -> a -> IO ()
+gotEvent = snd . getEventSource
-- | Get an Event from an EventSource.
-getEventFrom :: EventSource a -> MomentAutomation (Event a)
+getEventFrom :: EventSource a v -> MomentAutomation (Event a)
getEventFrom = MomentAutomation . fromAddHandler . addHandler
-- | Runs an action when an event occurs.
@@ -224,7 +241,7 @@ data Sensed a = SensorUnavailable | Sensed a
--
-- The Event only contains values when the sensor provided a reading,
-- not times when it was unavailable.
-sensedEvent :: EventSource (Sensed a) -> MomentAutomation (Event a)
+sensedEvent :: EventSource (Sensed a) v -> MomentAutomation (Event a)
sensedEvent s = do
e <- getEventFrom s
return $ filterJust $ flip fmap e $ \case
@@ -232,22 +249,32 @@ sensedEvent s = do
Sensed a -> Just a
-- | Create a Behavior from sensed values.
-sensedBehavior :: EventSource (Sensed a) -> MomentAutomation (Behavior (Sensed a))
-sensedBehavior s =
- MomentAutomation . stepper SensorUnavailable =<< getEventFrom s
+sensedBehavior :: EventSource (Sensed a) v -> MomentAutomation (Behavior (Sensed a))
+sensedBehavior s = sensedEventBehavior =<< getEventFrom s
+
+sensedEventBehavior :: Event (Sensed a) -> MomentAutomation (Behavior (Sensed a))
+sensedEventBehavior = automationStepper SensorUnavailable
+
+-- | `stepper` lifted into `MomentAutomation`
+automationStepper :: a -> Event a -> MomentAutomation (Behavior a)
+automationStepper a e = MomentAutomation $ stepper a e
-- | Call when a sensor has sensed a value.
--
-- > getFridgeTemperature >>= sensed (fridgeTemperature sensors)
-sensed :: EventSource (Sensed a) -> a -> IO ()
+sensed :: EventSource (Sensed a) v -> a -> IO ()
sensed s = gotEvent s . Sensed
-- | Same as `sensed`
--
-- > fridgeTemperature sensors =: 0
-(=:) :: EventSource (Sensed a) -> a -> IO ()
+(=:) :: EventSource (Sensed a) v -> a -> IO ()
(=:) = sensed
+-- | Call when a sensor is unavailable.
+sensorUnavailable :: EventSource (Sensed a) v -> IO ()
+sensorUnavailable s = gotEvent s SensorUnavailable
+
-- | A timestamped value.
--
-- In reactive-banana, an `Event` is tagged with its time of occurrence,
@@ -289,13 +316,13 @@ instance Timestamp TimeOfDay where
-- | Call when a sensor has sensed a value, which will be `Timestamped` with
-- the current time.
-sensedNow :: Timestamp t => EventSource (Sensed (Timestamped t a)) -> a -> IO ()
+sensedNow :: Timestamp t => EventSource (Sensed (Timestamped t a)) v -> a -> IO ()
sensedNow es a = do
now <- getCurrentTimestamp
gotEvent es (Sensed (Timestamped now a))
-- | Call when a sensor sensed a value with a particular timestamp.
-sensedAt :: Timestamp t => t -> EventSource (Sensed (Timestamped t a)) -> a -> IO ()
+sensedAt :: Timestamp t => t -> EventSource (Sensed (Timestamped t a)) v -> a -> IO ()
sensedAt ts es a = gotEvent es (Sensed (Timestamped ts a))
-- | Given a `Timestamped` `Event` and a function, produces an `Event`
@@ -332,16 +359,16 @@ data ClockSignal a = ClockSignal a
-- | Call repeatedly to feed a clock signal to an `Automation`
-- that needs to know what time it is.
-clockSignal :: Timestamp t => EventSource (ClockSignal t) -> IO ()
+clockSignal :: Timestamp t => EventSource (ClockSignal t) v -> IO ()
clockSignal es = gotEvent es . ClockSignal =<< getCurrentTimestamp
-- | Call to feed a particular time to an `Automation`.
-clockSignalAt :: Timestamp t => t -> EventSource (ClockSignal t) -> IO ()
+clockSignalAt :: Timestamp t => t -> EventSource (ClockSignal t) v -> IO ()
clockSignalAt t es = gotEvent es (ClockSignal t)
-- | Create a Behavior from a ClockSignal. It will initially be Nothing,
-- and then updates with each incoming clock signal.
-clockSignalBehavior :: Timestamp t => EventSource (ClockSignal t) -> MomentAutomation (Behavior (Maybe (ClockSignal t)))
+clockSignalBehavior :: Timestamp t => EventSource (ClockSignal t) v -> MomentAutomation (Behavior (Maybe (ClockSignal t)))
clockSignalBehavior s = MomentAutomation . stepper Nothing
=<< fmap Just <$> getEventFrom s
diff --git a/Reactive/Banana/Automation/Examples.hs b/Reactive/Banana/Automation/Examples.hs
index 8e8d2ea..99e1c4c 100644
--- a/Reactive/Banana/Automation/Examples.hs
+++ b/Reactive/Banana/Automation/Examples.hs
@@ -6,7 +6,6 @@
module Reactive.Banana.Automation.Examples where
import Reactive.Banana
-import Reactive.Banana.Frameworks
import Reactive.Banana.Automation
import Data.Time.Clock.POSIX
import Data.Time.LocalTime
@@ -15,10 +14,10 @@ import Data.Time.Calendar
-- | We'll use a single Sensors type containing all the sensors
-- used by the examples below.
data Sensors = Sensors
- { fridgeTemperature :: EventSource (Sensed Double)
- , motionSensor :: EventSource (Sensed (Timestamped POSIXTime Bool))
- , clock :: EventSource (ClockSignal LocalTime)
- , rainGaugeTipSensor :: EventSource (Sensed ())
+ { fridgeTemperature :: EventSource (Sensed Double) ()
+ , motionSensor :: EventSource (Sensed (Timestamped POSIXTime Bool)) ()
+ , clock :: EventSource (ClockSignal LocalTime) ()
+ , rainGaugeTipSensor :: EventSource (Sensed ()) ()
}
-- | And a single Actuators type containing all the actuators used by the
@@ -33,10 +32,10 @@ data Actuators
-- | For running the examples, you'll need this, to construct a `Sensors`
mkSensors :: IO Sensors
mkSensors = Sensors
- <$> newAddHandler
- <*> newAddHandler
- <*> newAddHandler
- <*> newAddHandler
+ <$> newEventSource ()
+ <*> newEventSource ()
+ <*> newEventSource ()
+ <*> newEventSource ()
-- | A fridge, containing the `fridgeTemperature` sensor and with
-- its power controlled by the `FridgePower` actuator.
diff --git a/reactive-banana-automation.cabal b/reactive-banana-automation.cabal
index c7ced86..c599f7f 100644
--- a/reactive-banana-automation.cabal
+++ b/reactive-banana-automation.cabal
@@ -1,5 +1,5 @@
Name: reactive-banana-automation
-Version: 0.2.0
+Version: 0.3.0
Cabal-Version: >= 1.8
License: AGPL-3
Maintainer: Joey Hess <id@joeyh.name>