implement command responses

This commit is contained in:
2025-12-28 16:23:42 -05:00
parent c3253f3204
commit 678b10de08
8 changed files with 242 additions and 57 deletions

View File

@@ -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"

View File

@@ -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(())
}