1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// ------------------------------------------------------------------------------
// Copyright 2019 Uwe Arzt, mail@uwe-arzt.de
// SPDX-License-Identifier: Apache-2.0
// ------------------------------------------------------------------------------

use std::fmt;

use byteorder::BigEndian;
use byteorder::ByteOrder;
use num_traits::ToPrimitive;

const HEADER_LENGTH: u8 = 0x06;
const KNXNET_VERSION: u8 = 0x10;

#[derive(PartialEq, Primitive, Debug)]
#[repr(u16)]
pub enum ServiceType {
    SearchRequest = 0x0201,
    SearchResponse = 0x0202,
    DescriptionRequest = 0x0203,
    DescriptionResponse = 0x0204,
    ConnectionRequest = 0x0205,
    ConnectionResponse = 0x0206,
    ConnectionstateRequest = 0x0207,
    ConnectionstateResponse = 0x0208,
    DisconnectRequest = 0x0209,
    DisconnectResponse = 0x020A,
    TunnelRequest = 0x0420,
    TunnelResponse = 0x0421,
    DeviceConfigurationRequest = 0x0310,
    DeviceConfigurationAck = 0x0311,
    RoutingIndication = 0x0530,
}

#[derive(PartialEq)]
pub struct Header {
    _header_length: u8,
    _knxnet_version: u8,
    service_type: ServiceType,
    payload_length: u16,
}

impl Header {
    /// Create an Header
    pub fn new(service_type: ServiceType, payload_length: u16) -> Header {
        Header {
            _header_length: HEADER_LENGTH,
            _knxnet_version: KNXNET_VERSION,
            service_type: service_type,
            payload_length: payload_length,
        }
    }

    /// Return the byte-size of an `Header`. Always 0x06.
    pub fn length() -> u8 {
        HEADER_LENGTH
    }

    /// Return the `version` field of a header. Always 0x10 (1.0).
    pub fn knxnet_version() -> u8 {
        KNXNET_VERSION
    }
    pub fn encode(&self) -> Vec<u8> {
        let mut buf = vec![self._header_length, self._knxnet_version];
        let mut tmp = [0, 2];
        BigEndian::write_u16(&mut tmp, self.service_type.to_u16().unwrap());
        buf.extend(&tmp);
        BigEndian::write_u16(&mut tmp, self.payload_length);
        buf.extend(&tmp);
        buf
    }
}

impl fmt::Display for Header {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "ServiceType: {:?} Length: {}",
            self.service_type, self.payload_length
        )
    }
}
impl fmt::Debug for Header {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{{ {} }}", self)
    }
}

// ------------------------------------------------------------------------------
#[cfg(test)]
mod tests {

    use crate::header::Header;
    use crate::header::ServiceType;
    use std::mem::size_of;

    #[test]
    fn t_header_length() {
        assert_eq!(size_of::<Header>(), 0x06);
        assert_eq!(Header::length(), 0x06);
    }
    #[test]
    fn t_header_version() {
        assert_eq!(Header::knxnet_version(), 0x10);
    }
    #[test]
    fn t_header_fmt() {
        assert_eq!(
            format!("{}", Header::new(ServiceType::DescriptionRequest, 0x17)),
            "ServiceType: DescriptionRequest Length: 23"
        );
    }
    #[test]
    fn t_header_encode() {
        assert_eq!(
            Header::new(ServiceType::DescriptionRequest, 0x17).encode(),
            &[0x06, 0x10, 0x02, 0x03, 0x00, 0x17]
        );
    }
}