Skip to content

Commit

Permalink
Add deployment_info to gateway_service
Browse files Browse the repository at this point in the history
  • Loading branch information
kurotych committed Nov 19, 2024
1 parent 95797fc commit 28743dd
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 24 deletions.
190 changes: 169 additions & 21 deletions mobile_config/src/gateway_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,98 @@ use chrono::{DateTime, TimeZone, Utc};
use futures::stream::BoxStream;
use helium_crypto::PublicKeyBinary;
use helium_proto::services::mobile_config::{
DeviceType as DeviceTypeProto, GatewayInfo as GatewayInfoProto,
GatewayMetadata as GatewayMetadataProto,
gateway_metadata::DeploymentInfo as DeploymentInfoProto,
CbrsDeploymentInfo as CbrsDeploymentInfoProto, DeviceType as DeviceTypeProto,
GatewayInfo as GatewayInfoProto, GatewayMetadata as GatewayMetadataProto,
RadioDeploymentInfo as RadioDeploymentInfoProto, WifiDeploymentInfo as WifiDeploymentInfoProto,
};
use serde::Deserialize;

pub type GatewayInfoStream = BoxStream<'static, GatewayInfo>;

#[derive(Clone, Debug, Deserialize)]
pub struct WifiDeploymentInfo {
/// Antenna ID
pub antenna: u32,
/// The height of the hotspot above ground level in whole meters
pub elevation: u32,
pub azimuth: u32,
#[serde(rename = "mechanicalDownTilt")]
pub mechanical_down_tilt: u32,
#[serde(rename = "electricalDownTilt")]
pub electrical_down_tilt: u32,
}
impl From<WifiDeploymentInfoProto> for WifiDeploymentInfo {
fn from(v: WifiDeploymentInfoProto) -> Self {
Self {
antenna: v.antenna,
elevation: v.elevation,
azimuth: v.azimuth,
mechanical_down_tilt: v.mechanical_down_tilt,
electrical_down_tilt: v.electrical_down_tilt,
}
}
}

#[derive(Clone, Debug, Deserialize)]
pub struct CbrsDeploymentInfo {
pub radio_deployment_info: Vec<RadioDeploymentInfo>,
}

impl From<CbrsDeploymentInfoProto> for CbrsDeploymentInfo {
fn from(v: CbrsDeploymentInfoProto) -> Self {
Self {
radio_deployment_info: v
.radio_deployment_info
.into_iter()
.map(|v| v.into())
.collect(),
}
}
}

impl From<RadioDeploymentInfoProto> for RadioDeploymentInfo {
fn from(v: RadioDeploymentInfoProto) -> Self {
Self {
radio_id: v.radio_id,
elevation: v.elevation,
}
}
}

#[derive(Clone, Debug, Deserialize)]
pub struct RadioDeploymentInfo {
/// CBSD_ID or radio
pub radio_id: String,
/// The asserted elevation of the gateway above ground level in whole meters
pub elevation: u32,
}

#[derive(Clone, Debug, Deserialize)]
pub enum DeploymentInfo {
#[serde(rename = "wifiInfoV0")]
WifiDeploymentInfo(WifiDeploymentInfo),
#[serde(rename = "cbrsInfoV0")]
CbrsDeploymentInfo(CbrsDeploymentInfo),
}

impl From<DeploymentInfoProto> for DeploymentInfo {
fn from(v: DeploymentInfoProto) -> Self {
match v {
DeploymentInfoProto::WifiDeploymentInfo(v) => {
DeploymentInfo::WifiDeploymentInfo(v.into())
}
DeploymentInfoProto::CbrsDeploymentInfo(v) => {
DeploymentInfo::CbrsDeploymentInfo(v.into())
}
}
}
}

