rs-modbus 0.1.0

A pure Rust implementation of MODBUS protocol.
Documentation
#[derive(Debug, Clone)]
pub struct ApplicationDataUnit {
    pub transaction: Option<u16>,
    pub unit: u8,
    pub fc: u8,
    pub data: Vec<u8>,
}

impl ApplicationDataUnit {
    pub fn new(unit: u8, fc: u8, data: Vec<u8>) -> Self {
        Self {
            transaction: None,
            unit,
            fc,
            data,
        }
    }

    pub fn with_transaction(mut self, transaction: u16) -> Self {
        self.transaction = Some(transaction);
        self
    }
}

#[derive(Debug, Clone)]
pub struct FramedDataUnit {
    pub adu: ApplicationDataUnit,
    pub raw: Vec<u8>,
}

#[derive(Debug, Clone)]
pub struct ServerId {
    pub server_id: u8,
    pub run_indicator_status: bool,
    pub additional_data: Vec<u8>,
}

#[derive(Debug, Clone)]
pub struct DeviceIdentification {
    pub read_device_id_code: u8,
    pub conformity_level: u8,
    pub more_follows: bool,
    pub next_object_id: u8,
    pub objects: Vec<DeviceObject>,
}

#[derive(Debug, Clone)]
pub struct DeviceObject {
    pub id: u8,
    pub value: String,
}

#[derive(Debug, Clone, Default)]
pub struct AddressRange {
    pub discrete_inputs: Vec<(u16, u16)>,
    pub coils: Vec<(u16, u16)>,
    pub input_registers: Vec<(u16, u16)>,
    pub holding_registers: Vec<(u16, u16)>,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_adu_new() {
        let adu = ApplicationDataUnit::new(1, 0x03, vec![0x00, 0x00, 0x00, 0x0a]);
        assert_eq!(adu.unit, 1);
        assert_eq!(adu.fc, 0x03);
        assert_eq!(adu.data, vec![0x00, 0x00, 0x00, 0x0a]);
        assert_eq!(adu.transaction, None);
    }

    #[test]
    fn test_adu_with_transaction() {
        let adu = ApplicationDataUnit::new(1, 0x03, vec![]).with_transaction(42);
        assert_eq!(adu.transaction, Some(42));
    }

    #[test]
    fn test_framed_data_unit() {
        let adu = ApplicationDataUnit::new(1, 0x03, vec![0x00, 0x01]);
        let raw = vec![0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x03, 0x00, 0x01];
        let frame = FramedDataUnit {
            adu,
            raw: raw.clone(),
        };
        assert_eq!(frame.raw, raw);
    }

    #[test]
    fn test_server_id() {
        let sid = ServerId {
            server_id: 1,
            run_indicator_status: true,
            additional_data: vec![1, 2, 3],
        };
        assert_eq!(sid.server_id, 1);
        assert!(sid.run_indicator_status);
        assert_eq!(sid.additional_data, vec![1, 2, 3]);
    }

    #[test]
    fn test_device_object() {
        let obj = DeviceObject {
            id: 0x01,
            value: "ProductCode".to_string(),
        };
        assert_eq!(obj.id, 0x01);
        assert_eq!(obj.value, "ProductCode");
    }

    #[test]
    fn test_device_identification() {
        let di = DeviceIdentification {
            read_device_id_code: 0x01,
            conformity_level: 0x81,
            more_follows: false,
            next_object_id: 0x00,
            objects: vec![
                DeviceObject {
                    id: 0x00,
                    value: "VendorName".to_string(),
                },
                DeviceObject {
                    id: 0x01,
                    value: "ProductCode".to_string(),
                },
            ],
        };
        assert_eq!(di.read_device_id_code, 0x01);
        assert_eq!(di.conformity_level, 0x81);
        assert!(!di.more_follows);
        assert_eq!(di.objects.len(), 2);
    }

    #[test]
    fn test_address_range_default() {
        let range = AddressRange::default();
        assert!(range.coils.is_empty());
        assert!(range.discrete_inputs.is_empty());
        assert!(range.input_registers.is_empty());
        assert!(range.holding_registers.is_empty());
    }
}