kos_core 0.1.2

The K-Scale Operating System
use crate::grpc_interface::google::longrunning::Operation;
use crate::hal::Actuator;
use crate::kos_proto::actuator::actuator_service_server::ActuatorService;
use crate::kos_proto::actuator::*;
use crate::kos_proto::common::ActionResponse;
use crate::telemetry::Telemetry;
use crate::telemetry_types::{ActuatorCommand, ActuatorState};
use std::sync::Arc;
use tonic::{Request, Response, Status};
use tracing::trace;

pub struct ActuatorServiceImpl {
    actuator: Arc<dyn Actuator>,
}

impl ActuatorServiceImpl {
    pub fn new(actuator: Arc<dyn Actuator>) -> Self {
        Self { actuator }
    }
}

#[tonic::async_trait]
impl ActuatorService for ActuatorServiceImpl {
    async fn command_actuators(
        &self,
        request: Request<CommandActuatorsRequest>,
    ) -> Result<Response<CommandActuatorsResponse>, Status> {
        let commands = request.into_inner().commands;

        let telemetry_commands: Vec<_> = commands.iter().map(ActuatorCommand::from).collect();

        let results = self
            .actuator
            .command_actuators(commands)
            .await
            .map_err(|e| Status::internal(format!("Failed to command actuators, {:?}", e)))?;

        trace!(
            "Commanding actuators, request: {:?}, results: {:?}",
            telemetry_commands.clone(),
            results
        );

        let telemetry = Telemetry::get().await;
        if let Some(telemetry) = telemetry {
            telemetry.increment_inference_step();

            if let Err(e) = telemetry
                .publish("actuator/command", &telemetry_commands)
                .await
            {
                tracing::warn!("Failed to publish telemetry: {}", e);
            }
        }

        Ok(Response::new(CommandActuatorsResponse { results }))
    }

    async fn configure_actuator(
        &self,
        request: Request<ConfigureActuatorRequest>,
    ) -> Result<Response<ActionResponse>, Status> {
        let config = request.into_inner();
        let response = self
            .actuator
            .configure_actuator(config)
            .await
            .map_err(|e| Status::internal(format!("Failed to configure actuator, {:?}", e)))?;
        Ok(Response::new(response))
    }

    async fn calibrate_actuator(
        &self,
        request: Request<CalibrateActuatorRequest>,
    ) -> Result<Response<Operation>, Status> {
        let calibrate_request = request.into_inner();
        let operation = self
            .actuator
            .calibrate_actuator(calibrate_request)
            .await
            .map_err(|e| Status::internal(format!("Failed to calibrate actuator, {:?}", e)))?;

        Ok(Response::new(operation))
    }

    async fn get_actuators_state(
        &self,
        request: Request<GetActuatorsStateRequest>,
    ) -> Result<Response<GetActuatorsStateResponse>, Status> {
        let request = request.into_inner();
        let actuator_ids = request.actuator_ids.clone();
        let states = self
            .actuator
            .get_actuators_state(actuator_ids)
            .await
            .map_err(|e| Status::internal(format!("Failed to get actuators state, {:?}", e)))?;

        let telemetry_states: Vec<_> = states.iter().map(ActuatorState::from).collect();
        let telemetry = Telemetry::get().await;
        if let Some(telemetry) = telemetry {
            if let Err(e) = telemetry.publish("actuator/state", &telemetry_states).await {
                tracing::warn!("Failed to publish telemetry: {}", e);
            }
        }

        trace!(
            "Getting actuators state, request: {:?}, response: {:?}",
            request.clone(),
            states
        );
        Ok(Response::new(GetActuatorsStateResponse { states }))
    }
}