Skip to main content

RuleEngine

Struct RuleEngine 

Source
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>

Source

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 by connect.
  • codec - the wire format the nodes publish their readings in.
§Returns

An engine with no actuators yet.

Source

pub fn with_actuator( self, name: impl Into<String>, actuator: impl Actuator<Command = bool> + Send + 'static, ) -> Self

Gives the engine an actuator a rule may drive by name.

§Arguments
  • name - the name a drive action uses.
  • actuator - the output, which the engine owns from here on.
§Returns

The engine, for chaining.

Source

pub fn rules(&self) -> impl Iterator<Item = &Rule>

Walks the rules in the order they are checked.

§Returns

Each rule.

Source

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

pub fn actuators(&self) -> Vec<&str>

Lists the actuators the rules drive, each of which the engine has to be given.

§Returns

Each actuator once, in name order.

Source

pub fn is_set(&self, rule: &str) -> Option<bool>

Whether a rule’s condition currently holds.

§Arguments
  • rule - the rule’s name.
§Returns

The condition’s state, or None for a name no rule has.

Source§

impl<L, C> RuleEngine<L, C>
where L: Transport + Receive, C: Codec<f32>,

Source

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.

Source

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.

Auto Trait Implementations§

§

impl<L, C> !RefUnwindSafe for RuleEngine<L, C>

§

impl<L, C> !Sync for RuleEngine<L, C>

§

impl<L, C> !UnwindSafe for RuleEngine<L, C>

§

impl<L, C> Freeze for RuleEngine<L, C>
where L: Freeze, C: Freeze,

§

impl<L, C> Send for RuleEngine<L, C>
where L: Send, C: Send,

§

impl<L, C> Unpin for RuleEngine<L, C>
where L: Unpin, C: Unpin,

§

impl<L, C> UnsafeUnpin for RuleEngine<L, C>
where L: UnsafeUnpin, C: UnsafeUnpin,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.