Skip to content

Commit

Permalink
Merge pull request #613 from swimos/docs_devguide_2.2
Browse files Browse the repository at this point in the history
Example code for dev guide 2.2
  • Loading branch information
SirCipher authored Jun 24, 2024
2 parents 0a4851e + 18ac469 commit 51b7909
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 1 deletion.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@
!/demos/**/Cargo.lock

**/.DS_Store
*.iml
*.iml

# Code coverage files
*.profraw
2 changes: 2 additions & 0 deletions .tarpaulin.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ exclude = [
"swimos_form_derive",
"swimos_agent_derive",
"macro_utilities",
"example_client_2_2",
"example_server_2_2"
]
workspace = true
avoid-cfg-tarpaulin = true
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ members = [
"example_apps/join_value",
"example_apps/aggregations",
"example_apps/time_series",
"example_apps/devguide/2_2/*",
]

exclude = [
Expand Down
9 changes: 9 additions & 0 deletions example_apps/devguide/2_2/example_client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "example_client_2_2"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { workspace = true, features = ["full"] }
swimos_client = { path = "../../../../client/swimos_client" }
swimos_form = { path = "../../../../api/swimos_form" }
54 changes: 54 additions & 0 deletions example_apps/devguide/2_2/example_client/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use swimos_client::{BasicValueDownlinkLifecycle, DownlinkConfig, RemotePath, SwimClientBuilder};

#[tokio::main]
async fn main() {
// Build a Swim Client using the default configuration.
// The `build` method returns a `SwimClient` instance and its internal
// runtime future that is spawned below.
let (client, task) = SwimClientBuilder::default().build().await;
let _client_task = tokio::spawn(task);
let handle = client.handle();

// Build a path the downlink.
let state_path = RemotePath::new(
// The host address
"ws://0.0.0.0:8080",
// You can provide any agent URI that matches the pattern
// "/example/:id"
"/example/1",
// This is the URI of the ValueLane<i32> in our ExampleAgent
"state",
);

let lifecycle = BasicValueDownlinkLifecycle::<usize>::default()
// Register an event handler that is invoked when the downlink connects to the agent.
.on_linked_blocking(|| println!("Downlink linked"))
// Register an event handler that is invoked when the downlink synchronises its state.
// with the agent.
.on_synced_blocking(|value| println!("Downlink synced with: {value:?}"))
// Register an event handler that is invoked when the downlink receives an event.
.on_event_blocking(|value| println!("Downlink event: {value:?}"));

// Build our downlink.
//
// This operation may fail if there is a connection issue.
let state_downlink = handle
.value_downlink::<i32>(state_path)
.lifecycle(lifecycle)
.downlink_config(DownlinkConfig::default())
.open()
.await
.expect("Failed to open downlink");

for i in 0..10 {
// Update the lane's state.
state_downlink
.set(i)
.await
.expect("Failed to set downlink state");
}

tokio::signal::ctrl_c()
.await
.expect("Failed to listen for ctrl-c.");
}
9 changes: 9 additions & 0 deletions example_apps/devguide/2_2/example_server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "example_server_2_2"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { workspace = true, features = ["full"] }
swimos = { path = "../../../../swimos", features = ["server"] }
swimos_form = { path = "../../../../api/swimos_form" }
104 changes: 104 additions & 0 deletions example_apps/devguide/2_2/example_server/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use swimos::{
agent::{
agent_lifecycle::HandlerContext, agent_model::AgentModel, event_handler::EventHandler,
lanes::ValueLane, lifecycle, projections, AgentLaneModel,
},
route::RoutePattern,
server::{Server, ServerBuilder, ServerHandle},
};

use std::{error::Error, time::Duration};

#[derive(AgentLaneModel)]
#[projections]
pub struct ExampleAgent {
state: ValueLane<i32>,
}

#[derive(Clone)]
pub struct ExampleLifecycle;

#[lifecycle(ExampleAgent)]
impl ExampleLifecycle {
// Handler invoked when the agent starts.
#[on_start]
pub fn on_start(
&self,
context: HandlerContext<ExampleAgent>,
) -> impl EventHandler<ExampleAgent> {
context.effect(|| println!("Starting agent."))
}

// Handler invoked when the agent is about to stop.
#[on_stop]
pub fn on_stop(
&self,
context: HandlerContext<ExampleAgent>,
) -> impl EventHandler<ExampleAgent> {
context.effect(|| println!("Stopping agent."))
}

// Handler invoked after the state of 'lane' has changed.
#[on_event(state)]
pub fn on_event(
&self,
context: HandlerContext<ExampleAgent>,
value: &i32,
) -> impl EventHandler<ExampleAgent> {
let n = *value;
// EventHandler::effect accepts a FnOnce()
// which runs a side effect.
context.effect(move || {
println!("Setting value to: {}", n);
})
}
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
// Create a dynamic route for our agents.
let route = RoutePattern::parse_str("/example/:id")?;
// Create an agent model which contains the factory for creating the agent as well
// as the lifecycle which will be run.
let agent = AgentModel::new(ExampleAgent::default, ExampleLifecycle.into_lifecycle());

// Create a server builder.
let server = ServerBuilder::with_plane_name("Plane")
// Bind to port 8080
.set_bind_addr("127.0.0.1:8080".parse().unwrap())
// For this guide, ensure agents timeout fairly quickly.
// An agent will timeout after they have received no new updates
// for this configured period of time.
.update_config(|config| {
config.agent_runtime.inactive_timeout = Duration::from_secs(20);
})
// Register the agent against the route.
.add_route(route, agent)
.build()
// Building the server may fail if many routes are registered and some
// are ambiguous.
.await?;

// Run the server. A tuple of the server's runtime
// future and a handle to the runtime is returned.
let (task, handle) = server.run();
// Watch for ctrl+c signals
let shutdown = manage_handle(handle);

// Join on the server and ctrl+c futures.
let (_, result) = tokio::join!(shutdown, task);

result?;
println!("Server stopped successfully.");
Ok(())
}

// Utility function for awaiting a stop signal in the terminal.
async fn manage_handle(mut handle: ServerHandle) {
tokio::signal::ctrl_c()
.await
.expect("Failed to register interrupt handler.");

println!("Stopping server.");
handle.stop();
}

0 comments on commit 51b7909

Please sign in to comment.