pub struct RuleEngine<L, C> { /* private fields */ }Expand description
Runs a set of rules off a link: every reading that arrives on a watched topic is judged, and the actions a set or cleared condition calls for are carried out.
The engine subscribes to each rule’s topic on connect and
then handles one message per step, decoding the reading with
the codec the nodes publish in, switching the actuators it was given by name, and
publishing over the same link. The node that publishes the reading and the node whose
actuator moves can be anywhere the link reaches; the engine is the third party
between them, and its rules are a file.
§Examples
use pamoja_codec::JsonCodec;
use pamoja_core::{Actuator, Result, Transport};
use pamoja_kit::Edge;
use pamoja_loopback::{LoopbackBroker, LoopbackTransport};
use pamoja_profile::{Action, Condition, Rule, RuleEngine, Rules};
struct Valve(bool);
impl Actuator for Valve {
type Command = bool;
async fn apply(&mut self, open: bool) -> Result<()> {
self.0 = open;
Ok(())
}
}
let broker = LoopbackBroker::new();
let mut probe = LoopbackTransport::new(broker.clone());
probe.connect().await?;
// Water the bed when it dries past 30, and stop once it is wetter than 35.
let rules = Rules::new().with(
Rule::new("water-when-dry", Condition::below("garden/bed-1/moisture", 30.0).with_hysteresis(5.0))
.then(Action::Drive { actuator: "bed-valve".into(), on: true })
.otherwise(Action::Drive { actuator: "bed-valve".into(), on: false }),
);
let mut engine = RuleEngine::new(rules, LoopbackTransport::new(broker), JsonCodec)
.with_actuator("bed-valve", Valve(false));
engine.connect().await?;
probe.send("garden/bed-1/moisture", b"28.0").await?;
let fired = engine.step().await?.expect("the link is up");
assert_eq!(fired[0].rule, "water-when-dry");
assert_eq!(fired[0].edge, Edge::Set);Implementations§
Source§impl<L, C> RuleEngine<L, C>
impl<L, C> RuleEngine<L, C>
Sourcepub fn new(rules: Rules, link: L, codec: C) -> Self
pub fn new(rules: Rules, link: L, codec: C) -> Self
Assembles an engine around a link and the codec the readings arrive in.
§Arguments
rules- the rules to run, checked when the engine connects.link- the link readings arrive on and actions publish over; connected byconnect.codec- the wire format the nodes publish their readings in.
§Returns
An engine with no actuators yet.
Sourcepub fn with_actuator(
self,
name: impl Into<String>,
actuator: impl Actuator<Command = bool> + Send + 'static,
) -> Self
pub fn with_actuator( self, name: impl Into<String>, actuator: impl Actuator<Command = bool> + Send + 'static, ) -> Self
Sourcepub fn topics(&self) -> Vec<&str>
pub fn topics(&self) -> Vec<&str>
Lists the topics the rules watch, which the engine subscribes to as it connects.
§Returns
Each topic once, in name order.
Source§impl<L, C> RuleEngine<L, C>where
L: Transport + Receive,
C: Codec<f32>,
impl<L, C> RuleEngine<L, C>where
L: Transport + Receive,
C: Codec<f32>,
Sourcepub async fn connect(&mut self) -> Result<()>
pub async fn connect(&mut self) -> Result<()>
Checks the rules, connects the link, and subscribes to every watched topic.
§Returns
Nothing once the engine is listening.
§Errors
Returns [Error::Codec] if a rule cannot be run (see Rules::check),
[Error::Unsupported] if a rule drives an actuator the engine was not given, and
the link’s own error if it cannot connect or subscribe.
Sourcepub async fn step(&mut self) -> Result<Option<Vec<Fired>>>
pub async fn step(&mut self) -> Result<Option<Vec<Fired>>>
Waits for one message and runs every rule that watches its topic.
§Returns
What fired, which is empty when the message set or cleared nothing, or None
once the link has ended.
§Errors
Returns the link’s error if receiving or publishing fails,
[Error::Codec] if a reading on a watched topic does not decode or is not a
finite number, and the actuator’s error if a drive fails.