pamoja_routing/lib.rs
1#![cfg_attr(not(test), no_std)]
2
3//! Cost-aware mesh routing for the pamoja SDK.
4//!
5//! Flooding gets a packet across a mesh by having every node rebroadcast it, which always
6//! works but is expensive: every node spends airtime and power on every packet. Once a
7//! mesh has settled, most traffic goes to a few known places, and a node that remembers
8//! the way can forward a packet to just the right neighbor instead of shouting it to the
9//! whole network. That is routing, and on the cheap radios this SDK targets the saving in
10//! airtime and battery is the difference between a network that lasts and one that does
11//! not.
12//!
13//! This crate is the decision layer for that, as pure logic with no radio and no
14//! allocation:
15//!
16//! - [`Router`] - a fixed-size table that learns the way to a node from the traffic it
17//! already hears: when a packet from a distant node arrives via a neighbor, that
18//! neighbor is the way back, at the cost the packet reports. The table keeps the
19//! cheapest way it knows to each destination and forgets the most expensive when it runs
20//! out of room.
21//! - [`Router::forward`] - the per-packet decision: deliver a packet that is for this
22//! node, [relay](Forward::Relay) one toward a known destination, or [flood](Forward::Flood)
23//! when there is no route yet. That last case is where this layer hands back to the
24//! flooding in `pamoja-mesh`, so routing is an optimization over flooding, never a
25//! single point of failure.
26//!
27//! Nodes are identified by the same address a [`pamoja-mesh`](https://docs.rs/pamoja-mesh)
28//! frame carries, so the two compose directly: learn from a received frame's source and
29//! the neighbor it came from, then ask [`forward`](Router::forward) where the next one
30//! should go.
31//!
32//! # Examples
33//!
34//! ```
35//! use pamoja_routing::{Forward, Router};
36//!
37//! // A gateway, node 1, on a farm mesh: the pump at node 9 is out of its range, and two
38//! // fence posts, nodes 5 and 7, relay for it.
39//! let (gateway, pump, east_post, west_post, weather_mast) = (1, 9, 5, 7, 32);
40//! let mut router: Router<16> = Router::new(gateway);
41//!
42//! // The pump's traffic arrives through the east post, two hops out.
43//! router.observe(pump, east_post, 2);
44//! assert_eq!(router.forward(pump), Forward::Relay(east_post));
45//!
46//! // A cheaper way to the pump turns up through the west post, and the router prefers it.
47//! router.observe(pump, west_post, 1);
48//! assert_eq!(router.forward(pump), Forward::Relay(west_post));
49//!
50//! // With no route to the weather mast yet, the router falls back to flooding.
51//! assert_eq!(router.forward(weather_mast), Forward::Flood);
52//! ```
53
54// `cfg(test)` already builds this crate against std, so the runtime-sized table compiles
55// for the test run whether or not the feature is on, and its tests always execute.
56#[cfg(any(feature = "alloc", test))]
57extern crate alloc;
58
59mod router;
60
61#[cfg(any(feature = "alloc", test))]
62pub use router::DynamicRouter;
63pub use router::{Forward, Route, Router};