Skip to content

Commit eb49190

Browse files
committed
Adds example code for dev guide 2.2
1 parent a91317f commit eb49190

File tree

6 files changed

+185
-8
lines changed

6 files changed

+185
-8
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ members = [
3737
"example_apps/tutorial_app",
3838
"example_apps/tutorial_app/model",
3939
"example_apps/tutorial_app/generator",
40+
"example_apps/devguide/2_2/*",
4041
]
4142

4243
exclude = [

client/swimos_client/src/lib.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ use swimos_remote::ws::RatchetClient;
2121
use futures_util::future::BoxFuture;
2222
#[cfg(feature = "deflate")]
2323
use ratchet::deflate::{DeflateConfig, DeflateExtProvider};
24+
pub use runtime::RemotePath;
2425
use runtime::{
25-
start_runtime, ClientConfig, DownlinkRuntimeError, RawHandle, RemotePath, Transport,
26-
WebSocketConfig,
26+
start_runtime, ClientConfig, DownlinkRuntimeError, RawHandle, Transport, WebSocketConfig,
2727
};
2828
pub use runtime::{CommandError, Commander};
2929
use std::sync::Arc;
30-
use swimos_api::downlink::DownlinkConfig;
31-
use swimos_downlink::lifecycle::{
30+
pub use swimos_api::downlink::DownlinkConfig;
31+
pub use swimos_downlink::lifecycle::{
3232
BasicMapDownlinkLifecycle, BasicValueDownlinkLifecycle, MapDownlinkLifecycle,
3333
ValueDownlinkLifecycle,
3434
};
@@ -222,7 +222,7 @@ impl ClientHandle {
222222
///
223223
/// # Arguments
224224
/// * `path` - The path of the downlink top open.
225-
pub fn value_downlink<L, T>(
225+
pub fn value_downlink<T>(
226226
&self,
227227
path: RemotePath,
228228
) -> ValueDownlinkBuilder<'_, BasicValueDownlinkLifecycle<T>> {
@@ -287,19 +287,19 @@ impl<'h, L> ValueDownlinkBuilder<'h, L> {
287287
}
288288

289289
/// Sets link options for the downlink.
290-
pub fn options(&mut self, options: DownlinkOptions) -> &mut Self {
290+
pub fn options(mut self, options: DownlinkOptions) -> Self {
291291
self.options = options;
292292
self
293293
}
294294

295295
/// Sets a new downlink runtime configuration.
296-
pub fn runtime_config(&mut self, config: DownlinkRuntimeConfig) -> &mut Self {
296+
pub fn runtime_config(mut self, config: DownlinkRuntimeConfig) -> Self {
297297
self.runtime_config = config;
298298
self
299299
}
300300

301301
/// Sets a new downlink configuration.
302-
pub fn downlink_config(&mut self, config: DownlinkConfig) -> &mut Self {
302+
pub fn downlink_config(mut self, config: DownlinkConfig) -> Self {
303303
self.downlink_config = config;
304304
self
305305
}
@@ -333,6 +333,7 @@ impl<'h, L> ValueDownlinkBuilder<'h, L> {
333333
}
334334
}
335335

336+
#[derive(Debug)]
336337
pub enum ValueDownlinkOperationError {
337338
NotYetSynced,
338339
DownlinkStopped,
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "example_client"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
tokio = { workspace = true, features = ["full"] }
8+
swimos_client = { path = "../../../../client/swimos_client" }
9+
swimos_form = { path = "../../../../api/swimos_form" }
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use swimos_client::{BasicValueDownlinkLifecycle, DownlinkConfig, RemotePath, SwimClientBuilder};
2+
3+
#[tokio::main]
4+
async fn main() {
5+
// Build a Swim Client using the default configuration.
6+
// The `build` method returns a `SwimClient` instance and its internal
7+
// runtime future that is spawned below.
8+
let (client, task) = SwimClientBuilder::default().build().await;
9+
let _client_task = tokio::spawn(task);
10+
let handle = client.handle();
11+
12+
// Build a path the downlink.
13+
let state_path = RemotePath::new(
14+
// The host address
15+
"ws://0.0.0.0:8080",
16+
// You can provide any agent URI that matches the pattern
17+
// "/example/:id"
18+
"/example/1",
19+
// This is the URI of the ValueLane<i32> in our ExampleAgent
20+
"state",
21+
);
22+
23+
let lifecycle = BasicValueDownlinkLifecycle::<usize>::default()
24+
// Register an event handler that is invoked when the downlink connects to the agent.
25+
.on_linked_blocking(|| println!("Downlink linked"))
26+
// Register an event handler that is invoked when the downlink synchronises its state.
27+
// with the agent.
28+
.on_synced_blocking(|value| println!("Downlink synced with: {value:?}"))
29+
// Register an event handler that is invoked when the downlink receives an event.
30+
.on_event_blocking(|value| println!("Downlink event: {value:?}"));
31+
32+
// Build our downlink.
33+
//
34+
// This operation may fail if there is a connection issue.
35+
let state_downlink = handle
36+
.value_downlink::<i32>(state_path)
37+
.lifecycle(lifecycle)
38+
.downlink_config(DownlinkConfig::default())
39+
.open()
40+
.await
41+
.expect("Failed to open downlink");
42+
43+
for i in 0..10 {
44+
// Update the lane's state.
45+
state_downlink
46+
.set(i)
47+
.await
48+
.expect("Failed to set downlink state");
49+
}
50+
51+
tokio::signal::ctrl_c()
52+
.await
53+
.expect("Failed to listen for ctrl-c.");
54+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "example_server"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
tokio = { workspace = true, features = ["full"] }
8+
swimos = { path = "../../../../swimos", features = ["server"] }
9+
swimos_form = { path = "../../../../api/swimos_form" }
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use swimos::{
2+
agent::{
3+
agent_lifecycle::utility::HandlerContext, agent_model::AgentModel,
4+
event_handler::EventHandler, lanes::ValueLane, lifecycle, projections, AgentLaneModel,
5+
},
6+
route::RoutePattern,
7+
server::{Server, ServerBuilder, ServerHandle},
8+
};
9+
10+
use std::{error::Error, time::Duration};
11+
#[derive(AgentLaneModel)]
12+
#[projections]
13+
pub struct ExampleAgent {
14+
state: ValueLane<i32>,
15+
}
16+
17+
#[derive(Clone)]
18+
pub struct ExampleLifecycle;
19+
20+
#[lifecycle(ExampleAgent)]
21+
impl ExampleLifecycle {
22+
// Handler invoked when the agent starts.
23+
#[on_start]
24+
pub fn on_start(
25+
&self,
26+
context: HandlerContext<ExampleAgent>,
27+
) -> impl EventHandler<ExampleAgent> {
28+
context.effect(|| println!("Starting agent."))
29+
}
30+
31+
// Handler invoked when the agent is about to stop.
32+
#[on_stop]
33+
pub fn on_stop(
34+
&self,
35+
context: HandlerContext<ExampleAgent>,
36+
) -> impl EventHandler<ExampleAgent> {
37+
context.effect(|| println!("Stopping agent."))
38+
}
39+
40+
// Handler invoked after the state of 'lane' has changed.
41+
#[on_event(state)]
42+
pub fn on_event(
43+
&self,
44+
context: HandlerContext<ExampleAgent>,
45+
value: &i32,
46+
) -> impl EventHandler<ExampleAgent> {
47+
let n = *value;
48+
// EventHandler::effect accepts a FnOnce()
49+
// which runs a side effect.
50+
context.effect(move || {
51+
println!("Setting value to: {}", n);
52+
})
53+
}
54+
}
55+
56+
#[tokio::main]
57+
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
58+
// Create a dynamic route for our agents.
59+
let route = RoutePattern::parse_str("/example/:id")?;
60+
// Create an agent model which contains the factory for creating the agent as well
61+
// as the lifecycle which will be run.
62+
let agent = AgentModel::new(ExampleAgent::default, ExampleLifecycle.into_lifecycle());
63+
64+
// Create a server builder.
65+
let server = ServerBuilder::with_plane_name("Plane")
66+
// Bind to port 8080
67+
.set_bind_addr("127.0.0.1:8080".parse().unwrap())
68+
// For this guide, ensure agents timeout fairly quickly.
69+
// An agent will timeout after they have received no new updates
70+
// for this configured period of time.
71+
.update_config(|config| {
72+
config.agent_runtime.inactive_timeout = Duration::from_secs(20);
73+
})
74+
// Register the agent against the route.
75+
.add_route(route, agent)
76+
.build()
77+
// Building the server may fail if many routes are registered and some
78+
// are ambiguous.
79+
.await?;
80+
81+
// Run the server. A tuple of the server's runtime
82+
// future and a handle to the runtime is returned.
83+
let (task, handle) = server.run();
84+
// Watch for ctrl+c signals
85+
let shutdown = manage_handle(handle);
86+
87+
// Join on the server and ctrl+c futures.
88+
let (_, result) = tokio::join!(shutdown, task);
89+
90+
result?;
91+
println!("Server stopped successfully.");
92+
Ok(())
93+
}
94+
95+
// Utility function for awaiting a stop signal in the terminal.
96+
async fn manage_handle(mut handle: ServerHandle) {
97+
tokio::signal::ctrl_c()
98+
.await
99+
.expect("Failed to register interrupt handler.");
100+
101+
println!("Stopping server.");
102+
handle.stop();
103+
}

0 commit comments

Comments
 (0)