1use anyhow::Result;
2use async_trait::async_trait;
3use macaddr::MacAddr;
4use measurements::{Power, Temperature};
5use reqwest::Method;
6use serde_json::Value;
7use std::collections::HashMap;
8use std::fmt::Debug;
9use std::net::IpAddr;
10use std::time::{Duration, SystemTime, UNIX_EPOCH};
11
12use crate::data::board::BoardData;
13use crate::data::device::DeviceInfo;
14use crate::data::fan::FanData;
15use crate::data::hashrate::{HashRate, HashRateUnit};
16use crate::data::message::MinerMessage;
17use crate::data::pool::PoolData;
18use crate::miners::commands::MinerCommand;
19
20use crate::data::miner::MinerData;
21use crate::miners::data::{DataCollector, DataField, DataLocation};
22
23#[async_trait]
25pub trait GetMinerData: CollectData {
26 async fn get_data(&self) -> MinerData;
29 fn parse_data(&self, data: HashMap<DataField, Value>) -> MinerData;
30}
31
32pub trait CollectData: GetDataLocations {
33 fn get_collector(&self) -> DataCollector<'_>;
38}
39
40pub trait GetDataLocations: Send + Sync + Debug {
41 fn get_locations(&self, data_field: DataField) -> Vec<DataLocation>;
46}
47
48#[async_trait]
49impl<
50 T: GetIP
51 + GetDeviceInfo
52 + GetExpectedHashboards
53 + GetExpectedChips
54 + GetExpectedFans
55 + GetMAC
56 + GetSerialNumber
57 + GetHostname
58 + GetApiVersion
59 + GetFirmwareVersion
60 + GetControlBoardVersion
61 + GetHashboards
62 + GetHashrate
63 + GetExpectedHashrate
64 + GetFans
65 + GetPsuFans
66 + GetFluidTemperature
67 + GetWattage
68 + GetWattageLimit
69 + GetLightFlashing
70 + GetMessages
71 + GetUptime
72 + GetIsMining
73 + GetPools,
74> GetMinerData for T
75{
76 async fn get_data(&self) -> MinerData {
77 let mut collector = self.get_collector();
78 let data = collector.collect_all().await;
79 self.parse_data(data)
80 }
81 fn parse_data(&self, data: HashMap<DataField, Value>) -> MinerData {
82 let schema_version = env!("CARGO_PKG_VERSION").to_string();
83 let timestamp = SystemTime::now()
84 .duration_since(UNIX_EPOCH)
85 .expect("Failed to get system time")
86 .as_secs();
87
88 let ip = self.get_ip();
89 let mac = self.parse_mac(&data);
90 let serial_number = self.parse_serial_number(&data);
91 let hostname = self.parse_hostname(&data);
92 let api_version = self.parse_api_version(&data);
93 let firmware_version = self.parse_firmware_version(&data);
94 let control_board_version = self.parse_control_board_version(&data);
95 let uptime = self.parse_uptime(&data);
96 let hashrate = self.parse_hashrate(&data);
97 let expected_hashrate = self.parse_expected_hashrate(&data);
98 let wattage = self.parse_wattage(&data);
99 let wattage_limit = self.parse_wattage_limit(&data);
100 let fluid_temperature = self.parse_fluid_temperature(&data);
101 let fans = self.parse_fans(&data);
102 let psu_fans = self.parse_psu_fans(&data);
103 let hashboards = self.parse_hashboards(&data);
104 let light_flashing = self.parse_light_flashing(&data);
105 let is_mining = self.parse_is_mining(&data);
106 let messages = self.parse_messages(&data);
107 let pools = self.parse_pools(&data);
108 let device_info = self.get_device_info();
109
110 let total_chips = hashboards.clone().iter().map(|b| b.working_chips).sum();
112 let average_temperature = {
113 let board_temps = hashboards
114 .iter()
115 .map(|b| b.board_temperature)
116 .filter(|x| x.is_some())
117 .map(|x| x.unwrap().as_celsius())
118 .collect::<Vec<f64>>();
119 if !board_temps.is_empty() {
120 Some(Temperature::from_celsius(
121 board_temps.iter().sum::<f64>() / hashboards.len() as f64,
122 ))
123 } else {
124 None
125 }
126 };
127 let efficiency = match (hashrate.as_ref(), wattage.as_ref()) {
128 (Some(hr), Some(w)) => {
129 let hashrate_th = hr.clone().as_unit(HashRateUnit::TeraHash).value;
130 Some(w.as_watts() / hashrate_th)
131 }
132 _ => None,
133 };
134
135 MinerData {
136 schema_version,
138 timestamp,
139
140 ip,
142 mac,
143
144 device_info,
146 serial_number,
147 hostname,
148
149 api_version,
151 firmware_version,
152 control_board_version,
153
154 expected_hashboards: device_info.hardware.boards,
156 hashboards,
157 hashrate,
158 expected_hashrate,
159
160 expected_chips: device_info.hardware.chips,
162 total_chips,
163
164 expected_fans: device_info.hardware.fans,
166 fans,
167 psu_fans,
168 average_temperature,
169 fluid_temperature,
170
171 wattage,
173 wattage_limit,
174 efficiency,
175
176 light_flashing,
178 messages,
179 uptime,
180 is_mining,
181
182 pools,
183 }
184 }
185}
186
187#[async_trait]
188pub trait APIClient: Send + Sync {
189 async fn get_api_result(&self, command: &MinerCommand) -> Result<Value>;
190}
191
192#[async_trait]
193pub trait WebAPIClient: Send + Sync + APIClient {
194 async fn send_command(
195 &self,
196 command: &str,
197 _privileged: bool,
198 parameters: Option<Value>,
199 method: Method,
200 ) -> Result<Value>;
201}
202
203#[async_trait]
204pub trait RPCAPIClient: Send + Sync + APIClient {
205 async fn send_command(
206 &self,
207 command: &str,
208 _privileged: bool,
209 parameters: Option<Value>,
210 ) -> Result<Value>;
211}
212pub trait GetIP: Send + Sync {
215 fn get_ip(&self) -> IpAddr;
217}
218
219pub trait GetDeviceInfo: Send + Sync {
220 fn get_device_info(&self) -> DeviceInfo;
222}
223
224trait GetExpectedHashboards: GetDeviceInfo {
225 #[allow(dead_code)]
226 fn get_expected_hashboards(&self) -> Option<u8> {
227 self.get_device_info().hardware.boards
228 }
229}
230impl<T: GetDeviceInfo> GetExpectedHashboards for T {}
231
232trait GetExpectedChips: GetDeviceInfo {
233 #[allow(dead_code)]
234 fn get_expected_chips(&self) -> Option<u16> {
235 self.get_device_info().hardware.chips
236 }
237}
238impl<T: GetDeviceInfo> GetExpectedChips for T {}
239
240trait GetExpectedFans: GetDeviceInfo {
241 #[allow(dead_code)]
242 fn get_expected_fans(&self) -> Option<u8> {
243 self.get_device_info().hardware.fans
244 }
245}
246impl<T: GetDeviceInfo> GetExpectedFans for T {}
247
248#[async_trait]
250pub trait GetMAC: CollectData {
251 async fn get_mac(&self) -> Option<MacAddr> {
252 let mut collector = self.get_collector();
253 let data = collector.collect(&[DataField::Mac]).await;
254 self.parse_mac(&data)
255 }
256 #[allow(unused_variables)]
257 fn parse_mac(&self, data: &HashMap<DataField, Value>) -> Option<MacAddr> {
258 None
259 }
260}
261
262#[async_trait]
264pub trait GetSerialNumber: CollectData {
265 async fn get_serial_number(&self) -> Option<String> {
266 let mut collector = self.get_collector();
267 let data = collector.collect(&[DataField::SerialNumber]).await;
268 self.parse_serial_number(&data)
269 }
270 #[allow(unused_variables)]
271 fn parse_serial_number(&self, data: &HashMap<DataField, Value>) -> Option<String> {
272 None
273 }
274}
275
276#[async_trait]
278pub trait GetHostname: CollectData {
279 async fn get_hostname(&self) -> Option<String> {
280 let mut collector = self.get_collector();
281 let data = collector.collect(&[DataField::Hostname]).await;
282 self.parse_hostname(&data)
283 }
284 #[allow(unused_variables)]
285 fn parse_hostname(&self, data: &HashMap<DataField, Value>) -> Option<String> {
286 None
287 }
288}
289
290#[async_trait]
292pub trait GetApiVersion: CollectData {
293 async fn get_api_version(&self) -> Option<String> {
294 let mut collector = self.get_collector();
295 let data = collector.collect(&[DataField::ApiVersion]).await;
296 self.parse_api_version(&data)
297 }
298 #[allow(unused_variables)]
299 fn parse_api_version(&self, data: &HashMap<DataField, Value>) -> Option<String> {
300 None
301 }
302}
303
304#[async_trait]
306pub trait GetFirmwareVersion: CollectData {
307 async fn get_firmware_version(&self) -> Option<String> {
308 let mut collector = self.get_collector();
309 let data = collector.collect(&[DataField::FirmwareVersion]).await;
310 self.parse_firmware_version(&data)
311 }
312 #[allow(unused_variables)]
313 fn parse_firmware_version(&self, data: &HashMap<DataField, Value>) -> Option<String> {
314 None
315 }
316}
317
318#[async_trait]
320pub trait GetControlBoardVersion: CollectData {
321 async fn get_control_board_version(&self) -> Option<String> {
322 let mut collector = self.get_collector();
323 let data = collector.collect(&[DataField::ControlBoardVersion]).await;
324 self.parse_control_board_version(&data)
325 }
326 #[allow(unused_variables)]
327 fn parse_control_board_version(&self, data: &HashMap<DataField, Value>) -> Option<String> {
328 None
329 }
330}
331#[async_trait]
333pub trait GetHashboards: CollectData {
334 async fn get_hashboards(&self) -> Vec<BoardData> {
335 let mut collector = self.get_collector();
336 let data = collector.collect(&[DataField::Hashboards]).await;
337 self.parse_hashboards(&data)
338 }
339 #[allow(unused_variables)]
340 fn parse_hashboards(&self, data: &HashMap<DataField, Value>) -> Vec<BoardData> {
341 vec![]
342 }
343}
344
345#[async_trait]
347pub trait GetHashrate: CollectData {
348 async fn get_hashrate(&self) -> Option<HashRate> {
349 let mut collector = self.get_collector();
350 let data = collector.collect(&[DataField::Hashrate]).await;
351 self.parse_hashrate(&data)
352 }
353 #[allow(unused_variables)]
354 fn parse_hashrate(&self, data: &HashMap<DataField, Value>) -> Option<HashRate> {
355 None
356 }
357}
358
359#[async_trait]
361pub trait GetExpectedHashrate: CollectData {
362 async fn get_expected_hashrate(&self) -> Option<HashRate> {
363 let mut collector = self.get_collector();
364 let data = collector.collect(&[DataField::ExpectedHashrate]).await;
365 self.parse_expected_hashrate(&data)
366 }
367 #[allow(unused_variables)]
368 fn parse_expected_hashrate(&self, data: &HashMap<DataField, Value>) -> Option<HashRate> {
369 None
370 }
371}
372
373#[async_trait]
375pub trait GetFans: CollectData {
376 async fn get_fans(&self) -> Vec<FanData> {
377 let mut collector = self.get_collector();
378 let data = collector.collect(&[DataField::Fans]).await;
379 self.parse_fans(&data)
380 }
381 #[allow(unused_variables)]
382 fn parse_fans(&self, data: &HashMap<DataField, Value>) -> Vec<FanData> {
383 vec![]
384 }
385}
386
387#[async_trait]
389pub trait GetPsuFans: CollectData {
390 async fn get_psu_fans(&self) -> Vec<FanData> {
391 let mut collector = self.get_collector();
392 let data = collector.collect(&[DataField::PsuFans]).await;
393 self.parse_psu_fans(&data)
394 }
395 #[allow(unused_variables)]
396 fn parse_psu_fans(&self, data: &HashMap<DataField, Value>) -> Vec<FanData> {
397 vec![]
398 }
399}
400
401#[async_trait]
403pub trait GetFluidTemperature: CollectData {
404 async fn get_fluid_temperature(&self) -> Option<Temperature> {
405 let mut collector = self.get_collector();
406 let data = collector.collect(&[DataField::FluidTemperature]).await;
407 self.parse_fluid_temperature(&data)
408 }
409 #[allow(unused_variables)]
410 fn parse_fluid_temperature(&self, data: &HashMap<DataField, Value>) -> Option<Temperature> {
411 None
412 }
413}
414
415#[async_trait]
417pub trait GetWattage: CollectData {
418 async fn get_wattage(&self) -> Option<Power> {
419 let mut collector = self.get_collector();
420 let data = collector.collect(&[DataField::Wattage]).await;
421 self.parse_wattage(&data)
422 }
423 #[allow(unused_variables)]
424 fn parse_wattage(&self, data: &HashMap<DataField, Value>) -> Option<Power> {
425 None
426 }
427}
428
429#[async_trait]
431pub trait GetWattageLimit: CollectData {
432 async fn get_wattage_limit(&self) -> Option<Power> {
433 let mut collector = self.get_collector();
434 let data = collector.collect(&[DataField::WattageLimit]).await;
435 self.parse_wattage_limit(&data)
436 }
437 #[allow(unused_variables)]
438 fn parse_wattage_limit(&self, data: &HashMap<DataField, Value>) -> Option<Power> {
439 None
440 }
441}
442
443#[async_trait]
445pub trait GetLightFlashing: CollectData {
446 async fn get_light_flashing(&self) -> Option<bool> {
447 let mut collector = self.get_collector();
448 let data = collector.collect(&[DataField::LightFlashing]).await;
449 self.parse_light_flashing(&data)
450 }
451 #[allow(unused_variables)]
452 fn parse_light_flashing(&self, data: &HashMap<DataField, Value>) -> Option<bool> {
453 None
454 }
455}
456
457#[async_trait]
459pub trait SetFaultLight {
460 async fn set_fault_light(&self, fault: bool) -> Result<bool>;
461}
462#[async_trait]
463pub trait SetPowerLimit {
464 async fn set_power_limit(&self, limit: Power) -> Result<bool>;
465}
466
467#[async_trait]
468pub trait Restart {
469 async fn restart(&self) -> Result<bool>;
470}
471
472#[async_trait]
473pub trait Pause {
474 async fn pause(&self, at_time: Option<Duration>) -> Result<bool>;
475}
476
477#[async_trait]
478pub trait Resume {
479 async fn resume(&self, at_time: Option<Duration>) -> Result<bool>;
480}
481
482#[async_trait]
484pub trait GetMessages: CollectData {
485 async fn get_messages(&self) -> Vec<MinerMessage> {
486 let mut collector = self.get_collector();
487 let data = collector.collect(&[DataField::Messages]).await;
488 self.parse_messages(&data)
489 }
490 #[allow(unused_variables)]
491 fn parse_messages(&self, data: &HashMap<DataField, Value>) -> Vec<MinerMessage> {
492 vec![]
493 }
494}
495
496#[async_trait]
498pub trait GetUptime: CollectData {
499 async fn get_uptime(&self) -> Option<Duration> {
500 let mut collector = self.get_collector();
501 let data = collector.collect(&[DataField::Uptime]).await;
502 self.parse_uptime(&data)
503 }
504 #[allow(unused_variables)]
505 fn parse_uptime(&self, data: &HashMap<DataField, Value>) -> Option<Duration> {
506 None
507 }
508}
509
510#[async_trait]
512pub trait GetIsMining: CollectData {
513 async fn get_is_mining(&self) -> bool {
514 let mut collector = self.get_collector();
515 let data = collector.collect(&[DataField::IsMining]).await;
516 self.parse_is_mining(&data)
517 }
518 #[allow(unused_variables)]
519 fn parse_is_mining(&self, data: &HashMap<DataField, Value>) -> bool {
520 true
521 }
522}
523
524#[async_trait]
526pub trait GetPools: CollectData {
527 async fn get_pools(&self) -> Vec<PoolData> {
528 let mut collector = self.get_collector();
529 let data = collector.collect(&[DataField::Pools]).await;
530 self.parse_pools(&data)
531 }
532 #[allow(unused_variables)]
533 fn parse_pools(&self, data: &HashMap<DataField, Value>) -> Vec<PoolData> {
534 vec![]
535 }
536}