use std::fmt;
use nom::bits::{bits, streaming};
use nom::error::Error;
use nom::IResult;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "chrono")]
use chrono::{DateTime, TimeZone, Utc};
use crate::utils;
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FixStatus {
pub predicted: bool,
pub diff_corrected: bool,
pub last_know: bool,
pub invalid_fix: bool,
pub twod_fix: bool,
pub historic: bool,
pub invalid_time: bool,
}
impl FixStatus {
pub fn parse(input: &[u8]) -> IResult<&[u8], FixStatus> {
#[allow(clippy::type_complexity)]
let (i, b): (&[u8], (u8, u8, u8, u8, u8, u8, u8)) =
bits::<_, _, Error<(&[u8], usize)>, _, _>(nom::sequence::tuple((
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
)))(input)?;
Ok((
i,
FixStatus {
predicted: b.6 == 1,
diff_corrected: b.5 == 1,
last_know: b.4 == 1,
invalid_fix: b.3 == 1,
twod_fix: b.2 == 1,
historic: b.1 == 1,
invalid_time: b.0 == 1,
},
))
}
}
#[derive(PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum NetworkTechnology {
CdmaGsm,
Umts,
Lte,
Reserved,
}
impl NetworkTechnology {
pub fn parse(input: u8) -> NetworkTechnology {
match input {
0 => NetworkTechnology::CdmaGsm,
1 => NetworkTechnology::Umts,
10 => NetworkTechnology::Lte,
11 => NetworkTechnology::Reserved,
_ => panic!("not found"),
}
}
}
impl fmt::Display for NetworkTechnology {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
NetworkTechnology::CdmaGsm => {
write!(f, "NetworkTechnology::CdmaGsm")
}
NetworkTechnology::Umts => write!(f, "NetworkTechnology::Umts"),
NetworkTechnology::Lte => write!(f, "NetworkTechnology::Lte"),
NetworkTechnology::Reserved => {
write!(f, "NetworkTechnology::Reserved")
}
}
}
}
impl fmt::Debug for NetworkTechnology {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
NetworkTechnology::CdmaGsm => {
write!(f, "NetworkTechnology::CdmaGsm")
}
NetworkTechnology::Umts => write!(f, "NetworkTechnology::Umts"),
NetworkTechnology::Lte => write!(f, "NetworkTechnology::Lte"),
NetworkTechnology::Reserved => {
write!(f, "NetworkTechnology::Reserved")
}
}
}
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CommState {
pub available: bool,
pub network_service: bool,
pub data_service: bool,
pub connected: bool,
pub voice_call_active: bool,
pub roaming: bool,
pub network_technology: NetworkTechnology,
}
impl CommState {
pub fn parse(input: &[u8]) -> IResult<&[u8], CommState> {
#[allow(clippy::type_complexity)]
let (i, b): (&[u8], (u8, u8, u8, u8, u8, u8, u8)) =
bits::<_, _, Error<(&[u8], usize)>, _, _>(nom::sequence::tuple((
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(2u8),
)))(input)?;
Ok((
i,
CommState {
available: b.6 == 1,
network_service: b.5 == 1,
data_service: b.4 == 1,
connected: b.3 == 1,
voice_call_active: b.2 == 1,
roaming: b.1 == 1,
network_technology: NetworkTechnology::parse(b.0),
},
))
}
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Inputs {
pub ignition: bool,
pub input_1: bool,
pub input_2: bool,
pub input_3: bool,
pub input_4: bool,
pub input_5: bool,
pub input_6: bool,
pub input_7: bool,
}
impl Inputs {
pub fn parse(input: &[u8]) -> IResult<&[u8], Inputs> {
#[allow(clippy::type_complexity)]
let (i, b): (&[u8], (u8, u8, u8, u8, u8, u8, u8, u8)) =
bits::<_, _, Error<(&[u8], usize)>, _, _>(nom::sequence::tuple((
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
)))(input)?;
Ok((
i,
Inputs {
ignition: b.7 == 1,
input_1: b.6 == 1,
input_2: b.5 == 1,
input_3: b.4 == 1,
input_4: b.3 == 1,
input_5: b.2 == 1,
input_6: b.1 == 1,
input_7: b.0 == 1,
},
))
}
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct UnitStatus {
pub ota_update: bool,
pub gps_antenna: bool,
pub gps_self_test: bool,
pub gps_tracking: bool,
pub reserved_1: bool,
pub reserved_2: bool,
pub reserved_3: bool,
pub unused: bool,
}
impl UnitStatus {
pub fn parse(input: &[u8]) -> IResult<&[u8], UnitStatus> {
#[allow(clippy::type_complexity)]
let (i, b): (&[u8], (u8, u8, u8, u8, u8, u8, u8, u8)) =
bits::<_, _, Error<(&[u8], usize)>, _, _>(nom::sequence::tuple((
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
streaming::take(1u8),
)))(input)?;
Ok((
i,
UnitStatus {
ota_update: b.7 == 0,
gps_antenna: b.6 == 0,
gps_self_test: b.5 == 0,
gps_tracking: b.4 == 0,
reserved_1: b.3 == 0,
reserved_2: b.2 == 0,
reserved_3: b.1 == 0,
unused: b.0 == 0,
},
))
}
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EventReport {
#[cfg(feature = "chrono")]
pub update_time: DateTime<Utc>,
#[cfg(not(feature = "chrono"))]
pub update_time: u32,
#[cfg(feature = "chrono")]
pub time_of_fix: DateTime<Utc>,
#[cfg(not(feature = "chrono"))]
pub time_of_fix: u32,
pub latitude: f32,
pub longitude: f32,
pub altitude: f32,
pub speed: f32,
pub heading: u16,
pub satellites: u8,
pub fix_status: FixStatus,
pub carrier: u16,
pub rssi: i16,
pub comm_state: CommState,
pub hdop: u8,
pub inputs: Inputs,
pub unit_status: UnitStatus,
pub event_index: u8,
pub event_code: u8,
pub accums: u8,
pub append: u8,
pub accum_list: Vec<u32>,
}
impl EventReport {
pub fn parse(input: &[u8]) -> std::io::Result<EventReport> {
let mut accum_list: Vec<u32> = vec![];
#[cfg(feature = "chrono")]
let (i, update_time) = utils::pdt(input).unwrap();
#[cfg(not(feature = "chrono"))]
let (i, update_time) = utils::pu32(input).unwrap();
let (i, time_of_fix) = utils::pu32(i).unwrap();
let (i, latitude) = utils::pf32(i).unwrap();
let (i, longitude) = utils::pf32(i).unwrap();
let (i, altitude) = utils::pf32(i).unwrap();
let (i, speed) = utils::pf32(i).unwrap();
let (i, heading) = utils::pu16(i).unwrap();
let (i, satellites) = utils::pu8(i).unwrap();
let (i, fix_status) = FixStatus::parse(i).unwrap();
let (i, carrier) = utils::pu16(i).unwrap();
let (i, rssi) = utils::p16(i).unwrap();
let (i, comm_state) = CommState::parse(i).unwrap();
let (i, hdop) = utils::pu8(i).unwrap();
let (i, inputs) = Inputs::parse(i).unwrap();
let (i, unit_status) = UnitStatus::parse(i).unwrap();
let (i, event_index) = utils::pu8(i).unwrap();
let (i, event_code) = utils::pu8(i).unwrap();
let (i, accums) = utils::pu8(i).unwrap();
let (i, append) = utils::pu8(i).unwrap();
let mut inp = i;
for _ in 0..accums {
let (x, v) = utils::pu32(inp).unwrap();
inp = x;
accum_list.push(v);
}
Ok(EventReport {
update_time,
time_of_fix,
latitude,
longitude,
altitude,
speed,
heading,
satellites,
fix_status,
carrier,
rssi,
comm_state,
hdop,
inputs,
unit_status,
event_index,
event_code,
accums,
append,
accum_list,
})
}
}
#[cfg(test)]
mod tests {
use super::EventReport;
use crate::message_header::MessageHeader;
use crate::messages::event_report::NetworkTechnology;
use crate::options_header::OptionsHeader;
#[test]
fn test_parse_event_report_message() {
let data: [u8; 117] = [
0x83, 0x05, 0x46, 0x34, 0x66, 0x32, 0x35, 0x01, 0x01, 0x01, 0x02,
0x3a, 0x86, 0x5f, 0xf1, 0x3a, 0x54, 0x5f, 0xf1, 0x3a, 0x57, 0xf1,
0xe2, 0x85, 0x78, 0xe4, 0x22, 0xd6, 0x40, 0x00, 0x01, 0x36, 0xf8,
0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0xff,
0x8d, 0x02, 0x1e, 0x1e, 0x00, 0x7b, 0x21, 0x10, 0x00, 0x00, 0x00,
0x31, 0xe0, 0x00, 0x00, 0x10, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,
0x22, 0x2a, 0x32, 0x00, 0x00, 0x03, 0xf1, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x2d, 0x3f, 0x01, 0xc8, 0x2d,
0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let (i, _) = OptionsHeader::parse(&data).unwrap();
let (i, _) = MessageHeader::parse(i).unwrap();
let event_report = EventReport::parse(i).unwrap();
assert_eq!(event_report.update_time, 1609644628);
assert_eq!(event_report.time_of_fix, 1609644631);
approx::assert_relative_eq!(event_report.latitude, -23.6812936);
approx::assert_relative_eq!(
event_report.longitude,
-46.747897599999995
);
approx::assert_relative_eq!(event_report.altitude, 0.0079608);
approx::assert_relative_eq!(event_report.speed, 0.0000011);
assert_eq!(event_report.heading, 0);
assert_eq!(event_report.satellites, 6);
assert_eq!(event_report.fix_status.twod_fix, true);
assert_eq!(event_report.carrier, 0);
assert_eq!(event_report.rssi, -115);
assert_eq!(event_report.comm_state.available, false);
assert_eq!(
event_report.comm_state.network_technology,
NetworkTechnology::CdmaGsm
);
assert_eq!(event_report.hdop, 30);
assert_eq!(event_report.inputs.ignition, false);
assert_eq!(event_report.unit_status.gps_antenna, true);
assert_eq!(event_report.event_index, 123);
assert_eq!(event_report.event_code, 33);
assert_eq!(event_report.accums, 16);
assert_eq!(event_report.append, 0);
assert_eq!(event_report.accum_list.len(), 16);
}
}