use std::collections::HashMap;
use std::sync::Arc;
use async_trait::async_trait;
use parking_lot::RwLock;
use mabi_core::device::{DeviceHandle, DeviceInfo, DeviceState};
use mabi_core::types::{DataPoint, DataPointDef};
use mabi_core::value::Value;
use mabi_core::Result as CoreResult;
#[async_trait]
pub trait DevicePort: Send + Sync {
fn info(&self) -> DeviceInfo;
fn id(&self) -> String {
self.info().id
}
fn state(&self) -> DeviceState {
self.info().state
}
async fn start(&self) -> CoreResult<()>;
async fn stop(&self) -> CoreResult<()>;
async fn read(&self, point_id: &str) -> CoreResult<DataPoint>;
async fn write(&self, point_id: &str, value: Value) -> CoreResult<()>;
fn point_definitions(&self) -> Vec<DataPointDef> {
Vec::new()
}
}
pub type DynDevicePort = Arc<dyn DevicePort>;
#[derive(Clone)]
pub struct CoreDevicePort {
handle: DeviceHandle,
}
impl CoreDevicePort {
pub fn new(handle: DeviceHandle) -> Self {
Self { handle }
}
pub fn handle(&self) -> &DeviceHandle {
&self.handle
}
pub fn into_shared(self) -> DynDevicePort {
Arc::new(self)
}
}
impl From<DeviceHandle> for CoreDevicePort {
fn from(handle: DeviceHandle) -> Self {
Self::new(handle)
}
}
#[async_trait]
impl DevicePort for CoreDevicePort {
fn info(&self) -> DeviceInfo {
self.handle.info()
}
async fn start(&self) -> CoreResult<()> {
self.handle.start().await
}
async fn stop(&self) -> CoreResult<()> {
self.handle.stop().await
}
async fn read(&self, point_id: &str) -> CoreResult<DataPoint> {
self.handle.read(point_id).await
}
async fn write(&self, point_id: &str, value: Value) -> CoreResult<()> {
self.handle.write(point_id, value).await
}
}
#[derive(Clone, Default)]
pub struct DeviceRegistry {
inner: Arc<RwLock<HashMap<String, DynDevicePort>>>,
}
impl DeviceRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&self, device_id: impl Into<String>, port: DynDevicePort) {
self.inner.write().insert(device_id.into(), port);
}
pub fn register_handle(&self, device_id: impl Into<String>, handle: DeviceHandle) {
self.register(device_id, CoreDevicePort::new(handle).into_shared());
}
pub fn get(&self, device_id: &str) -> Option<DynDevicePort> {
self.inner.read().get(device_id).cloned()
}
pub fn contains(&self, device_id: &str) -> bool {
self.inner.read().contains_key(device_id)
}
pub fn device_ids(&self) -> Vec<String> {
self.inner.read().keys().cloned().collect()
}
pub fn entries(&self) -> Vec<(String, DynDevicePort)> {
self.inner
.read()
.iter()
.map(|(device_id, port)| (device_id.clone(), Arc::clone(port)))
.collect()
}
pub fn len(&self) -> usize {
self.inner.read().len()
}
pub fn is_empty(&self) -> bool {
self.inner.read().is_empty()
}
pub fn remove(&self, device_id: &str) -> Option<DynDevicePort> {
self.inner.write().remove(device_id)
}
pub fn clear(&self) {
self.inner.write().clear();
}
}
#[cfg(test)]
mod tests {
use super::DeviceRegistry;
#[test]
fn registry_starts_empty() {
let registry = DeviceRegistry::new();
assert!(registry.is_empty());
assert_eq!(registry.len(), 0);
}
}