Implement Commanding (#6)
Reviewed-on: #6 Co-authored-by: Sergey Savelyev <sergeysav.nn@gmail.com> Co-committed-by: Sergey Savelyev <sergeysav.nn@gmail.com>
This commit was merged in pull request #6.
This commit is contained in:
141
examples/simple_command/src/main.rs
Normal file
141
examples/simple_command/src/main.rs
Normal file
@@ -0,0 +1,141 @@
|
||||
use chrono::DateTime;
|
||||
use server::core::client_side_command::Inner;
|
||||
use server::core::command_service_client::CommandServiceClient;
|
||||
use server::core::telemetry_value::Value;
|
||||
use server::core::{
|
||||
ClientSideCommand, Command, CommandDefinitionRequest, CommandParameterDefinition,
|
||||
CommandResponse, TelemetryDataType,
|
||||
};
|
||||
use std::error::Error;
|
||||
use tokio::select;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tonic::codegen::tokio_stream::wrappers::ReceiverStream;
|
||||
use tonic::codegen::tokio_stream::StreamExt;
|
||||
use tonic::transport::Channel;
|
||||
|
||||
struct CommandHandler {
|
||||
handle: JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl CommandHandler {
|
||||
pub async fn new<F: Fn(Command) -> CommandResponse + Send + 'static>(
|
||||
cancellation_token: CancellationToken,
|
||||
client: &mut CommandServiceClient<Channel>,
|
||||
command_definition_request: CommandDefinitionRequest,
|
||||
handler: F,
|
||||
) -> anyhow::Result<Self> {
|
||||
let (tx, rx) = mpsc::channel(4);
|
||||
|
||||
// The buffer size of 4 means this is safe to send immediately
|
||||
tx.send(ClientSideCommand {
|
||||
inner: Some(Inner::Request(command_definition_request)),
|
||||
})
|
||||
.await?;
|
||||
let response = client.new_command(ReceiverStream::new(rx)).await?;
|
||||
let mut cmd_stream = response.into_inner();
|
||||
|
||||
let handle = tokio::spawn(async move {
|
||||
loop {
|
||||
select! {
|
||||
_ = cancellation_token.cancelled() => break,
|
||||
Some(msg) = cmd_stream.next() => {
|
||||
match msg {
|
||||
Ok(cmd) => {
|
||||
let uuid = cmd.uuid.clone();
|
||||
let mut response = handler(cmd);
|
||||
response.uuid = uuid;
|
||||
match tx.send(ClientSideCommand {
|
||||
inner: Some(Inner::Response(response))
|
||||
}).await {
|
||||
Ok(()) => {},
|
||||
Err(e) => {
|
||||
println!("SendError: {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("Error: {e}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
else => break,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Self { handle })
|
||||
}
|
||||
|
||||
pub async fn join(self) -> anyhow::Result<()> {
|
||||
Ok(self.handle.await?)
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let cancellation_token = CancellationToken::new();
|
||||
{
|
||||
let cancellation_token = cancellation_token.clone();
|
||||
tokio::spawn(async move {
|
||||
let _ = tokio::signal::ctrl_c().await;
|
||||
cancellation_token.cancel();
|
||||
});
|
||||
}
|
||||
|
||||
let mut client = CommandServiceClient::connect("http://[::1]:50051").await?;
|
||||
|
||||
let cmd_handler = CommandHandler::new(
|
||||
cancellation_token,
|
||||
&mut client,
|
||||
CommandDefinitionRequest {
|
||||
name: "simple_command/a".to_string(),
|
||||
parameters: vec![
|
||||
CommandParameterDefinition {
|
||||
name: "a".to_string(),
|
||||
data_type: TelemetryDataType::Float32.into(),
|
||||
},
|
||||
CommandParameterDefinition {
|
||||
name: "b".to_string(),
|
||||
data_type: TelemetryDataType::Float64.into(),
|
||||
},
|
||||
CommandParameterDefinition {
|
||||
name: "c".to_string(),
|
||||
data_type: TelemetryDataType::Boolean.into(),
|
||||
},
|
||||
],
|
||||
},
|
||||
|command| {
|
||||
let timestamp = command.timestamp.expect("Missing Timestamp");
|
||||
let timestamp = DateTime::from_timestamp(timestamp.secs, timestamp.nanos as u32)
|
||||
.expect("Could not construct date time");
|
||||
let Value::Float32(a) = command.parameters["a"].value.expect("Missing Value a") else {
|
||||
panic!("Wrong Type a");
|
||||
};
|
||||
let Value::Float64(b) = command.parameters["b"].value.expect("Missing Value b") else {
|
||||
panic!("Wrong Type b");
|
||||
};
|
||||
let Value::Boolean(c) = command.parameters["c"].value.expect("Missing Value c") else {
|
||||
panic!("Wrong Type c");
|
||||
};
|
||||
|
||||
println!("Command Received:\n timestamp: {timestamp}\n a: {a}\n b: {b}\n c: {c}");
|
||||
|
||||
CommandResponse {
|
||||
uuid: command.uuid.clone(),
|
||||
success: true,
|
||||
response: format!(
|
||||
"Successfully Received Command! timestamp: {timestamp} a: {a} b: {b} c: {c}"
|
||||
),
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
cmd_handler.join().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user