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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
use std::{
fmt,
hash::{Hash, Hasher},
};
use chrono::{DateTime, Utc};
use crate::id_hash::IdHash;
/// All VBus data types consist of a `Header` element.
///
/// Just like the fact that the first 6 bytes of each VBus live byte stream are the same (SYNC to
/// protocol version), the `Header` struct is the common type for all concrete VBus data types.
///
/// In addition to the information stored within the first 6 bytes of the VBus header (destination
/// and source addresses as well as the protocol version), the `Header` type also stores the
/// VBus channel associated with this data as well as the point in time the data was received.
///
/// ## The "identity" of `Header` values
///
/// The fields in the `Header` struct can be separated into two categories:
///
/// 1. Fields that are used to identify the `Header` and (for concrete VBus data types) its payload:
/// - `channel`
/// - `source_address`
/// - `destination_address`
/// - `protocol_version`
/// 2. Fields that are not used to identify the `Header`:
/// - `timestamp`
///
/// Two `Header` values with different `timestamp` fields are considered identical, if all of their
/// other fields match.
///
/// This is also respected by the `id_hash` and `id_string` functions. They return the same result
/// for VBus data values that are considered "identical", allowing some fields to differ.
#[derive(Clone)]
pub struct Header {
/// The timestamp when this `Header` was received.
pub timestamp: DateTime<Utc>,
/// The channel number on which this `Header` was received.
pub channel: u8,
/// The destination address of this `Header`.
pub destination_address: u16,
/// The source address of this `Header`.
pub source_address: u16,
/// The VBus protocol version of this `Header`.
pub protocol_version: u8,
}
impl Header {
/// Creates the common identification string prefix for this `Header`.
///
/// The string contains all fields that count towards the "identity" of the `Header`:
///
/// - `channel`
/// - `destination_address`
/// - `source_address`
/// - `protocol_version`
///
/// # Examples
///
/// ```rust
/// use resol_vbus::{Header};
/// use resol_vbus::utils::utc_timestamp;
///
/// let header = Header {
/// timestamp: utc_timestamp(1485688933),
/// channel: 0x11,
/// destination_address: 0x1213,
/// source_address: 0x1415,
/// protocol_version: 0x16,
/// };
///
/// assert_eq!("11_1213_1415_16", header.id_string());
/// ```
pub fn id_string(&self) -> String {
format!(
"{:02X}_{:04X}_{:04X}_{:02X}",
self.channel, self.destination_address, self.source_address, self.protocol_version
)
}
}
impl IdHash for Header {
/// Returns an identification hash for this `Header`.
///
/// The hash contains all fields that count towards the "identity" of the `Header`:
///
/// - `channel`
/// - `destination_address`
/// - `source_address`
/// - `protocol_version`
///
/// # Examples
///
/// ```rust
/// use resol_vbus::{Header, id_hash};
/// use resol_vbus::utils::utc_timestamp;
///
/// let header = Header {
/// timestamp: utc_timestamp(1485688933),
/// channel: 0x11,
/// destination_address: 0x1213,
/// source_address: 0x1415,
/// protocol_version: 0x16,
/// };
///
/// assert_eq!(8369676560183260683, id_hash(&header));
/// ```
fn id_hash<H: Hasher>(&self, h: &mut H) {
self.channel.hash(h);
self.destination_address.hash(h);
self.source_address.hash(h);
self.protocol_version.hash(h);
}
}
impl Default for Header {
fn default() -> Header {
Header {
timestamp: Utc::now(),
channel: 0x00,
destination_address: 0x0000,
source_address: 0x0000,
protocol_version: 0x00,
}
}
}
impl fmt::Debug for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Header")
.field("timestamp", &self.timestamp)
.field("channel", &format_args!("0x{:02X}", self.channel))
.field(
"destination_address",
&format_args!("0x{:04X}", self.destination_address),
)
.field(
"source_address",
&format_args!("0x{:04X}", self.source_address),
)
.field(
"protocol_version",
&format_args!("0x{:02X}", self.protocol_version),
)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::utc_timestamp;
#[test]
fn test_debug_fmt() {
let header = Header {
timestamp: utc_timestamp(1485688933),
channel: 0x11,
destination_address: 0x1213,
source_address: 0x1415,
protocol_version: 0x16,
};
let result = format!("{:?}", header);
assert_eq!("Header { timestamp: 2017-01-29T11:22:13Z, channel: 0x11, destination_address: 0x1213, source_address: 0x1415, protocol_version: 0x16 }", result);
}
}