use super::*;
use crate::pv;
use crate::pv::link::InvalidSlotNumber;
use crate::pv::physical::RSSI;
use chrono::{DateTime, Local};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(rename = "snake_case")]
pub enum Event {
PowerReport(PowerReportEvent),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Gateway {
pub id: gateway::link::GatewayID,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub address: Option<pv::LongAddress>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Node {
pub id: pv::NodeID,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub address: Option<pv::LongAddress>,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct PowerReportEvent {
pub gateway: Gateway,
pub node: Node,
pub timestamp: DateTime<Local>,
pub voltage_in: f64,
pub voltage_out: f64,
pub current: f64,
pub dc_dc_duty_cycle: f64,
pub temperature: f64,
pub rssi: RSSI,
}
impl PowerReportEvent {
pub fn new(
gateway: Gateway,
node: Node,
slot_clock: &SlotClock,
report: &pv::application::PowerReport,
) -> Result<Self, InvalidSlotNumber> {
let timestamp = slot_clock.get(report.slot_counter)?;
let (voltage_in, voltage_out) = report.voltage_in_and_voltage_out.into();
let (current, temperature) = report.current_and_temperature.into();
let temperature = if temperature & 0x800 == 0 {
temperature
} else {
temperature | 0xF000
} as i16;
Ok(Self {
gateway,
node,
timestamp: timestamp.into(),
voltage_in: voltage_in as f64 / 20.0, voltage_out: voltage_out as f64 / 10.0, dc_dc_duty_cycle: report.dc_dc_duty_cycle as f64 / 255.0,
current: current as f64 / 200.0, temperature: temperature as f64 / 10.0, rssi: report.rssi,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pv::application::{PowerReport, U12Pair};
#[test]
fn negative_temperature() {
let gateway = Gateway {
id: 1.try_into().unwrap(),
address: None,
};
let node = Node {
id: 1.try_into().unwrap(),
address: None,
};
let rssi = RSSI(100);
let timestamp = SystemTime::now();
let slot_counter = SlotCounter::from(0);
let slot_clock = SlotClock::new(slot_counter, timestamp).unwrap();
let power_report = PowerReport {
voltage_in_and_voltage_out: U12Pair::try_from((500, 250)).unwrap(),
dc_dc_duty_cycle: 255,
current_and_temperature: U12Pair::try_from((200, 0xfff)).unwrap(),
unknown: [0, 0, 0],
slot_counter,
rssi,
};
let power_report_event =
PowerReportEvent::new(gateway, node, &slot_clock, &power_report).unwrap();
let actual = serde_json::to_string(&power_report_event).unwrap();
let expected = serde_json::to_string(&PowerReportEvent {
gateway,
node,
timestamp: timestamp.into(),
voltage_in: 25.0,
voltage_out: 25.0,
current: 1.00,
dc_dc_duty_cycle: 1.0,
temperature: -0.1,
rssi,
})
.unwrap();
assert_eq!(actual, expected); }
}