use std::collections::HashMap;
use std::sync::Arc;
use parking_lot::RwLock;
use super::codec::{BoxedDptCodec, DptCodec, DptId};
use super::types::*;
use crate::error::{KnxError, KnxResult};
pub struct DptRegistry {
codecs: RwLock<HashMap<DptId, Arc<BoxedDptCodec>>>,
main_type_fallbacks: RwLock<HashMap<u16, Arc<BoxedDptCodec>>>,
}
impl DptRegistry {
pub fn new() -> Self {
let registry = Self {
codecs: RwLock::new(HashMap::new()),
main_type_fallbacks: RwLock::new(HashMap::new()),
};
registry.register_standard_types();
registry
}
pub fn empty() -> Self {
Self {
codecs: RwLock::new(HashMap::new()),
main_type_fallbacks: RwLock::new(HashMap::new()),
}
}
pub fn register<C: DptCodec + 'static>(&self, codec: C) {
let id = codec.id();
let boxed: BoxedDptCodec = Box::new(codec);
self.codecs.write().insert(id, Arc::new(boxed));
}
pub fn register_boxed(&self, codec: BoxedDptCodec) {
let id = codec.id();
self.codecs.write().insert(id, Arc::new(codec));
}
pub fn register_fallback<C: DptCodec + 'static>(&self, main_type: u16, codec: C) {
let boxed: BoxedDptCodec = Box::new(codec);
self.main_type_fallbacks
.write()
.insert(main_type, Arc::new(boxed));
}
pub fn get(&self, id: &DptId) -> Option<Arc<BoxedDptCodec>> {
if let Some(codec) = self.codecs.read().get(id).cloned() {
return Some(codec);
}
self.main_type_fallbacks.read().get(&id.main).cloned()
}
pub fn get_or_err(&self, id: &DptId) -> KnxResult<Arc<BoxedDptCodec>> {
self.get(id)
.ok_or_else(|| KnxError::InvalidDpt(format!("Unknown DPT: {}", id)))
}
pub fn get_by_str(&self, id: &str) -> Option<Arc<BoxedDptCodec>> {
id.parse::<DptId>().ok().and_then(|id| self.get(&id))
}
pub fn contains(&self, id: &DptId) -> bool {
self.codecs.read().contains_key(id)
|| self.main_type_fallbacks.read().contains_key(&id.main)
}
pub fn list_ids(&self) -> Vec<DptId> {
self.codecs.read().keys().copied().collect()
}
pub fn len(&self) -> usize {
self.codecs.read().len()
}
pub fn is_empty(&self) -> bool {
self.codecs.read().is_empty()
}
fn register_standard_types(&self) {
self.register(Dpt1Switch);
self.register(Dpt1Bool);
self.register(Dpt1UpDown);
self.register(Dpt2SwitchControl);
self.register(Dpt3DimmingControl);
self.register(Dpt3BlindsControl);
self.register(Dpt5Scaling);
self.register(Dpt5Angle);
self.register(Dpt5Counter);
self.register(Dpt6Percent);
self.register(Dpt7Pulses);
self.register(Dpt8PulsesDiff);
self.register(Dpt9Temperature);
self.register(Dpt9Lux);
self.register(Dpt9Humidity);
self.register(Dpt12Counter);
self.register(Dpt13Counter);
self.register(Dpt14Float);
self.register(Dpt16String);
self.register(Dpt17Scene);
self.register(Dpt18SceneControl);
self.register(Dpt20HvacMode);
self.register(Dpt232Rgb);
self.register_fallback(1, Dpt1Switch);
self.register_fallback(5, Dpt5Counter);
self.register_fallback(9, Dpt9Temperature);
self.register_fallback(14, Dpt14Float);
}
}
impl Default for DptRegistry {
fn default() -> Self {
Self::new()
}
}
lazy_static::lazy_static! {
pub static ref GLOBAL_DPT_REGISTRY: DptRegistry = DptRegistry::new();
}
#[cfg(test)]
pub fn get_codec(id: &DptId) -> Option<Arc<BoxedDptCodec>> {
GLOBAL_DPT_REGISTRY.get(id)
}
#[cfg(test)]
pub fn get_codec_by_str(id: &str) -> Option<Arc<BoxedDptCodec>> {
GLOBAL_DPT_REGISTRY.get_by_str(id)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dpt::DptValue;
#[test]
fn test_registry_get() {
let registry = DptRegistry::new();
let codec = registry.get(&DptId::new(9, 1)).unwrap();
assert_eq!(codec.name(), "Temperature");
}
#[test]
fn test_registry_get_by_str() {
let registry = DptRegistry::new();
assert!(registry.get_by_str("DPT 9.001").is_some());
assert!(registry.get_by_str("9.1").is_some());
assert!(registry.get_by_str("1").is_some());
}
#[test]
fn test_registry_fallback() {
let registry = DptRegistry::new();
let codec = registry.get(&DptId::new(9, 999));
assert!(codec.is_some());
}
#[test]
fn test_registry_custom_codec() {
let registry = DptRegistry::empty();
struct MyDpt;
impl DptCodec for MyDpt {
fn id(&self) -> DptId {
DptId::new(999, 1)
}
fn name(&self) -> &'static str {
"My Custom DPT"
}
fn size(&self) -> usize {
2
}
fn encode(&self, _value: &DptValue) -> KnxResult<Vec<u8>> {
Ok(vec![0xAB, 0xCD])
}
fn decode(&self, _data: &[u8]) -> KnxResult<DptValue> {
Ok(DptValue::U16(0xABCD))
}
}
registry.register(MyDpt);
let codec = registry.get(&DptId::new(999, 1)).unwrap();
assert_eq!(codec.name(), "My Custom DPT");
}
#[test]
fn test_global_registry() {
let codec = get_codec(&DptId::new(1, 1)).unwrap();
assert_eq!(codec.name(), "Switch");
let codec = get_codec_by_str("DPT 9.001").unwrap();
assert_eq!(codec.name(), "Temperature");
}
}