Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CONSULT-469] - introduce macro for PGN registration and NmeaMessage type #408

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions micro-rdk-nmea-macros/src/composition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,22 @@ impl PgnComposition {
let error_ident = quote! {#crate_ident::parse_helpers::errors::NmeaParseError};
let mrdk_crate = crate::utils::get_micro_rdk_crate_ident();
quote! {

impl #impl_generics #name #src_generics #src_where_clause {
pub fn from_cursor(mut cursor: #crate_ident::parse_helpers::parsers::DataCursor, source_id: u8) -> Result<Self, #error_ident> {
#(#attribute_getters)*
}

impl #impl_generics #crate_ident::messages::message::Message for #name #src_generics #src_where_clause {
fn from_cursor(mut cursor: #crate_ident::parse_helpers::parsers::DataCursor, source_id: u8) -> Result<Self, #error_ident> {
use #crate_ident::parse_helpers::parsers::FieldReader;
#(#parsing_logic)*
Ok(Self {
#(#struct_initialization)*
})
}
#(#attribute_getters)*

pub fn to_readings(self) -> Result<#mrdk_crate::common::sensor::GenericReadingsResult, #error_ident> {

fn to_readings(self) -> Result<#mrdk_crate::common::sensor::GenericReadingsResult, #error_ident> {
let mut readings = std::collections::HashMap::new();
#(#proto_conversion_logic)*
Ok(readings)
Expand Down
5 changes: 4 additions & 1 deletion micro-rdk-nmea/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ mod tests {
use base64::{engine::general_purpose, Engine};

use crate::{
messages::pgns::{GnssPositionData, GnssSatsInView, TemperatureExtendedRange, WaterDepth},
messages::{
message::Message,
pgns::{GnssPositionData, GnssSatsInView, TemperatureExtendedRange, WaterDepth},
},
parse_helpers::{
enums::{
Gns, GnsIntegrity, GnsMethod, RangeResidualMode, SatelliteStatus, TemperatureSource,
Expand Down
63 changes: 63 additions & 0 deletions micro-rdk-nmea/src/messages/message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::collections::HashMap;

use base64::{engine::general_purpose, Engine};
use micro_rdk::{
common::sensor::GenericReadingsResult,
google::protobuf::{value::Kind, Value},
};

use crate::parse_helpers::{errors::NmeaParseError, parsers::DataCursor};

pub trait Message: Sized + Clone {
fn from_cursor(cursor: DataCursor, source_id: u8) -> Result<Self, NmeaParseError>;
fn to_readings(self) -> Result<GenericReadingsResult, NmeaParseError>;
}

#[derive(Debug, Clone)]
pub struct UnparsedMessageData {
data: Vec<u8>,
pgn: u32,
source_id: u8,
}

impl UnparsedMessageData {
pub fn from_bytes(data: Vec<u8>, pgn: u32, source_id: u8) -> Result<Self, NmeaParseError> {
Ok(Self {
data,
source_id,
pgn,
})
}

pub fn to_readings(self) -> Result<GenericReadingsResult, NmeaParseError> {
let data_string = general_purpose::STANDARD.encode(self.data);
Ok(HashMap::from([
(
"source_id".to_string(),
Value {
kind: Some(Kind::NumberValue(self.source_id as f64)),
},
),
(
"data".to_string(),
Value {
kind: Some(Kind::StringValue(data_string)),
},
),
(
"pgn".to_string(),
Value {
kind: Some(Kind::NumberValue(self.pgn as f64)),
},
),
]))
}

pub fn pgn(&self) -> u32 {
self.pgn
}

pub fn source_id(&self) -> u8 {
self.source_id
}
}
1 change: 1 addition & 0 deletions micro-rdk-nmea/src/messages/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod message;
pub mod pgns;
108 changes: 105 additions & 3 deletions micro-rdk-nmea/src/messages/pgns.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
use std::collections::HashMap;

use micro_rdk::{
common::sensor::GenericReadingsResult,
google::protobuf::{value::Kind, Struct, Value},
};
use micro_rdk_nmea_macros::{FieldsetDerive, PgnMessageDerive};

use super::message::{Message, UnparsedMessageData};
use crate::parse_helpers::{
enums::{Gns, GnsIntegrity, GnsMethod, RangeResidualMode, SatelliteStatus, TemperatureSource},
parsers::{DataCursor, FieldSet},
errors::NmeaParseError,
parsers::{DataCursor, FieldSet, NmeaMessageMetadata},
};

#[derive(PgnMessageDerive, Debug)]
#[derive(PgnMessageDerive, Clone, Debug)]
pub struct WaterDepth {
source_id: u8,
#[scale = 0.01]
Expand All @@ -16,7 +24,7 @@ pub struct WaterDepth {
range: u8,
}

#[derive(PgnMessageDerive, Debug)]
#[derive(PgnMessageDerive, Clone, Debug)]
pub struct TemperatureExtendedRange {
source_id: u8,
instance: u8,
Expand Down Expand Up @@ -97,6 +105,7 @@ pub struct Satellite {

#[derive(PgnMessageDerive, Clone, Debug)]
pub struct GnssSatsInView {
source_id: u8,
#[lookup]
#[bits = 2]
range_residual_mode: RangeResidualMode,
Expand All @@ -106,3 +115,96 @@ pub struct GnssSatsInView {
#[length_field = "sats_in_view"]
satellites: Vec<Satellite>,
}

macro_rules! define_pgns {
( $(($pgndef:ident, $enum:ident, $pgn:expr)),* ) => {
#[derive(Clone, Debug)]
pub enum MessageData {
$($enum($pgndef)),*,
Unsupported(UnparsedMessageData)
}

impl MessageData {
pub fn pgn(&self) -> u32 {
match self {
$(Self::$enum(_) => $pgn),*,
Self::Unsupported(unparsed) => unparsed.pgn()
}
}

pub fn from_bytes(pgn: u32, source_id: u8, bytes: Vec<u8>) -> Result<Self, crate::parse_helpers::errors::NmeaParseError> {
Ok(match pgn {
$($pgn => {
let cursor = DataCursor::new(bytes);
Self::$enum($pgndef::from_cursor(cursor, source_id)?)
}),*,
x => Self::Unsupported(UnparsedMessageData::from_bytes(bytes, x, source_id)?)
})
}

pub fn to_readings(self) -> Result<GenericReadingsResult, crate::parse_helpers::errors::NmeaParseError> {
match self {
$(Self::$enum(msg) => msg.to_readings()),*,
Self::Unsupported(msg) => msg.to_readings()
}
}
}
};
}

define_pgns!(
(WaterDepth, Pgn128267, 128267),
(TemperatureExtendedRange, Pgn130316, 130316),
(GnssSatsInView, Pgn129540, 129540)
);

pub struct NmeaMessage {
metadata: NmeaMessageMetadata,
data: MessageData,
}

impl NmeaMessage {
pub fn new(mut bytes: Vec<u8>) -> Result<Self, NmeaParseError> {
let msg_data = bytes.split_off(33);
let metadata = NmeaMessageMetadata::try_from(bytes)?;
let data = MessageData::from_bytes(metadata.pgn(), metadata.src() as u8, msg_data)?;
Ok(Self { metadata, data })
}

pub fn to_readings(self) -> Result<GenericReadingsResult, NmeaParseError> {
Ok(HashMap::from([
(
"prio".to_string(),
Value {
kind: Some(Kind::NumberValue(self.metadata.priority() as f64)),
},
),
(
"pgn".to_string(),
Value {
kind: Some(Kind::NumberValue(self.metadata.pgn() as f64)),
},
),
(
"src".to_string(),
Value {
kind: Some(Kind::NumberValue(self.metadata.src() as f64)),
},
),
(
"dst".to_string(),
Value {
kind: Some(Kind::NumberValue(self.metadata.dst() as f64)),
},
),
(
"fields".to_string(),
Value {
kind: Some(Kind::StructValue(Struct {
fields: self.data.to_readings()?,
})),
},
),
]))
}
}
7 changes: 7 additions & 0 deletions micro-rdk-nmea/src/parse_helpers/parsers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ pub struct NmeaMessageMetadata {
priority: u16,
src: u16,
dst: u16,
pgn: u32,
}

impl NmeaMessageMetadata {
Expand All @@ -243,6 +244,10 @@ impl NmeaMessageMetadata {
pub fn timestamp(&self) -> DateTime<Utc> {
self.timestamp.clone()
}

pub fn pgn(&self) -> u32 {
self.pgn
}
}

impl TryFrom<Vec<u8>> for NmeaMessageMetadata {
Expand All @@ -251,6 +256,7 @@ impl TryFrom<Vec<u8>> for NmeaMessageMetadata {
if value.len() < 32 {
return Err(NmeaParseError::NotEnoughData);
}
let pgn = u32::from_le_bytes(value[0..4].try_into()?);
let seconds = u64::from_le_bytes(value[8..16].try_into()?) as i64;
let millis = u64::from_le_bytes(value[16..24].try_into()?);
let timestamp = DateTime::from_timestamp(seconds, (millis * 1000) as u32)
Expand All @@ -264,6 +270,7 @@ impl TryFrom<Vec<u8>> for NmeaMessageMetadata {
priority,
src,
dst,
pgn,
})
}
}
Expand Down