#[cfg(feature = "alloc")]
use alloc::collections::BTreeMap;
use smol_str::SmolStr;
use crate::path::PropertyPath;
use crate::thing::ThingId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum Qos {
#[default]
AtMostOnce = 0,
AtLeastOnce = 1,
ExactlyOnce = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PayloadCodec {
#[default]
Json,
Cbor,
Raw,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RegisterArea {
Coil,
DiscreteInput,
HoldingRegister,
InputRegister,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ByteOrder {
#[default]
BigEndian,
LittleEndian,
MidBigEndian,
MidLittleEndian,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DataType {
U16,
I16,
U32,
I32,
F32,
F64,
String(u16),
Bytes(u16),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u16)]
pub enum ContentFormat {
#[default]
TextPlain = 0,
OctetStream = 42,
Json = 50,
Cbor = 60,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "kind", rename_all = "snake_case"))]
pub enum ProtocolBinding {
Mqtt {
publish_topic: SmolStr,
subscribe_topic: SmolStr,
qos: Qos,
codec: PayloadCodec,
},
Modbus {
unit_id: u8,
area: RegisterArea,
address: u16,
data_type: DataType,
byte_order: ByteOrder,
},
Coap {
uri: SmolStr,
content_format: ContentFormat,
observable: bool,
},
OpcUa {
node_id: SmolStr,
attribute: u32,
},
}
#[cfg(feature = "alloc")]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ThingMapping {
#[cfg_attr(feature = "serde", serde(rename = "thingId"))]
pub thing_id: ThingId,
pub bindings: BTreeMap<PropertyPath, ProtocolBinding>,
}
#[cfg(feature = "alloc")]
impl ThingMapping {
pub fn new(thing_id: ThingId) -> Self {
Self {
thing_id,
bindings: BTreeMap::new(),
}
}
pub fn bind(
&mut self,
path: PropertyPath,
binding: ProtocolBinding,
) -> Option<ProtocolBinding> {
self.bindings.insert(path, binding)
}
pub fn get(&self, path: &PropertyPath) -> Option<&ProtocolBinding> {
self.bindings.get(path)
}
}
#[cfg(test)]
#[cfg(feature = "alloc")]
mod tests {
use super::*;
use crate::thing::{FeatureId, PropertyId};
#[test]
fn bind_and_lookup() {
let mut m = ThingMapping::new(ThingId::new("acme:pump-7"));
let p = PropertyPath::new(
ThingId::new("acme:pump-7"),
FeatureId::new("temperature"),
PropertyId::new("value"),
);
let b = ProtocolBinding::Modbus {
unit_id: 1,
area: RegisterArea::HoldingRegister,
address: 40001 - 40001, data_type: DataType::F32,
byte_order: ByteOrder::BigEndian,
};
assert!(m.bind(p.clone(), b.clone()).is_none());
assert_eq!(m.get(&p), Some(&b));
}
}