use knx_rs_core::address::{DestinationAddress, GroupAddress, IndividualAddress};
use knx_rs_core::cemi::CemiFrame;
use knx_rs_core::dpt::{self, Dpt, DptValue};
use knx_rs_core::message::MessageCode;
use knx_rs_core::types::Priority;
use crate::error::KnxIpError;
use crate::{KnxConnection, KnxFuture};
pub trait GroupOps: KnxConnection {
fn group_write(&self, ga: GroupAddress, data: &[u8]) -> KnxFuture<'_, Result<(), KnxIpError>> {
let frame = match build_group_write(ga, data) {
Ok(frame) => frame,
Err(err) => return Box::pin(core::future::ready(Err(err))),
};
self.send(frame)
}
fn group_write_value(
&self,
ga: GroupAddress,
dpt: Dpt,
value: &DptValue,
) -> KnxFuture<'_, Result<(), KnxIpError>> {
let encoded = match dpt::encode(dpt, value) {
Ok(encoded) => encoded,
Err(err) => {
return Box::pin(core::future::ready(Err(KnxIpError::Protocol(
err.to_string(),
))));
}
};
let frame = match build_group_write(ga, &encoded) {
Ok(frame) => frame,
Err(err) => return Box::pin(core::future::ready(Err(err))),
};
self.send(frame)
}
fn group_read(&self, ga: GroupAddress) -> KnxFuture<'_, Result<(), KnxIpError>> {
let frame = match build_group_read(ga) {
Ok(frame) => frame,
Err(err) => return Box::pin(core::future::ready(Err(err))),
};
self.send(frame)
}
fn group_respond(
&self,
ga: GroupAddress,
data: &[u8],
) -> KnxFuture<'_, Result<(), KnxIpError>> {
let frame = match build_group_response(ga, data) {
Ok(frame) => frame,
Err(err) => return Box::pin(core::future::ready(Err(err))),
};
self.send(frame)
}
}
impl<T: KnxConnection> GroupOps for T {}
fn build_group_write(ga: GroupAddress, data: &[u8]) -> Result<CemiFrame, KnxIpError> {
let mut payload = Vec::with_capacity(2 + data.len());
payload.push(0x00); if data.len() == 1 && data[0] <= 0x3F {
payload.push(0x80 | (data[0] & 0x3F)); } else {
payload.push(0x80); payload.extend_from_slice(data);
}
CemiFrame::try_new_l_data(
MessageCode::LDataReq,
IndividualAddress::from_raw(0x0000), DestinationAddress::Group(ga),
Priority::Low,
&payload,
)
.map_err(|e| KnxIpError::Protocol(e.to_string()))
}
fn build_group_read(ga: GroupAddress) -> Result<CemiFrame, KnxIpError> {
CemiFrame::try_new_l_data(
MessageCode::LDataReq,
IndividualAddress::from_raw(0x0000),
DestinationAddress::Group(ga),
Priority::Low,
&[0x00, 0x00], )
.map_err(|e| KnxIpError::Protocol(e.to_string()))
}
fn build_group_response(ga: GroupAddress, data: &[u8]) -> Result<CemiFrame, KnxIpError> {
let mut payload = Vec::with_capacity(2 + data.len());
payload.push(0x00);
if data.len() == 1 && data[0] <= 0x3F {
payload.push(0x40 | (data[0] & 0x3F)); } else {
payload.push(0x40); payload.extend_from_slice(data);
}
CemiFrame::try_new_l_data(
MessageCode::LDataReq,
IndividualAddress::from_raw(0x0000),
DestinationAddress::Group(ga),
Priority::Low,
&payload,
)
.map_err(|e| KnxIpError::Protocol(e.to_string()))
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use knx_rs_core::address::GroupAddress;
use knx_rs_core::dpt::{DPT_SWITCH, DPT_VALUE_TEMP};
#[test]
fn build_group_write_short() {
let frame = build_group_write(GroupAddress::from_raw(0x0801), &[0x01]).unwrap();
assert_eq!(frame.destination_address_raw(), 0x0801);
let payload = frame.payload();
assert_eq!(payload[0], 0x00); assert_eq!(payload[1], 0x81); }
#[test]
fn build_group_write_long() {
let data = [0x0C, 0x34]; let frame = build_group_write(GroupAddress::from_raw(0x0801), &data).unwrap();
let payload = frame.payload();
assert_eq!(payload[0], 0x00);
assert_eq!(payload[1], 0x80); assert_eq!(&payload[2..], &[0x0C, 0x34]);
}
#[test]
fn build_group_read_frame() {
let frame = build_group_read(GroupAddress::from_raw(0x0801)).unwrap();
let payload = frame.payload();
assert_eq!(payload, &[0x00, 0x00]);
}
#[test]
fn build_group_response_short() {
let frame = build_group_response(GroupAddress::from_raw(0x0801), &[0x01]).unwrap();
let payload = frame.payload();
assert_eq!(payload[1], 0x41); }
#[test]
fn dpt_encoding_in_write() {
let encoded = dpt::encode(DPT_SWITCH, &DptValue::Bool(true)).unwrap();
let frame = build_group_write(GroupAddress::from_raw(0x0802), &encoded).unwrap();
let payload = frame.payload();
assert_eq!(payload[1], 0x81);
let encoded = dpt::encode(DPT_VALUE_TEMP, &DptValue::Float(21.5)).unwrap();
let frame = build_group_write(GroupAddress::from_raw(0x0801), &encoded).unwrap();
assert_eq!(frame.payload().len(), 4); }
}