diff --git a/Cargo.lock b/Cargo.lock index 3b32fa8..c769091 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,9 +180,9 @@ dependencies = [ [[package]] name = "chirpstack_api" -version = "4.9.0-test.1" +version = "4.9.0-test.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef50294bd90c389f76dcce5b7214adf48b92feef5b4b231d17d4fa3589fb8951" +checksum = "e47ab9c1f13fef2a591b32e81a34aead5395f4d2c983de01d76bc278d8e39795" dependencies = [ "hex", "pbjson-build", diff --git a/Cargo.toml b/Cargo.toml index 762a6bb..79e0cf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ "usage", "derive", ] } - chirpstack_api = { version = "4.9.0-test.1", default-features = false } + chirpstack_api = { version = "4.9.0-test.2", default-features = false } lrwn_filters = { version = "4.7", features = ["serde"] } log = "0.4" simple_logger = "5.0" diff --git a/src/mesh.rs b/src/mesh.rs index ce9dec7..659aa05 100644 --- a/src/mesh.rs +++ b/src/mesh.rs @@ -166,19 +166,31 @@ async fn proxy_heartbeat_mesh_packet(pl: &gw::UplinkFrame, packet: MeshPacket) - packet ); - let heartbeat_pl = gw::MeshStats { + let heartbeat_pl = gw::MeshHeartbeat { gateway_id: hex::encode(backend::get_gateway_id().await?), relay_id: hex::encode(mesh_pl.relay_id), - relay_path: mesh_pl.relay_path.iter().map(hex::encode).collect(), + relay_path: mesh_pl + .relay_path + .iter() + .map(|v| gw::MeshHeartbeatRelayPath { + relay_id: hex::encode(&v.relay_id), + rssi: v.rssi.into(), + snr: v.snr.into(), + }) + .collect(), time: Some(mesh_pl.timestamp.into()), }; proxy::send_mesh_heartbeat(&heartbeat_pl).await } -async fn relay_mesh_packet(_: &gw::UplinkFrame, mut packet: MeshPacket) -> Result<()> { +async fn relay_mesh_packet(pl: &gw::UplinkFrame, mut packet: MeshPacket) -> Result<()> { let conf = config::get(); let relay_id = backend::get_relay_id().await?; + let rx_info = pl + .rx_info + .as_ref() + .ok_or_else(|| anyhow!("rx_info is None"))?; match &mut packet.payload { packets::Payload::Uplink(pl) => { @@ -237,7 +249,11 @@ async fn relay_mesh_packet(_: &gw::UplinkFrame, mut packet: MeshPacket) -> Resul } // Add our Relay ID to the path. - pl.relay_path.push(relay_id); + pl.relay_path.push(packets::RelayPath { + relay_id, + rssi: rx_info.rssi as i16, + snr: rx_info.snr as i8, + }); } } diff --git a/src/packets.rs b/src/packets.rs index 2c57a9c..43002e4 100644 --- a/src/packets.rs +++ b/src/packets.rs @@ -420,7 +420,7 @@ impl DownlinkMetadata { pub struct HeartbeatPayload { pub timestamp: SystemTime, pub relay_id: [u8; 4], - pub relay_path: Vec<[u8; 4]>, + pub relay_path: Vec, } impl HeartbeatPayload { @@ -429,8 +429,8 @@ impl HeartbeatPayload { return Err(anyhow!("At least 8 bytes are expected")); } - if (b.len() - 8) % 4 != 0 { - return Err(anyhow!("Invalid amoutn of Relay path bytes")); + if (b.len() - 8) % 6 != 0 { + return Err(anyhow!("Invalid amount of Relay path bytes")); } let mut ts_b: [u8; 4] = [0; 4]; @@ -443,12 +443,12 @@ impl HeartbeatPayload { let mut relay_id: [u8; 4] = [0; 4]; relay_id.copy_from_slice(&b[4..8]); - let relay_path: Vec<[u8; 4]> = b[8..] - .chunks(4) + let relay_path: Vec = b[8..] + .chunks(6) .map(|v| { - let mut relay_id: [u8; 4] = [0; 4]; - relay_id.copy_from_slice(v); - relay_id + let mut b: [u8; 6] = [0; 6]; + b.copy_from_slice(v); + RelayPath::from_bytes(b) }) .collect(); @@ -464,12 +464,67 @@ impl HeartbeatPayload { let mut b = timestamp.to_be_bytes().to_vec(); b.extend_from_slice(&self.relay_id); for relay_path in &self.relay_path { - b.extend_from_slice(relay_path); + b.extend_from_slice(&relay_path.to_bytes()?); } Ok(b) } } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct RelayPath { + pub relay_id: [u8; 4], + pub rssi: i16, + pub snr: i8, +} + +impl RelayPath { + pub fn from_bytes(b: [u8; 6]) -> Self { + let mut relay_id = [0; 4]; + relay_id.copy_from_slice(&b[0..4]); + + let snr = b[5] & 0x3f; + let snr = if snr > 31 { + (snr as i8) - 64 + } else { + snr as i8 + }; + + RelayPath { + relay_id, + snr, + rssi: -(b[4] as i16), + } + } + + pub fn to_bytes(&self) -> Result<[u8; 6]> { + if self.rssi > 0 { + return Err(anyhow!("Max rssi value is 0")); + } + if self.rssi < -255 { + return Err(anyhow!("Min rssi value is -255")); + } + if self.snr < -32 { + return Err(anyhow!("Min snr value is -32")); + } + if self.snr > 31 { + return Err(anyhow!("Max snr value is 31")); + } + + Ok([ + self.relay_id[0], + self.relay_id[1], + self.relay_id[2], + self.relay_id[3], + -self.rssi as u8, + if self.snr < 0 { + (self.snr + 64) as u8 + } else { + self.snr as u8 + }, + ]) + } +} + pub fn encode_freq(freq: u32) -> Result<[u8; 3]> { let mut freq = freq; // Support LoRaWAN 2.4GHz, in which case the stepping is 200Hz: @@ -964,7 +1019,9 @@ mod test { #[test] fn test_heartbeat_payload_from_slice() { - let b = vec![59, 154, 202, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + let b = vec![ + 59, 154, 202, 0, 1, 2, 3, 4, 5, 6, 7, 8, 120, 52, 9, 10, 11, 12, 120, 52, + ]; let heartbeat_pl = HeartbeatPayload::from_slice(&b).unwrap(); assert_eq!( HeartbeatPayload { @@ -972,7 +1029,18 @@ mod test { .checked_add(Duration::from_secs(1_000_000_000)) .unwrap(), relay_id: [1, 2, 3, 4], - relay_path: vec![[5, 6, 7, 8], [9, 10, 11, 12]], + relay_path: vec![ + RelayPath { + relay_id: [5, 6, 7, 8], + rssi: -120, + snr: -12, + }, + RelayPath { + relay_id: [9, 10, 11, 12], + rssi: -120, + snr: -12, + }, + ], }, heartbeat_pl, ); @@ -985,11 +1053,22 @@ mod test { .checked_add(Duration::from_secs(1_000_000_000)) .unwrap(), relay_id: [1, 2, 3, 4], - relay_path: vec![[5, 6, 7, 8], [9, 10, 11, 12]], + relay_path: vec![ + RelayPath { + relay_id: [5, 6, 7, 8], + rssi: -120, + snr: -12, + }, + RelayPath { + relay_id: [9, 10, 11, 12], + rssi: -120, + snr: -12, + }, + ], }; let b = heartbeat_pl.to_vec().unwrap(); assert_eq!( - vec![59, 154, 202, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + vec![59, 154, 202, 0, 1, 2, 3, 4, 5, 6, 7, 8, 120, 52, 9, 10, 11, 12, 120, 52], b ); } diff --git a/src/proxy.rs b/src/proxy.rs index 2db8ba5..580459b 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -126,7 +126,7 @@ pub async fn send_stats(pl: &gw::GatewayStats) -> Result<()> { Ok(()) } -pub async fn send_mesh_heartbeat(pl: &gw::MeshStats) -> Result<()> { +pub async fn send_mesh_heartbeat(pl: &gw::MeshHeartbeat) -> Result<()> { info!("Sending mesh heartbeat event"); let event_chan = EVENT_CHAN diff --git a/tests/border_gateway_mesh_heartbeat.rs b/tests/border_gateway_mesh_heartbeat.rs index ce00738..58daa2e 100644 --- a/tests/border_gateway_mesh_heartbeat.rs +++ b/tests/border_gateway_mesh_heartbeat.rs @@ -28,7 +28,18 @@ async fn test_border_gateway_mesh_heartbeat() { payload: packets::Payload::Heartbeat(packets::HeartbeatPayload { relay_id: [2, 2, 2, 2], timestamp: UNIX_EPOCH, - relay_path: vec![[1, 2, 3, 4], [5, 6, 7, 8]], + relay_path: vec![ + packets::RelayPath { + relay_id: [1, 2, 3, 4], + rssi: -120, + snr: -12, + }, + packets::RelayPath { + relay_id: [5, 6, 7, 8], + rssi: -120, + snr: -12, + }, + ], }), mic: None, }; @@ -70,24 +81,35 @@ async fn test_border_gateway_mesh_heartbeat() { .unwrap(); } - // We expect to receive the MeshStats to be received by the forwarder. - let mesh_stats: gw::MeshStats = { + // We expect to receive the MeshHeartbeat to be received by the forwarder. + let mesh_heartbeat: gw::MeshHeartbeat = { let mut event_sock = common::FORWARDER_EVENT_SOCK.get().unwrap().lock().await; let msg = event_sock.recv().await.unwrap(); let cmd = String::from_utf8(msg.get(0).map(|v| v.to_vec()).unwrap()).unwrap(); assert_eq!("mesh_heartbeat", cmd); - gw::MeshStats::decode(msg.get(1).cloned().unwrap()).unwrap() + gw::MeshHeartbeat::decode(msg.get(1).cloned().unwrap()).unwrap() }; assert_eq!( - gw::MeshStats { + gw::MeshHeartbeat { gateway_id: "0101010101010101".to_string(), time: Some(UNIX_EPOCH.into()), relay_id: "02020202".to_string(), - relay_path: vec!["01020304".to_string(), "05060708".to_string(),], + relay_path: vec![ + gw::MeshHeartbeatRelayPath { + relay_id: "01020304".into(), + rssi: -120, + snr: -12, + }, + gw::MeshHeartbeatRelayPath { + relay_id: "05060708".into(), + rssi: -120, + snr: -12, + }, + ], }, - mesh_stats + mesh_heartbeat ); } diff --git a/tests/relay_gateway_relay_mesh_heartbeat.rs b/tests/relay_gateway_relay_mesh_heartbeat.rs index 32cb19b..8fe9cfe 100644 --- a/tests/relay_gateway_relay_mesh_heartbeat.rs +++ b/tests/relay_gateway_relay_mesh_heartbeat.rs @@ -95,7 +95,11 @@ async fn test_relay_gateway_relay_mesh_heartbeat() { packet.mhdr.hop_count += 1; if let packets::Payload::Heartbeat(v) = &mut packet.payload { - v.relay_path.push([2, 2, 2, 2]); + v.relay_path.push(packets::RelayPath { + relay_id: [2, 2, 2, 2], + rssi: -60, + snr: 12, + }); } packet.set_mic(Aes128Key::null()).unwrap();