lego-powered-up 0.4.0

Control Lego PoweredUp hubs and devices
Documentation
use tokio::sync::broadcast;

use crate::hubs::{Channels, Tokens};
use crate::notifications::{
    DatasetType, NetworkCommand, PortOutputCommandFeedbackFormat,
    PortValueCombinedFormat, PortValueSingleFormat,
};
use crate::IoTypeId;
use crate::{Error, Result};
use basic::Basic;
use definition::Definition;
use hubled::HubLed;
use motor::EncoderMotor;
use remote::RcDevice;
use sensor::GenericSensor;
use visionsensor::VisionSensor;

pub mod basic;
pub mod definition;
pub mod headlight;
pub mod hubled;
pub mod modes;
pub mod motor;
pub mod remote;
pub mod sensor;
pub mod visionsensor;

#[derive(Debug, Clone)]
pub struct IoDevice {
    pub def: Definition,
    tokens: Tokens,
    channels: Channels,
}

impl IoDevice {
    pub fn kind(&self) -> &IoTypeId {
        self.def.kind()
    }
    pub fn def(&self) -> &Definition {
        &self.def
    }
    pub fn port(&self) -> u8 {
        self.def.port()
    }
    pub fn channels(&self) -> &Channels {
        &self.channels
    }
    pub fn new(kind: IoTypeId, port: u8, tokens: Tokens) -> Self {
        Self {
            def: Definition::new(kind, port),
            tokens,
            channels: Default::default(),
        }
    }
    pub fn cache_channels(&mut self, channels: Channels) {
        self.channels = channels;
    }
}

// Implement device-traits
impl Basic for IoDevice {
    fn port(&self) -> u8 {
        self.def.port()
    }
    fn tokens(&self) -> Tokens {
        self.tokens.clone()
    }
    fn get_rx(&self) -> Result<broadcast::Receiver<PortValueSingleFormat>> {
        if let Some(sender) = &self.channels.singlevalue_sender {
            Ok(sender.subscribe())
        } else {
            Err(Error::NoneError(String::from("Sender not found")))
        }
    }
}
impl GenericSensor for IoDevice {
    fn check(&self) -> Result<()> {
        Ok(())
    }
    fn check_dataset(&self, mode: u8, datasettype: DatasetType) -> Result<()> {
        if let Some(pm) = self.def.modes().get(&mode) {
            let vf = pm.value_format;
            if datasettype == vf.dataset_type {
                Ok(())
            } else {
                Err(Error::NoneError(String::from("Incorrect dataset type")))
            }
        } else {
            Err(Error::NoneError(String::from("Mode not found")))
        }
    }
}

impl RcDevice for IoDevice {
    fn get_rx_pvs(&self) -> Result<broadcast::Receiver<PortValueSingleFormat>> {
        if let Some(sender) = &self.channels.singlevalue_sender {
            Ok(sender.subscribe())
        } else {
            Err(Error::NoneError(String::from("Sender not found")))
        }
    }
    fn get_rx_nwc(&self) -> Result<broadcast::Receiver<NetworkCommand>> {
        if let Some(sender) = &self.channels.networkcmd_sender {
            Ok(sender.subscribe())
        } else {
            Err(Error::NoneError(String::from("Sender not found")))
        }
    }
    fn check(&self) -> Result<()> {
        match self.def.kind() {
            IoTypeId::RemoteButtons => Ok(()),
            _ => Err(Error::HubError(String::from(
                "Not a remote control device",
            ))),
        }
    }
}

impl EncoderMotor for IoDevice {
    fn get_rx_combined(
        &self,
    ) -> Result<broadcast::Receiver<PortValueCombinedFormat>> {
        if let Some(sender) = &self.channels.combinedvalue_sender {
            Ok(sender.subscribe())
        } else {
            Err(Error::NoneError(String::from("Sender not found")))
        }
    }
    fn get_rx_feedback(
        &self,
    ) -> Result<broadcast::Receiver<PortOutputCommandFeedbackFormat>> {
        if let Some(sender) = &self.channels.commandfeedback_sender {
            Ok(sender.subscribe())
        } else {
            Err(Error::NoneError(String::from("Sender not found")))
        }
    }
    fn check(&self) -> Result<()> {
        match self.def.kind() {
            IoTypeId::TechnicLargeLinearMotor
            | IoTypeId::TechnicXLargeLinearMotor
            | IoTypeId::TechnicMediumAngularMotorGrey
            | IoTypeId::TechnicLargeAngularMotorGrey
            | IoTypeId::InternalMotorTacho => Ok(()),
            _ => Err(Error::HubError(String::from("Not an Encoder Motor"))),
        }
    }
}

impl HubLed for IoDevice {
    fn check(&self) -> Result<()> {
        match self.def.kind() {
            IoTypeId::HubLed => Ok(()),
            _ => Err(Error::HubError(String::from("Not a Hub LED device"))),
        }
    }
}

impl VisionSensor for IoDevice {
    fn check(&self) -> Result<()> {
        match self.def.kind() {
            IoTypeId::VisionSensor => Ok(()),
            _ => {
                Err(Error::HubError(String::from("Not a Vision sensor Motor")))
            }
        }
    }
}

#[macro_export]
macro_rules! device_trait {
    ($name:tt, [$( $b:item ),*])  => {
        #[async_trait]
        pub trait $name: Debug + Send + Sync + Basic {
            fn check(&self) -> Result<()>;
            $(
                $b
            )*
        }
    }
}