From 79a22e9bc2f58f08d6b51817e96cccb29228bc84 Mon Sep 17 00:00:00 2001 From: Alejandro Cabeza Romero Date: Fri, 20 Dec 2024 17:25:30 +0100 Subject: [PATCH] Implement ping pong example. --- Cargo.toml | 1 + examples/ping_pong/Cargo.toml | 10 ++++ examples/ping_pong/README.md | 17 +++++++ examples/ping_pong/src/main.rs | 26 ++++++++++ examples/ping_pong/src/messages.rs | 15 ++++++ examples/ping_pong/src/service_ping.rs | 68 ++++++++++++++++++++++++++ examples/ping_pong/src/service_pong.rs | 47 ++++++++++++++++++ 7 files changed, 184 insertions(+) create mode 100644 examples/ping_pong/Cargo.toml create mode 100644 examples/ping_pong/README.md create mode 100644 examples/ping_pong/src/main.rs create mode 100644 examples/ping_pong/src/messages.rs create mode 100644 examples/ping_pong/src/service_ping.rs create mode 100644 examples/ping_pong/src/service_pong.rs diff --git a/Cargo.toml b/Cargo.toml index 4b81df0..8174081 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "overwatch-rs", "overwatch-derive", + "examples/ping_pong", ] [profile.release-opt] diff --git a/examples/ping_pong/Cargo.toml b/examples/ping_pong/Cargo.toml new file mode 100644 index 0000000..4f458c1 --- /dev/null +++ b/examples/ping_pong/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ping_pong" +version = "0.1.0" +edition = "2021" + +[dependencies] +overwatch-rs = { path = "../../overwatch-rs" } +overwatch-derive = { path = "../../overwatch-derive" } +async-trait = "0.1.83" +tokio = { version = "1", features = ["macros"] } diff --git a/examples/ping_pong/README.md b/examples/ping_pong/README.md new file mode 100644 index 0000000..66c59ef --- /dev/null +++ b/examples/ping_pong/README.md @@ -0,0 +1,17 @@ +# PingPong +This example project demonstrates how to set up a basic Overwatch application in Rust. + +### Behaviour +This project demonstrates a simple communication pattern between two services. +1. Every second, the `Ping` service sends a message to the `Pong` service. +2. The `Pong` service receives the message and prints it to the console. Afterwards, it sends a message back to the `Ping` service. +3. The `Ping` service receives the message and prints it to the console. + +### Features +- **Services**: Shows how to define and register services within an Overwatch application. +- **Message Passing**: Demonstrates communication between services using the relay. + +### About +This project serves as a barebones template to get started with the Overwatch library. +It provides the foundational structure for creating more complex applications. + diff --git a/examples/ping_pong/src/main.rs b/examples/ping_pong/src/main.rs new file mode 100644 index 0000000..ad019a9 --- /dev/null +++ b/examples/ping_pong/src/main.rs @@ -0,0 +1,26 @@ +// Crate +use overwatch_derive::Services; +use overwatch_rs::overwatch::OverwatchRunner; +use overwatch_rs::services::handle::ServiceHandle; +// Internal +use crate::service_ping::PingService; +use crate::service_pong::PongService; + +mod service_ping; +mod service_pong; +mod messages; + +#[derive(Services)] +struct PingPong { + ping: ServiceHandle, + pong: ServiceHandle, +} + +fn main() { + let ping_pong_settings = PingPongServiceSettings { + ping: (), + pong: (), + }; + let ping_pong = OverwatchRunner::::run(ping_pong_settings, None).expect("OverwatchRunner failed"); + ping_pong.wait_finished(); +} diff --git a/examples/ping_pong/src/messages.rs b/examples/ping_pong/src/messages.rs new file mode 100644 index 0000000..3a09817 --- /dev/null +++ b/examples/ping_pong/src/messages.rs @@ -0,0 +1,15 @@ +use overwatch_rs::services::relay::RelayMessage; + +#[derive(Debug)] +pub enum PingMessage { + Pong +} + +impl RelayMessage for PingMessage {} + +#[derive(Debug)] +pub enum PongMessage { + Ping +} + +impl RelayMessage for PongMessage {} diff --git a/examples/ping_pong/src/service_ping.rs b/examples/ping_pong/src/service_ping.rs new file mode 100644 index 0000000..f1122ae --- /dev/null +++ b/examples/ping_pong/src/service_ping.rs @@ -0,0 +1,68 @@ +// Crates +use std::time::Duration; +use tokio::time::sleep; +use overwatch_rs::DynError; +use overwatch_rs::services::{ServiceCore, ServiceData, ServiceId}; +use overwatch_rs::services::handle::ServiceStateHandle; +use overwatch_rs::services::state::{NoOperator, NoState}; +// Internal +use crate::messages::{PingMessage, PongMessage}; +use crate::service_pong::PongService; + +pub struct PingService { + service_state_handle: ServiceStateHandle +} + +impl ServiceData for PingService { + const SERVICE_ID: ServiceId = "ping"; + type Settings = (); + type State = NoState; + type StateOperator = NoOperator; + type Message = PingMessage; +} + +#[async_trait::async_trait] +impl ServiceCore for PingService { + fn init(service_state_handle: ServiceStateHandle) -> Result { + Ok(Self { + service_state_handle + }) + } + + async fn run(self) -> Result<(), DynError> { + let Self { + service_state_handle + } = self; + + let mut inbound_relay = service_state_handle.inbound_relay; + let pong_outbound_relay = service_state_handle.overwatch_handle.relay::().connect().await?; + + let mut pong_count = 0; + + loop { + tokio::select! { + _ = sleep(Duration::from_secs(1)) => { + println!("Sending Ping"); + pong_outbound_relay.send(PongMessage::Ping).await.unwrap(); + } + Some(message) = inbound_relay.recv() => { + match message { + PingMessage::Pong => { + println!("Received Pong"); + pong_count += 1; + } + } + } + true = async { + pong_count >= 5 + } => { + println!("Received {} Pongs. Exiting...", pong_count); + break; + } + } + } + + service_state_handle.overwatch_handle.shutdown().await; + Ok(()) + } +} \ No newline at end of file diff --git a/examples/ping_pong/src/service_pong.rs b/examples/ping_pong/src/service_pong.rs new file mode 100644 index 0000000..30c1375 --- /dev/null +++ b/examples/ping_pong/src/service_pong.rs @@ -0,0 +1,47 @@ +// Crates +use overwatch_rs::DynError; +use overwatch_rs::services::{ServiceCore, ServiceData, ServiceId}; +use overwatch_rs::services::handle::ServiceStateHandle; +use overwatch_rs::services::state::{NoOperator, NoState}; +use crate::messages::{PingMessage, PongMessage}; +use crate::service_ping::PingService; + +pub struct PongService { + service_state_handle: ServiceStateHandle +} + +impl ServiceData for PongService { + const SERVICE_ID: ServiceId = ""; + type Settings = (); + type State = NoState; + type StateOperator = NoOperator; + type Message = PongMessage; +} + +#[async_trait::async_trait] +impl ServiceCore for PongService { + fn init(service_state_handle: ServiceStateHandle) -> Result { + Ok(Self { + service_state_handle + }) + } + + async fn run(self) -> Result<(), DynError> { + let Self { + service_state_handle + } = self; + + let mut inbound_relay = service_state_handle.inbound_relay; + let ping_outbound_relay = service_state_handle.overwatch_handle.relay::().connect().await?; + + while let Some(message) = inbound_relay.recv().await { + match message { + PongMessage::Ping => { + println!("Received Ping. Sending Pong."); + ping_outbound_relay.send(PingMessage::Pong).await.unwrap(); + } + } + } + Ok(()) + } +}