use crate::protocol::tables::{TableSet};
use crate::protocol::tables::{
HeadTable,
GPSTimeTable,
ECEFTable,
SIGMATable,
BattMemTable,
PositionVCV
};
use crate::protocol::time::gpstime;
use chrono::{DateTime, Utc};
use super::dt_gen_format;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::error::Error;
use crate::structure;
use tracing::{info, warn, error};
use moebius_tools::time::has_dt_gen::HasDtGen;
#[doc = r#"
This struct contains the message received from source.
Is deserialized ona HasMap an describe by dt_gen, checksum
"#]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GsofData {
#[serde(with = "dt_gen_format")]
pub dt_recv: DateTime<Utc>,
#[serde(with = "dt_gen_format")]
pub dt_gen: DateTime<Utc>,
pub checksum: u16,
pub head: Option<HeadTable>,
pub time: Option<GPSTimeTable>,
pub ecef: Option<ECEFTable>,
pub sigma: Option<SIGMATable>,
pub batt_mem: Option<BattMemTable>,
pub position_vcv: Option<PositionVCV>
}
impl HasDtGen for GsofData {
fn get_dt_gen(&self) -> DateTime<Utc> {
self.dt_gen
}
}
impl Default for GsofData {
fn default() -> Self {
GsofData {
dt_recv: Utc::now(),
dt_gen: Utc::now(),
checksum:0,
head:None,
time:None,
ecef:None,
sigma:None,
batt_mem:None,
position_vcv:None
}
}
}
impl fmt::Display for GsofData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Data(protocol: Gsof, recv {}, gen, {})", self.dt_recv, self.dt_gen )
}
}
use tokio::io::AsyncReadExt;
impl GsofData {
pub fn new(
dt_recv:DateTime<Utc>,
dt_gen:DateTime<Utc>,
checksum:u16)-> Self {
Self {
dt_recv,
dt_gen,
checksum,
head:None,
time:None,
ecef:None,
sigma:None,
batt_mem:None,
position_vcv:None
}
}
fn set_head(&mut self, table:&HeadTable) {
self.head = Some(*table);
}
fn set_time(&mut self, table: &GPSTimeTable) {
self.time = Some(*table);
}
fn set_ecef(&mut self, table: &ECEFTable) {
self.ecef = Some(*table);
}
fn set_sigma(&mut self, table: &SIGMATable) {
self.sigma = Some(*table);
}
fn set_batt_mem(&mut self, table: &BattMemTable) {
self.batt_mem = Some(*table);
}
fn set_position_vcv(&mut self, table: &PositionVCV) {
self.position_vcv = Some(*table);
}
fn set_dt_gen(&mut self, dt: &DateTime<Utc>) {
self.dt_gen = *dt;
}
fn set_dt_recv(&mut self, dt: &DateTime<Utc>) {
self.dt_recv = *dt;
}
pub fn latency(&self) -> i64 {
let diff = self.dt_recv - self.dt_gen;
diff.num_milliseconds()
}
fn set_checksum(&mut self, checksum: &u16) {
self.checksum = *checksum;
}
pub fn json_dumps(&self) -> String {
serde_json::to_string_pretty(&self).unwrap()
}
pub fn json_loads(serialized: &str) -> Self {
serde_json::from_str(serialized).unwrap()
}
pub fn json_to_value(&self) -> serde_json::Value {
serde_json::to_value(self).unwrap()
}
pub fn json_from_value(
value:&serde_json::Value) -> Result<Self,Box<dyn Error>> {
match serde_json::from_value(value.clone()){
Ok(val)=> Ok(val),
Err(err)=>Err(Box::new(err))
}
}
fn get_records(
slice_line:&[u8],
tables: &TableSet,
checksum: u16) ->
Result<Self, String> {
let mut line:Vec<u8> = slice_line.to_vec();
let mut collect_tables = GsofData::default();
collect_tables.set_checksum(&checksum);
while line.len() > 3 {
let byte_struct = structure!(">2B");
let head = &line[0..2];
let values = byte_struct.unpack(head).unwrap();
let table_key:u8 = values.0;
let record_length:usize = values.1 as usize;
let table_bytes = &line[2..record_length+2];
match tables.get(&table_key) {
Some(table_info) if table_info.name=="TIME"=> {
let byte_struct = structure!(">IH4B");
let values = byte_struct.unpack(table_bytes).unwrap();
let table = GPSTimeTable::new(values);
collect_tables.set_time(&table);
},
Some(table_info) if table_info.name=="ECEF"=> {
let byte_struct = structure!(">3d");
let values = byte_struct.unpack(table_bytes).unwrap();
let table = ECEFTable::new(values);
collect_tables.set_ecef(&table);
},
Some(table_info) if table_info.name=="SIGMA"=> {
let byte_struct = structure!(">9fH");
let values = byte_struct.unpack(table_bytes).unwrap();
let table = SIGMATable::new(values);
collect_tables.set_sigma(&table);
},
Some(table_info) if table_info.name=="BATT_MEM"=> {
let byte_struct = structure!(">Hd");
let values = byte_struct.unpack(table_bytes).unwrap();
let table = BattMemTable::new(values);
collect_tables.set_batt_mem(&table);
},
Some(table_info) if table_info.name=="POSITION_VCV"=> {
let byte_struct = structure!(">8fh");
let values = byte_struct.unpack(table_bytes).unwrap();
let table = PositionVCV::new(values);
collect_tables.set_position_vcv(&table);
},
Some(table_info) => {
info!("Table not registered for option {}", table_info.name);
},
None => {
warn!("Error de conversion al obtener table_info");
}
};
if record_length <= line.len() {
line.drain(0..record_length+2);
} else {
line.drain(0..line.len());
}
}
match collect_tables.time {
Some(ref table)=>{
let leap: i64 = 18;
match gpstime(&table, leap) {
Ok(dt_gen)=>{
let dt_recv:DateTime<Utc> = Utc::now();
collect_tables.set_dt_gen(&dt_gen);
collect_tables.set_dt_recv(&dt_recv);
Ok(collect_tables)
},
Err(e)=>{
error!("Cannot convert table time to a Datetime<Utc>");
return Err(e.to_string());
}
} },
None => {
let dt_gen:DateTime<Utc> = Utc::now();
let dt_recv:DateTime<Utc> = Utc::now();
warn!("Data received at {} doesn't have TIME table", dt_recv);
collect_tables.set_dt_gen(&dt_gen);
collect_tables.set_dt_recv(&dt_recv);
Ok(collect_tables)
}
} }
pub async fn read_from_stream<S:AsyncReadExt + Unpin>(
read_stream:&mut S,
tables:&TableSet) -> Result<Self,String> {
let table_key:u8 = 0;
let header_metadata = tables.get(&table_key).unwrap();
let header_conv = &header_metadata.conversion;
let header_bytes:u8 = 7;
let mut header_buff = vec![0;header_bytes.into()];
match read_stream.read_exact(&mut header_buff).await {
Ok(_n)=>{
let header = header_buff;
let conv = &header_conv[0];
assert_eq!(conv, ">7B");
let len = header.len();
let to_sum = &header[1..len];
let sum_head:u16 = to_sum
.iter().fold(0, |a, &b| a as u16 + b as u16);
let byte_struct = structure!(">7B");
let values = byte_struct.unpack(header).unwrap();
let head_table = HeadTable::new(values);
let msg_len = &head_table.length -3;
let mut line = vec![0;msg_len.into()];
match read_stream.read_exact(&mut line).await {
Ok(_n)=> {
let tot_bytes = line.iter().fold(0, |a, &b| a as u16 + b as
u16);
let mut checksum = vec![0;2];
match read_stream.read_exact(&mut checksum).await {
Ok(_n)=> {
let byte_struct = structure!(">2B");
let values = byte_struct.unpack(checksum).unwrap();
let check = (sum_head+tot_bytes) % 256;
if check == values.0 as u16 {
match Self::get_records(
&line,
tables,
check) {
Ok(mut data) => {
data.set_head(&head_table);
Ok(data)
},
Err(_) => Err("Error on get_records".into())
}
}
else {
warn!("Message with wrong checksum {} != {}",
check,
values.0);
Err("Error on checksum".into())
}
},
Err(err)=>{
error!("Error at reading stream slice checksum {}", err);
Err(err.to_string())
}
} },
Err(err)=> {
error!("Error al leer de stream slice body {}", err);
Err(err.to_string())
}
}
}, Err(err)=> {
error!("Error ar reading stream slice head {}", err);
Err(err.to_string())
}
} }
}