implement command responses
This commit is contained in:
@@ -11,3 +11,4 @@ chrono = "0.4.39"
|
||||
tokio-util = "0.7.13"
|
||||
num-traits = "0.2.19"
|
||||
log = "0.4.29"
|
||||
anyhow = "1.0.100"
|
||||
|
||||
@@ -1,11 +1,79 @@
|
||||
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::{CommandDefinitionRequest, CommandParameterDefinition, TelemetryDataType};
|
||||
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>> {
|
||||
@@ -20,8 +88,10 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
let mut client = CommandServiceClient::connect("http://[::1]:50051").await?;
|
||||
|
||||
let command_response = client
|
||||
.new_command(CommandDefinitionRequest {
|
||||
let cmd_handler = CommandHandler::new(
|
||||
cancellation_token,
|
||||
&mut client,
|
||||
CommandDefinitionRequest {
|
||||
name: "simple_command/a".to_string(),
|
||||
parameters: vec![
|
||||
CommandParameterDefinition {
|
||||
@@ -37,28 +107,35 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
data_type: TelemetryDataType::Boolean.into(),
|
||||
},
|
||||
],
|
||||
})
|
||||
.await?;
|
||||
let mut command_stream = command_response.into_inner();
|
||||
},
|
||||
|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");
|
||||
};
|
||||
|
||||
loop {
|
||||
select! {
|
||||
_ = cancellation_token.cancelled() => {
|
||||
break;
|
||||
},
|
||||
Some(command) = command_stream.next() => {
|
||||
let command = command?;
|
||||
println!("Command Received:\n timestamp: {timestamp}\n a: {a}\n b: {b}\n c: {c}");
|
||||
|
||||
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"); };
|
||||
CommandResponse {
|
||||
uuid: command.uuid.clone(),
|
||||
success: true,
|
||||
response: format!(
|
||||
"Successfully Received Command! timestamp: {timestamp} a: {a} b: {b} c: {c}"
|
||||
),
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
println!("Command Received:\n timestamp: {timestamp}\n a: {a}\n b: {b}\n c: {c}");
|
||||
},
|
||||
}
|
||||
}
|
||||
cmd_handler.join().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user