#[derive(Clone, Debug)]
pub struct GatewayMetadata {
pub location: u64,
pub deployment_info: Option<DeploymentInfo>,
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -42,48 +125,100 @@ impl TryFrom<GatewayInfoProto> for GatewayInfo {
type Error = GatewayInfoProtoParseError;

fn try_from(info: GatewayInfoProto) -> Result<Self, Self::Error> {
let metadata = if let Some(ref metadata) = info.metadata {
let device_type_ = info.device_type().into();

let GatewayInfoProto {
address,
metadata,
device_type: _,
created_at,
refreshed_at,
} = info;

let metadata = if let Some(metadata) = metadata {
Some(
u64::from_str_radix(&metadata.location, 16)
.map(|location| GatewayMetadata { location })?,
u64::from_str_radix(&metadata.location, 16).map(|location| GatewayMetadata {
location,
deployment_info: metadata.deployment_info.map(|v| v.into()),
})?,
)
} else {
None
};
let device_type = info.device_type().into();

let created_at = Utc
.timestamp_opt(info.created_at as i64, 0)
.timestamp_opt(created_at as i64, 0)
.single()
.ok_or(GatewayInfoProtoParseError::InvalidCreatedAt(
info.created_at,
))?;
.ok_or(GatewayInfoProtoParseError::InvalidCreatedAt(created_at))?;

let refreshed_at = Utc
.timestamp_opt(info.refreshed_at as i64, 0)
.single()
.ok_or(GatewayInfoProtoParseError::InvalidRefreshedAt(
info.refreshed_at,
))?;
let refreshed_at = Utc.timestamp_opt(refreshed_at as i64, 0).single().ok_or(
GatewayInfoProtoParseError::InvalidRefreshedAt(info.refreshed_at),
)?;

Ok(Self {
address: info.address.into(),
address: address.into(),
metadata,
device_type,
device_type: device_type_,
created_at,
refreshed_at,
})
}
}

impl From<WifiDeploymentInfo> for WifiDeploymentInfoProto {
fn from(v: WifiDeploymentInfo) -> Self {
Self {
antenna: v.antenna,
elevation: v.elevation,
azimuth: v.azimuth,
mechanical_down_tilt: v.mechanical_down_tilt,
electrical_down_tilt: v.electrical_down_tilt,
}
}
}

impl From<RadioDeploymentInfo> for RadioDeploymentInfoProto {
fn from(v: RadioDeploymentInfo) -> Self {
Self {
radio_id: v.radio_id,
elevation: v.elevation,
}
}
}

impl From<CbrsDeploymentInfo> for CbrsDeploymentInfoProto {
fn from(v: CbrsDeploymentInfo) -> Self {
Self {
radio_deployment_info: v
.radio_deployment_info
.into_iter()
.map(|v| v.into())
.collect(),
}
}
}

impl From<DeploymentInfo> for DeploymentInfoProto {
fn from(v: DeploymentInfo) -> Self {
match v {
DeploymentInfo::WifiDeploymentInfo(v) => {
DeploymentInfoProto::WifiDeploymentInfo(v.into())
}
DeploymentInfo::CbrsDeploymentInfo(v) => {
DeploymentInfoProto::CbrsDeploymentInfo(v.into())
}
}
}
}

impl TryFrom<GatewayInfo> for GatewayInfoProto {
type Error = hextree::Error;

fn try_from(info: GatewayInfo) -> Result<Self, Self::Error> {
let metadata = if let Some(metadata) = info.metadata {
Some(GatewayMetadataProto {
location: hextree::Cell::from_raw(metadata.location)?.to_string(),
deployment_info: None, // TODO
deployment_info: metadata.deployment_info.map(|v| v.into()),
})
} else {
None
Expand Down Expand Up @@ -147,7 +282,7 @@ impl std::str::FromStr for DeviceType {
}

pub(crate) mod db {
use super::{DeviceType, GatewayInfo, GatewayMetadata};
use super::{DeploymentInfo, DeviceType, GatewayInfo, GatewayMetadata};
use chrono::{DateTime, Utc};
use futures::stream::{Stream, StreamExt};
use helium_crypto::PublicKeyBinary;
Expand All @@ -156,7 +291,7 @@ pub(crate) mod db {

const GET_METADATA_SQL: &str = r#"
select kta.entity_key, infos.location::bigint, infos.device_type,
infos.refreshed_at, infos.created_at
infos.refreshed_at, infos.created_at, infos.deployment_info
from mobile_hotspot_infos infos
join key_to_assets kta on infos.asset = kta.asset
"#;
Expand Down Expand Up @@ -231,11 +366,24 @@ pub(crate) mod db {

impl sqlx::FromRow<'_, sqlx::postgres::PgRow> for GatewayInfo {
fn from_row(row: &sqlx::postgres::PgRow) -> sqlx::Result<Self> {
let deployment_info =
match row.try_get::<Option<Json<DeploymentInfo>>, &str>("deployment_info") {
Ok(di) => di.map(|v| v.0),
// We shouldn't fail if an error occurs in this case.
// This is because the data in this column could be inconsistent,
// and we don't want to break backward compatibility.
Err(_e) => None,
};

// If location field is None, GatewayMetadata also is None, even if deployment_info is present.
// Because "location" is mandatory field
let metadata = row
.get::<Option<i64>, &str>("location")
.map(|loc| GatewayMetadata {
location: loc as u64,
deployment_info,
});

let device_type = DeviceType::from_str(
row.get::<Json<String>, &str>("device_type")
.to_string()
Expand Down
11 changes: 8 additions & 3 deletions mobile_config/tests/gateway_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,9 @@ async fn gateway_stream_info_data_types(pool: PgPool) {
.await;
let gateways = resp.first().unwrap().gateways.clone();
assert_eq!(gateways.len(), 3);

// TODO Check deployment info
let _gw = gateways.first().unwrap();
}

async fn add_db_record(
Expand All @@ -339,16 +342,17 @@ async fn add_mobile_hotspot_infos(
sqlx::query(
r#"
INSERT INTO
"mobile_hotspot_infos" ("asset", "location", "device_type", "created_at", "refreshed_at")
"mobile_hotspot_infos" ("asset", "location", "device_type", "created_at", "refreshed_at", "deployment_info")
VALUES
($1, $2, $3::jsonb, $4, $5);
($1, $2, $3::jsonb, $4, $5, $6::jsonb);
"#,
)
.bind(asset)
.bind(location)
.bind(device_type)
.bind(created_at)
.bind(refreshed_at)
.bind(r#"{"wifiInfoV0": {"antenna": 18, "azimuth": 160, "elevation": 5, "electricalDownTilt": 1, "mechanicalDownTilt": 2}}"#)
.execute(pool)
.await
.unwrap();
Expand Down Expand Up @@ -378,7 +382,8 @@ async fn create_db_tables(pool: &PgPool) {
location numeric NULL,
device_type jsonb NOT NULL,
created_at timestamptz NOT NULL DEFAULT NOW(),
refreshed_at timestamptz
refreshed_at timestamptz,
deployment_info jsonb
);"#,
)
.execute(pool)
Expand Down

0 comments on commit 28743dd

Please sign in to comment.