Value Lanes
This page covers the specifics of Value Lanes and does not cover the more general aspects of Lanes. For more general information about lanes, see the Lane page.
Overview
A Value Lane stores a value that can be mutated and retreived. Each time the state is updated, the updated state is sent to all uplinks attached to it. A Value Lane meets the following requirements:
- The state of the lane can be updated by calling the
setfunction on the Handler Context’s. - Following a
setinvocation, the Value Lane’son_eventlifecycle event handler will be invoked with the updated state and theon_setlifecycle event handler will be invoked with the previous and current state of the lane. - The updated state will be sent to all attached uplinks as an event envelope.
Example: using a Value Lane to store an i32:
use swimos::agent::{
agent_lifecycle::utility::HandlerContext,
event_handler::{EventHandler, HandlerActionExt},
lanes::ValueLane,
lifecycle, projections, AgentLaneModel,
};
#[projections]
#[derive(AgentLaneModel)]
pub struct ExampleAgent {
lane: ValueLane<i32>,
}
#[derive(Clone)]
pub struct ExampleLifecycle;
#[lifecycle(ExampleAgent)]
impl ExampleLifecycle {
#[on_event(lane)]
pub fn on_event(
&self,
context: HandlerContext<ExampleAgent>,
value: &i32,
) -> impl EventHandler<ExampleAgent> {
let n = *value;
context.effect(move || {
println!("Setting value to: {}", n);
})
}
}
Use cases
Value Lanes are used for maintaining state and propagating updates to the state immediately. Common usecases are:
- Forming a digital representation of a physical object; such as the state of a vehicle.
- Persisting state in stream computing. If the SwimOS Server is configured to use a persistent store and the agent times out, the state of the Value Lane will be reloaded from the persistence engine. See persistence for more information.
- Propagating state in real time. When a downlink first links to a Value Lane, its current state is sent to the downlink. Value Lanes differ to Demand Lanes as they are backed by computation where Value lanes are backed by a value that is kept in memory.
Event handler
Value Lanes expose two event handlers. An on_set handler which is provided a reference to the updated state of the lane, and on_event which is provided a reference to the previous and the updated state of the lane.
On Event
The on_event event handler has the following signature for an i32 lane type:
#[on_event(lane_name)]
fn handler(&self, context: HandlerContext<ExampleAgent>, state: &i32) -> impl EventHandler<ExampleAgent> {
//...
}
Only one may be registered for the lane and it is invoked exactly once after the state of the lane has changed.
On Set
The on_set event handler has the following signature for an i32 lane type:
#[on_set(lane_name)]
fn handler(&self, context: HandlerContext<ExampleAgent>, new_value: &i32, previous_value: Option<i32>) -> impl EventHandler<ExampleAgent> {
//...
}
Only one may be registered for the lane and it is invoked exactly once when the state of the lane has changed and is invoked after any registered on_event handler has been invoked.
Handler Context Operations
The HandlerContext provided as an argument to lifecycle event handlers provide a set_value function for updating the state of a Value Lane and a get_value for retreiving the state of a Value Lane. The following example demonstrates retreiving and updating the state of a Value Lane:
use swimos::agent::{
agent_lifecycle::utility::HandlerContext,
event_handler::{EventHandler, HandlerActionExt},
lanes::ValueLane,
lifecycle, projections,
AgentLaneModel,
};
#[projections]
#[derive(AgentLaneModel)]
pub struct ExampleAgent {
state: ValueLane<i32>,
event_count: ValueLane<u32>,
}
#[derive(Clone)]
pub struct ExampleLifecycle;
#[lifecycle(ExampleAgent)]
impl ExampleLifecycle {
#[on_event(state)]
pub fn on_event(
&self,
context: HandlerContext<ExampleAgent>,
_state: &i32,
) -> impl EventHandler<ExampleAgent> {
context
.get_value(ExampleAgent::EVENT_COUNT)
.and_then(move |event_count: u32| {
context.set_value(ExampleAgent::EVENT_COUNT, event_count.wrapping_add(1))
})
}
}
Subscription
A subscription to a Value Lane can only be achieved via a Value Downlink. An example client for the previous agent example:
use std::time::Duration;
use swimos_client::{
BasicValueDownlinkLifecycle, DownlinkConfig, RemotePath,
SwimClientBuilder,
};
#[tokio::main]
async fn main() {
let (client, task) = SwimClientBuilder::default().build().await;
let _client_task = tokio::spawn(task);
let handle = client.handle();
let path = RemotePath::new("ws://0.0.0.0:65098", "/example/1", "lane");
let lifecycle = BasicValueDownlinkLifecycle::default()
.on_event_blocking(|command: &i32| println!("{command:?}"));
let downlink = handle
.value_downlink::<String>(path)
.lifecycle(lifecycle)
.downlink_config(DownlinkConfig::default())
.open()
.await
.expect("Failed to open downlink");
for i in 0..10 {
downlink.set(i).await.unwrap();
tokio::time::sleep(Duration::from_millis(100)).await;
}
tokio::signal::ctrl_c()
.await
.expect("Failed to listen for ctrl-c.");
}
Further reading: Downlinks
Try It Yourself
A standalone project that demonstrates Value Lanes is available here.