1use std::collections::HashMap;
4use std::sync::Arc;
5
6use async_trait::async_trait;
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10use crate::error::Result;
11use crate::protocol::Protocol;
12use crate::types::{DataPoint, DataPointDef};
13use crate::value::Value;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
17#[serde(rename_all = "lowercase")]
18pub enum DeviceState {
19 #[default]
21 Uninitialized,
22 Initializing,
24 Online,
26 Offline,
28 Error,
30 ShuttingDown,
32}
33
34impl DeviceState {
35 pub fn is_operational(&self) -> bool {
37 matches!(self, Self::Online)
38 }
39
40 pub fn can_accept_requests(&self) -> bool {
42 matches!(self, Self::Online | Self::Initializing)
43 }
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct DeviceInfo {
49 pub id: String,
51 pub name: String,
53 pub description: String,
55 pub protocol: Protocol,
57 pub state: DeviceState,
59 pub point_count: usize,
61 pub created_at: DateTime<Utc>,
63 pub updated_at: DateTime<Utc>,
65 #[serde(default)]
67 pub metadata: HashMap<String, String>,
68}
69
70impl DeviceInfo {
71 pub fn new(id: impl Into<String>, name: impl Into<String>, protocol: Protocol) -> Self {
73 let now = Utc::now();
74 Self {
75 id: id.into(),
76 name: name.into(),
77 description: String::new(),
78 protocol,
79 state: DeviceState::Uninitialized,
80 point_count: 0,
81 created_at: now,
82 updated_at: now,
83 metadata: HashMap::new(),
84 }
85 }
86
87 pub fn with_description(mut self, description: impl Into<String>) -> Self {
89 self.description = description.into();
90 self
91 }
92
93 pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
95 self.metadata.insert(key.into(), value.into());
96 self
97 }
98}
99
100#[async_trait]
102pub trait Device: Send + Sync {
103 fn info(&self) -> &DeviceInfo;
105
106 fn id(&self) -> &str {
108 &self.info().id
109 }
110
111 fn name(&self) -> &str {
113 &self.info().name
114 }
115
116 fn protocol(&self) -> Protocol {
118 self.info().protocol
119 }
120
121 fn state(&self) -> DeviceState {
123 self.info().state
124 }
125
126 async fn initialize(&mut self) -> Result<()>;
128
129 async fn start(&mut self) -> Result<()>;
131
132 async fn stop(&mut self) -> Result<()>;
134
135 async fn tick(&mut self) -> Result<()>;
137
138 fn point_definitions(&self) -> Vec<&DataPointDef>;
140
141 fn point_definition(&self, point_id: &str) -> Option<&DataPointDef>;
143
144 async fn read(&self, point_id: &str) -> Result<DataPoint>;
146
147 async fn read_multiple(&self, point_ids: &[&str]) -> Result<Vec<DataPoint>> {
149 let mut results = Vec::with_capacity(point_ids.len());
150 for point_id in point_ids {
151 results.push(self.read(point_id).await?);
152 }
153 Ok(results)
154 }
155
156 async fn read_all(&self) -> Result<Vec<DataPoint>> {
158 let point_ids: Vec<&str> = self
159 .point_definitions()
160 .iter()
161 .map(|d| d.id.as_str())
162 .collect();
163 self.read_multiple(&point_ids).await
164 }
165
166 async fn write(&mut self, point_id: &str, value: Value) -> Result<()>;
168
169 async fn write_multiple(&mut self, values: &[(&str, Value)]) -> Result<()> {
171 for (point_id, value) in values {
172 self.write(point_id, value.clone()).await?;
173 }
174 Ok(())
175 }
176
177 fn subscribe(&self) -> Option<tokio::sync::broadcast::Receiver<DataPoint>> {
179 None
180 }
181
182 fn statistics(&self) -> DeviceStatistics {
184 DeviceStatistics::default()
185 }
186}
187
188#[derive(Debug, Clone, Default, Serialize, Deserialize)]
190pub struct DeviceStatistics {
191 pub reads_total: u64,
193 pub writes_total: u64,
195 pub read_errors: u64,
197 pub write_errors: u64,
199 pub ticks_total: u64,
201 pub avg_tick_duration_us: u64,
203 pub last_error: Option<String>,
205 pub uptime_secs: u64,
207}
208
209impl DeviceStatistics {
210 pub fn record_read(&mut self) {
212 self.reads_total += 1;
213 }
214
215 pub fn record_read_error(&mut self, error: &str) {
217 self.read_errors += 1;
218 self.last_error = Some(error.to_string());
219 }
220
221 pub fn record_write(&mut self) {
223 self.writes_total += 1;
224 }
225
226 pub fn record_write_error(&mut self, error: &str) {
228 self.write_errors += 1;
229 self.last_error = Some(error.to_string());
230 }
231
232 pub fn record_tick(&mut self, duration_us: u64) {
234 self.ticks_total += 1;
235 self.avg_tick_duration_us =
237 (self.avg_tick_duration_us * (self.ticks_total - 1) + duration_us) / self.ticks_total;
238 }
239}
240
241pub type BoxedDevice = Box<dyn Device>;
243
244pub type ArcDevice = Arc<dyn Device>;
246
247#[derive(Clone)]
250pub struct DeviceHandle {
251 inner: Arc<tokio::sync::RwLock<BoxedDevice>>,
252 cached_info: Arc<parking_lot::RwLock<DeviceInfo>>,
254}
255
256impl DeviceHandle {
257 pub fn new(device: BoxedDevice) -> Self {
259 let info = device.info().clone();
260 Self {
261 inner: Arc::new(tokio::sync::RwLock::new(device)),
262 cached_info: Arc::new(parking_lot::RwLock::new(info)),
263 }
264 }
265
266 pub fn info(&self) -> DeviceInfo {
268 self.cached_info.read().clone()
269 }
270
271 pub fn id(&self) -> String {
273 self.cached_info.read().id.clone()
274 }
275
276 pub fn state(&self) -> DeviceState {
278 self.cached_info.read().state
279 }
280
281 pub async fn refresh_info(&self) {
283 let info = self.inner.read().await.info().clone();
284 *self.cached_info.write() = info;
285 }
286
287 pub async fn initialize(&self) -> Result<()> {
289 let result = self.inner.write().await.initialize().await;
290 self.refresh_info().await;
291 result
292 }
293
294 pub async fn start(&self) -> Result<()> {
296 let result = self.inner.write().await.start().await;
297 self.refresh_info().await;
298 result
299 }
300
301 pub async fn stop(&self) -> Result<()> {
303 let result = self.inner.write().await.stop().await;
304 self.refresh_info().await;
305 result
306 }
307
308 pub async fn tick(&self) -> Result<()> {
310 self.inner.write().await.tick().await
311 }
312
313 pub async fn read(&self, point_id: &str) -> Result<DataPoint> {
315 self.inner.read().await.read(point_id).await
316 }
317
318 pub async fn write(&self, point_id: &str, value: Value) -> Result<()> {
320 self.inner.write().await.write(point_id, value).await
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327
328 #[test]
329 fn test_device_state() {
330 assert!(!DeviceState::Uninitialized.is_operational());
331 assert!(DeviceState::Online.is_operational());
332 assert!(DeviceState::Online.can_accept_requests());
333 }
334
335 #[test]
336 fn test_device_info() {
337 let info = DeviceInfo::new("dev-001", "Test Device", Protocol::ModbusTcp)
338 .with_description("A test device")
339 .with_metadata("location", "Building A");
340
341 assert_eq!(info.id, "dev-001");
342 assert_eq!(info.protocol, Protocol::ModbusTcp);
343 assert_eq!(
344 info.metadata.get("location"),
345 Some(&"Building A".to_string())
346 );
347 }
348
349 #[test]
350 fn test_device_statistics() {
351 let mut stats = DeviceStatistics::default();
352 stats.record_read();
353 stats.record_tick(100);
354 stats.record_tick(200);
355
356 assert_eq!(stats.reads_total, 1);
357 assert_eq!(stats.ticks_total, 2);
358 assert_eq!(stats.avg_tick_duration_us, 150);
359 }
360}