1use serde_json::json;
3use tokio_tungstenite::tungstenite::protocol::Message;
4
5use crate::error::{validate_pin, ObnizError, ObnizResult};
6use crate::obniz::Obniz;
7
8#[derive(Debug, Clone)]
10pub struct AdConfig {
11 pub stream: bool,
12}
13
14#[derive(Debug, Clone, PartialEq)]
16pub struct AdValue {
17 pub channel: u8,
18 pub voltage: f64,
19}
20
21#[derive(Debug)]
23pub struct AdChannel {
24 channel: u8,
25 obniz: Obniz,
26}
27
28impl AdChannel {
29 pub fn new(channel: u8, obniz: Obniz) -> Self {
30 Self { channel, obniz }
31 }
32
33 pub fn channel_key(&self) -> String {
34 format!("ad{}", self.channel)
35 }
36
37 pub async fn get(&self) -> ObnizResult<f64> {
39 validate_pin(self.channel)?;
40
41 let channel_key = self.channel_key();
42 let request = json!([{&channel_key: "get"}]);
43 let message = Message::from(request.to_string());
44
45 let response = self
46 .obniz
47 .send_await_response(message, channel_key.clone())
48 .await
49 .map_err(|e| ObnizError::Connection(e.to_string()))?;
50
51 if let Some(array) = response.as_array() {
54 if let Some(first_item) = array.first() {
55 if let Some(value) = first_item.get(&channel_key) {
56 return value.as_f64().ok_or_else(|| {
57 ObnizError::Generic("Invalid voltage value in response".to_string())
58 });
59 }
60 }
61 }
62
63 if let Some(value) = response.get(&channel_key) {
65 value
66 .as_f64()
67 .ok_or_else(|| ObnizError::Generic("Invalid voltage value in response".to_string()))
68 } else {
69 Err(ObnizError::Generic(format!(
70 "No response for AD channel {}",
71 self.channel
72 )))
73 }
74 }
75
76 pub async fn configure(&self, config: AdConfig) -> ObnizResult<()> {
78 validate_pin(self.channel)?;
79
80 let channel_key = self.channel_key();
81 let request = json!([{&channel_key: {"stream": config.stream}}]);
82 let message = Message::from(request.to_string());
83
84 self.obniz
85 .send_message(message)
86 .map_err(|e| ObnizError::Connection(e.to_string()))
87 }
88
89 pub async fn enable_stream(&self) -> ObnizResult<()> {
91 self.configure(AdConfig { stream: true }).await
92 }
93
94 pub async fn disable_stream(&self) -> ObnizResult<()> {
96 self.configure(AdConfig { stream: false }).await
97 }
98
99 pub async fn on_change<F>(&self, callback: F) -> ObnizResult<()>
101 where
102 F: Fn(f64) + Send + Sync + 'static,
103 {
104 validate_pin(self.channel)?;
105
106 self.enable_stream().await?;
108
109 let channel_key = self.channel_key();
110 let channel_key_clone = channel_key.clone();
111
112 self.obniz
113 .register_callback(channel_key, move |response| {
114 if let Some(value) = response.get(&channel_key_clone) {
115 if let Some(voltage) = value.as_f64() {
116 callback(voltage);
117 }
118 }
119 })
120 .map_err(|e| ObnizError::CallbackError(e.to_string()))?;
121
122 Ok(())
123 }
124
125 pub fn remove_callback(&self) -> ObnizResult<()> {
127 validate_pin(self.channel)?;
128 let channel_key = self.channel_key();
129 self.obniz
130 .unregister_callback(channel_key)
131 .map_err(|e| ObnizError::CallbackError(e.to_string()))
132 }
133
134 pub async fn deinit(&self) -> ObnizResult<()> {
136 validate_pin(self.channel)?;
137
138 let channel_key = self.channel_key();
139 let request = json!([{&channel_key: null}]);
140 let message = Message::from(request.to_string());
141
142 self.obniz
143 .send_message(message)
144 .map_err(|e| ObnizError::Connection(e.to_string()))
145 }
146}
147
148#[derive(Debug, Clone)]
150pub struct AdManager {
151 obniz: Obniz,
152}
153
154impl AdManager {
155 pub fn new(obniz: Obniz) -> Self {
156 Self { obniz }
157 }
158
159 pub fn channel(&self, channel: u8) -> ObnizResult<AdChannel> {
161 validate_pin(channel)?;
162 Ok(AdChannel::new(channel, self.obniz.clone()))
163 }
164
165 pub async fn get_voltage(&self, channel: u8) -> ObnizResult<f64> {
167 self.channel(channel)?.get().await
168 }
169
170 pub async fn get_voltages(&self, channels: Vec<u8>) -> ObnizResult<Vec<AdValue>> {
172 let mut results = Vec::new();
173
174 for channel in channels {
175 let voltage = self.get_voltage(channel).await?;
176 results.push(AdValue { channel, voltage });
177 }
178
179 Ok(results)
180 }
181
182 pub async fn enable_channel_stream(&self, channel: u8) -> ObnizResult<()> {
184 self.channel(channel)?.enable_stream().await
185 }
186
187 pub async fn disable_channel_stream(&self, channel: u8) -> ObnizResult<()> {
189 self.channel(channel)?.disable_stream().await
190 }
191
192 pub async fn set_channel_callback<F>(&self, channel: u8, callback: F) -> ObnizResult<()>
194 where
195 F: Fn(f64) + Send + Sync + 'static,
196 {
197 self.channel(channel)?.on_change(callback).await
198 }
199
200 pub fn remove_channel_callback(&self, channel: u8) -> ObnizResult<()> {
202 self.channel(channel)?.remove_callback()
203 }
204
205 pub async fn deinit_channel(&self, channel: u8) -> ObnizResult<()> {
207 self.channel(channel)?.deinit().await
208 }
209
210 pub async fn deinit_all(&self) -> ObnizResult<()> {
212 for channel in 0..=11 {
213 if let Err(e) = self.deinit_channel(channel).await {
214 eprintln!("Failed to deinitialize AD channel {channel}: {e}");
215 }
216 }
217 Ok(())
218 }
219
220 pub async fn read_all(&self) -> ObnizResult<Vec<AdValue>> {
222 let channels: Vec<u8> = (0..=11).collect();
223 self.get_voltages(channels).await
224 }
225
226 pub fn voltage_to_percentage(voltage: f64) -> f64 {
228 (voltage / 5.0 * 100.0).clamp(0.0, 100.0)
229 }
230
231 pub fn is_voltage_safe(voltage: f64) -> bool {
233 (0.0..=5.0).contains(&voltage)
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240
241 #[test]
242 fn test_ad_value_creation() {
243 let value = AdValue {
244 channel: 5,
245 voltage: 3.3,
246 };
247
248 assert_eq!(value.channel, 5);
249 assert_eq!(value.voltage, 3.3);
250 }
251
252 #[test]
253 fn test_ad_config_creation() {
254 let config = AdConfig { stream: true };
255 assert!(config.stream);
256
257 let config = AdConfig { stream: false };
258 assert!(!config.stream);
259 }
260
261 #[test]
262 fn test_voltage_to_percentage() {
263 assert_eq!(AdManager::voltage_to_percentage(0.0), 0.0);
264 assert_eq!(AdManager::voltage_to_percentage(2.5), 50.0);
265 assert_eq!(AdManager::voltage_to_percentage(5.0), 100.0);
266 assert_eq!(AdManager::voltage_to_percentage(6.0), 100.0); }
268
269 #[test]
270 fn test_voltage_safety_check() {
271 assert!(AdManager::is_voltage_safe(0.0));
272 assert!(AdManager::is_voltage_safe(3.3));
273 assert!(AdManager::is_voltage_safe(5.0));
274 assert!(!AdManager::is_voltage_safe(-0.1));
275 assert!(!AdManager::is_voltage_safe(5.1));
276 }
277
278 #[test]
279 fn test_channel_key_generation() {
280 assert_eq!(format!("ad{}", 0), "ad0");
283 assert_eq!(format!("ad{}", 11), "ad11");
284 }
285}