use std::collections::BTreeMap;
use std::fs::OpenOptions;
use std::io::Read;
use asterix_parser::asterix::uap::providers::Provider;
use asterix_parser::asterix::uap_json::enums::{AttributeItem, AttributeType, AttributeValueType, ConstraintType, DataItemType, DataItemValueType, DataType, DenominatorType, ElementType, ElementValueType, LsbType, Uap};
use asterix_parser::asterix::uap_json::structures::{DataItemRule, DataRecord};
use asterix_parser::asterix::uap_json::traits::Describe;
use bitvec::prelude::*;
use json::stringify;
use thiserror::Error;
use asterix_parser::asterix::category::{Category, CategoryIndex, CategoryKey};
use asterix_parser::asterix::Context;
#[derive(Error, Debug)]
pub enum ParseError {
#[error("category not found")]
CategoryNotFound { category_code: String },
#[error("Message informed length [{informed}] doesn't match current length [{current}]")]
BadMessageInformedLength { informed: usize, current: usize },
#[error("UAP definition not found for {uap_name}")]
UapDefinitionNotFound { uap_name: String },
#[error("Parser unforeseen dependency relation")]
ParserUnforeseenDependecyRelation,
#[error("Could not open UAP JSON definition file")]
OpenUapJsonDefinitionError
}
#[derive(Default, Clone)]
pub struct FSpecInfo {
pub len: u8,
pub fspec:BTreeMap<u8, bool>
}
#[derive(Clone)]
pub struct AttributeConstraint {
constraint_type: ConstraintType,
limit_value: AttributeConstraintLimit
}
#[derive(Clone)]
pub enum AttributeConstraintLimit {
SignedInteger { value: i32 },
}
#[derive(Clone)]
pub enum AttributeLsb {
Integer { value: i32 },
Real { value: f64 }
}
#[derive(Default, Clone) ]
pub struct Attribute {
pub name: String,
pub title: String,
pub bits: BitVec<u8, Msb0>,
pub value_type: String,
pub signed: Option<bool>,
pub constraints: Option<Vec<AttributeConstraint>>,
pub lsb: Option<AttributeLsb>,
pub unit: Option<String>,
pub description: Option<String>
}
#[test]
pub(crate) fn test_asterix_parse() -> anyhow::Result<()> {
let _cat034_1_29_definition = "resources/uaps/cat034_1_29_definition.json";
let _cat048_1_30_definition = "resources/uaps/cat048_1_30_definition.json";
let _cat048_1_31_definition = "resources/uaps/cat048_1_31_definition.json";
let my_file_name = _cat048_1_31_definition;
let mut myfile = match OpenOptions::new()
.read(true)
.write(false)
.create(false)
.open(my_file_name) {
Ok(f) => f,
Err(e) => panic!("Error opening file {}", e),
};
let mut raw_json = String::new();
myfile.read_to_string(&mut raw_json).unwrap();
let trimmed_json = raw_json.trim();
let deserialized:DataRecord = match serde_json::from_str(&trimmed_json) {
Ok(d) => d,
Err(_) => { return Err(ParseError::OpenUapJsonDefinitionError.into())}
};
let mut indent_level = 0 as usize;
let _description = deserialized.describe(&mut indent_level);
let mut message_octets: Vec<u8> = vec![];
if my_file_name.contains("048") {
message_octets
.append(&mut vec![
0x30, 0x00, 0x30, 0xFD, 0xF7, 0x02, 0x19, 0xC9, 0x35, 0x6D, 0x4D, 0xA0, 0xC5, 0xAF, 0xF1, 0xE0, 0x02, 0x00, 0x05, 0x28, 0x3C, 0x66, 0x0C, 0x10, 0xC2, 0x36, 0xD4,
0x18, 0x20, 0x01, 0xC0, 0x78, 0x00,
0x31, 0xBC, 0x00, 0x00,
0x40, 0x0D, 0xEB, 0x07, 0xB9, 0x58, 0x2E, 0x41, 0x00, 0x20, 0xF5 ]);
} else {
message_octets
.append(&mut vec![
0x22, 0x00, 0x10, 0xf6, 0x19, 0x0e, 0x02, 0x3a, 0x69,0x2b, 0x40, 0x88, 0x40, 0x40, 0x80, 0x00 ]);
}
let message = parse_message(&deserialized, &message_octets)?;
describe_message_attributes(message);
Ok(())
}
fn describe_message_attributes(message: BTreeMap<String, Attribute>) {
for attribute in message {
println!();
println!("key:[{}], bits length[{:02}] - bits:{}]", attribute.0, attribute.1.bits.len(), attribute.1.bits);
print!(" Title: {:?}", attribute.1.title);
if attribute.1.description.is_some()
{
print!(", description{:?}", attribute.1.description);
}
println!();
if attribute.1.signed.is_some() {
println!(" Signed: {}", attribute.1.signed.unwrap());
}
if attribute.1.unit.is_some() {
println!(" Unit: {}", attribute.1.unit.unwrap());
}
if attribute.1.constraints.is_some() {
match attribute.1.constraints {
Some(c) => {
for constraint in c {
let constraint_str =
match constraint.constraint_type {
ConstraintType::Less => { stringify!(ConstraintType::Less ) },
ConstraintType::Great => { stringify!(ConstraintType::Great ) },
ConstraintType::LessOrEqual => { stringify!(ConstraintType::LessOrEqual ) },
ConstraintType::GreatOrEqual => { stringify!(ConstraintType::GreatOrEqual ) },
};
let constraint_value =
match constraint.limit_value {
AttributeConstraintLimit::SignedInteger { value } => value.to_string(),
};
println!(" Constraint: {} {} ", constraint_str, constraint_value);
}
},
None => (),
}
}
if attribute.1.lsb.is_some() {
match attribute.1.lsb {
Some(e) => {
let lsb_value = match e {
AttributeLsb::Integer { value } => value.to_string(),
AttributeLsb::Real { value } => format!("{:15.7}", value),
};
println!(" LSB: {} ", lsb_value);
},
None => (),
}
}
}
}
fn parse_message(data_record: &DataRecord, octets: &Vec<u8>) -> anyhow::Result<BTreeMap<String, Attribute>> {
let mut attributes_map = BTreeMap::<String, Attribute>::new();
let asterix_context = Context::new()?;
let cat_index = match CategoryIndex::from_u8(data_record.number) {
Some(c) => c,
None => return Err(ParseError::CategoryNotFound { category_code: data_record.number.to_string() }.into())
};
let category_key = CategoryKey {
index: cat_index,
edition: data_record.edition.clone(),
provider: Provider::Standard };
let category = get_category(asterix_context, category_key, octets[0])?;
let mut category_attribute = Attribute::default();
category_attribute.name = category.key.index.as_string();
category_attribute.description = Some(category.description);
attributes_map.insert(stringify("message_category"), category_attribute);
let octets_self_intormed_length = octets[1] as usize * 256 + octets[2] as usize;
if octets_self_intormed_length != octets.len() {
return Err(ParseError::BadMessageInformedLength {
informed: octets_self_intormed_length,
current: octets.len()
}.into());
}
let category_prefix = format!("{}", category.key.index.as_str());
let mut current_index = 3 as usize;
let expected_frns = data_record.catalogue.len() as u8;
let fspec_info: FSpecInfo = get_fspec_array(octets, current_index, expected_frns);
current_index += fspec_info.len as usize;
describe_fspec(&fspec_info);
let data_items_map = find_present_data_item_definitions(
&fspec_info.fspec,
&data_record.uap,
&data_record.catalogue)?;
list_present_data_item_definitions(&data_items_map);
let mut frn_bits = BTreeMap::<u8, BitVec<u8,Msb0>>::new();
let mut calc_len_current_index = usize::from(current_index);
for data_item_entry in &data_items_map {
let data_item_length =
match &data_item_entry.1.rule {
DataItemType::ContextFree { value } => {
let primary_field_length = match value {
DataItemValueType::Element { rule:_, size } => usize::from(*size),
DataItemValueType::Group { items } => {
find_group_length_in_bits(items)?
},
DataItemValueType::Extended { items } => {
find_extended_length_in_bits(items, octets, calc_len_current_index)?
},
DataItemValueType::Repetitive { rep, variation} => {
find_repetitive_length_in_bits(rep, variation, octets, calc_len_current_index)?
},
DataItemValueType::Compound { fspec, items } => {
find_compound_length_in_bits(fspec, items, octets, calc_len_current_index)?
},
DataItemValueType::Explicit { expl:_ } => 0_usize, };
primary_field_length
},
};
assert_eq!(0, data_item_length%8);
let start_octet = calc_len_current_index;
let end_octet = start_octet + (data_item_length/8);
let bits = octets[start_octet..end_octet].view_bits::<Msb0>().to_bitvec();
frn_bits.insert(*data_item_entry.0, bits);
calc_len_current_index += data_item_length/8;
}
assert_eq!(octets_self_intormed_length, calc_len_current_index);
attributes_map = find_message_attributes(&category_prefix, &data_items_map, &frn_bits)?;
Ok(attributes_map)
}
fn find_message_attributes(
category_prefix: &String,
data_items_map: &BTreeMap<u8, &DataItemRule>,
frn_bits: &BTreeMap<u8, BitVec<u8, Msb0>>) -> anyhow::Result<BTreeMap<String, Attribute>> {
let mut found_attributes = BTreeMap::<String, Attribute>::new();
for data_item_entry in data_items_map {
let data_item_prefix = format!("I{}", &data_item_entry.1.name);
let data_item_bits = frn_bits[data_item_entry.0].to_bitvec();
let mut frn_consumed_bits = 0_usize;
match &data_item_entry.1.rule {
DataItemType::ContextFree { value } => match value {
DataItemValueType::Element { rule, size }
=> {
let mut attribute = Attribute::default();
attribute.name = format!("{}_{}_{}", *category_prefix, data_item_prefix.as_str() ,
data_item_entry.1.title.replace(" ", "_").to_uppercase().as_str());
let last_bit = frn_consumed_bits + *size as usize;
attribute.bits = data_item_bits[frn_consumed_bits..last_bit].to_bitvec();
fill_dataitem_element_attributes(rule, &mut attribute);
found_attributes.insert(attribute.name.to_owned(), attribute);
},
DataItemValueType::Group { items }
=> {
let group_attributes =
fill_group_attributes(&category_prefix, &data_item_prefix, items, &mut frn_consumed_bits, data_item_bits)?;
for attribute in group_attributes {
found_attributes.insert(attribute.name.to_owned(), attribute);
}
},
DataItemValueType::Extended { items:_ } => (),
DataItemValueType::Repetitive { rep:_, variation:_ } => (),
DataItemValueType::Compound { fspec:_, items:_ } => (),
DataItemValueType::Explicit { expl:_ } => (),
},
}
}
Ok(found_attributes)
}
fn fill_group_attributes(
category_prefix: &str,
data_item_prefix: &str,
items: &Vec<Option<AttributeItem>>,
frn_consumed_bits: &mut usize,
data_item_bits: BitVec<u8, Msb0>) -> anyhow::Result<Vec<Attribute>> {
let mut group_attributes = Vec::<Attribute>::new();
for item in items {
let inner_item = match item {
Some(e) => e,
None => continue,
};
match inner_item {
AttributeItem::SpareEntry { length, spare:_ } => {
*frn_consumed_bits += length;
continue
} ,
AttributeItem::AttributeEntry(v) => {
let mut attribute = Attribute::default();
attribute.name = format!("{}_{}_{}", category_prefix, data_item_prefix, v.name);
attribute.title = v.title.to_owned();
attribute.description = v.description.to_owned();
match &v.rule {
AttributeType::ContextFree { value } => match value {
AttributeValueType::Element { rule:_, size } => {
let start_bit = *frn_consumed_bits;
let last_bit = *frn_consumed_bits + *size as usize;
attribute.bits = data_item_bits[start_bit..last_bit].to_bitvec();
*frn_consumed_bits = last_bit;
},
_ => { return Err(ParseError::ParserUnforeseenDependecyRelation.into())}
},
}
group_attributes.push(attribute);
},
};
}
Ok(group_attributes)
}
fn fill_dataitem_element_attributes(
rule: &ElementType,
attribute: &mut Attribute) {
match rule {
ElementType::ContextFree { value } => match value {
ElementValueType::Fx => (),
ElementValueType::Integer { constraints:_, signed:_} => (),
ElementValueType::Quantity { constraints, lsb, signed, unit }
=> {
let mut found_constraints = Vec::<AttributeConstraint>::new();
for constraint in constraints {
let constraint_value = match constraint.value {
DataType::Integer { value } => value,
};
let attribute_constraint = AttributeConstraint {
constraint_type: constraint.constraint_type,
limit_value: AttributeConstraintLimit::SignedInteger { value: constraint_value }
};
found_constraints.push(attribute_constraint);
}
if found_constraints.len() > 0 {
attribute.constraints = Some(found_constraints);
}
attribute.signed = Some(*signed);
let lsb =
match lsb {
LsbType::Integer { value } => AttributeLsb::Integer { value: *value },
LsbType::Div { denominator, numerator } => {
let num = match numerator {
DataType::Integer { value } => *value as f64,
};
let den = match denominator {
DenominatorType::Pow { base, exponent } => {
base.powf(*exponent)
},
};
AttributeLsb::Real { value: num/den }
},
};
attribute.lsb = Some(lsb);
attribute.unit = Some(unit.to_owned());
},
ElementValueType::Raw => (),
ElementValueType::Regular { size:_ } => (),
ElementValueType::String { variation:_ } => (),
ElementValueType::Table { values:_ } => (),
},
}
}
fn find_compound_length_in_bits(
_fspec: &Option<u8>,
items: &Vec<Option<AttributeItem>>,
octets: &Vec<u8>,
calc_len_current_index: usize) -> anyhow::Result<usize> {
let minimum_expected_frns = 7_u8;
let fspec_info: FSpecInfo = get_fspec_array(octets, calc_len_current_index, minimum_expected_frns);
describe_fspec(&fspec_info);
let mut item_count = 1_u8;
let mut items_length = usize::from(fspec_info.len)*8; for item in items {
match item {
Some(_) => (),
None => {
item_count += 1;
continue;
},
};
if fspec_info.fspec[&item_count] == false {
item_count += 1;
continue;
}
item_count += 1;
items_length += find_attribute_item_length_in_bits(item)?
}
Ok(items_length)
}
fn find_repetitive_length_in_bits(
rep: &ElementValueType,
variation: &AttributeValueType,
octets: &Vec<u8>,
calc_len_current_index: usize) -> anyhow::Result<usize> {
let multiplier_bits = match rep {
ElementValueType::Regular { size } => usize::from(*size),
_ => { return Err(ParseError::ParserUnforeseenDependecyRelation.into())},
};
assert_eq!(0, multiplier_bits%8);
let start_octet = calc_len_current_index;
let end_octet = start_octet + (multiplier_bits/8);
let bitslice = octets[start_octet..end_octet].view_bits::<Msb0>();
let multiplier = bitslice.load::<usize>();
let repetitive_len =
match variation {
AttributeValueType::Group { items } => {
find_group_length_in_bits(items)?
}
_ => { return Err(ParseError::ParserUnforeseenDependecyRelation.into())},
};
Ok(multiplier_bits + repetitive_len * multiplier)
}
fn find_extended_length_in_bits(
items: &Vec<Option<AttributeItem>>,
octets: &Vec<u8>,
calc_len_current_index: usize) -> anyhow::Result<usize> {
let mut local_current_index = calc_len_current_index;
let mut item_len = 0_usize;
let mut fx_set = true;
for item in items {
if !fx_set {
break
}
item_len += find_attribute_item_length_in_bits(&item)?;
if item_len%8 == 0 {
let bits = octets[local_current_index].view_bits::<Msb0>().to_bitvec();
if bits[7] == true {
local_current_index += 1;
} else {
fx_set = false;
}
}
}
Ok(item_len)
}
fn find_attribute_item_length_in_bits(item: &Option<AttributeItem>) -> anyhow::Result<usize> {
let item_length = match item {
Some(i) => {
let entry_len = match i {
AttributeItem::SpareEntry { length, spare } => {
if *spare {*length } else { 0_usize }
},
AttributeItem::AttributeEntry(e) => {
let a_len = match &e.rule {
AttributeType::ContextFree { value } => {
let len = match value {
AttributeValueType::Element { rule:_, size } => usize::from(*size),
AttributeValueType::Group { items } => find_group_length_in_bits(items)?, _ => { return Err(ParseError::ParserUnforeseenDependecyRelation.into())}
};
len
},
};
a_len
},
};
entry_len
},
None => 1_usize };
Ok(item_length)
}
fn find_group_length_in_bits(items: &Vec<Option<AttributeItem>>) -> anyhow::Result<usize> {
let mut len = 0_usize;
for item in items {
match item {
Some(aitem) => {
match aitem {
AttributeItem::SpareEntry { length, spare:_ } => { len += length; },
AttributeItem::AttributeEntry(aentry) => {
match &aentry.rule {
AttributeType::ContextFree { value } => match value {
AttributeValueType::Element { rule: _, size } => { len += usize::from(*size); },
_ => { return Err(ParseError::ParserUnforeseenDependecyRelation.into())}
},
}
},
}
},
None => (),
}
}
Ok(len)
}
fn get_category(context:Context, category_key: CategoryKey, category_octet: u8) -> anyhow::Result<Category> {
let category = match context.categories.get(&category_key) {
Some(c) => c.clone(),
None => return Err((ParseError::CategoryNotFound { category_code: category_octet.to_string()}).into())
};
Ok(category.clone())
}
fn get_fspec_array(octets: &[u8], current_index: usize, expected_frns: u8) -> FSpecInfo {
let mut fx = true;
let mut frn:u8 = 0;
let mut fspec_info = FSpecInfo::default();
let mut local_current_index = current_index;
fspec_info.len = 1;
while fx {
let octet = octets[local_current_index];
let fx_octet = octet.view_bits::<Msb0>().to_bitvec();
let mut bit_count = 0;
for bit in fx_octet {
if bit_count == 7 {
fx = bit;
if bit {
local_current_index += 1;
fspec_info.len += 1;
}
}
else {
frn += 1;
fspec_info.fspec.insert(frn, bit);
}
bit_count += 1;
}
}
while frn < expected_frns {
frn += 1;
fspec_info.fspec.insert(frn, false);
}
fspec_info
}
fn describe_fspec(fspec_info: &FSpecInfo) {
println!("FRN configuration: ");
let mut frnh = String::new();
let mut frnx = String::new();
let mut frnl: String = String::new();
for frn in &fspec_info.fspec {
frnh += format!("| {:02}", frn.0).as_str();
frnx += format!("| {:2}", if *frn.1 {"X"} else {" "}).as_str();
frnl += "+---";
}
frnl += "+";
frnh += "|";
frnx += "|";
println!("{}", frnl);
println!("{}", frnh);
println!("{}", frnl);
println!("{}", frnx);
println!("{}", frnl);
}
fn find_present_data_item_definitions<'a> (
fspec: &'a BTreeMap<u8, bool>,
uap: &Uap,
catalogue: &'a [DataItemRule]) -> anyhow::Result<BTreeMap<u8, &'a DataItemRule>> {
let mut data_items_map = BTreeMap::<u8, &DataItemRule>::new();
let uap_items = match &uap {
Uap::Uap { items } => items,
};
for frn in fspec.into_iter().filter(| x | *x.1 ) {
let index = (frn.0 - 1) as usize;
let uap_data_item = format!("{}", &uap_items[usize::from(index)]);
let catalog_entry
= catalogue.into_iter()
.find(|x| x.name == uap_data_item);
let data_item_def
= match catalog_entry {
Some(d) => d,
None => { return Err(ParseError::UapDefinitionNotFound { uap_name: uap_data_item}.into()); },
};
data_items_map.insert(*frn.0, data_item_def);
}
Ok(data_items_map)
}
fn list_present_data_item_definitions(data_items_map: &BTreeMap<u8, &DataItemRule>) {
println!();
println!("Data items present in the given message");
for present_data_item in data_items_map {
let p_frn = present_data_item.0;
let p_data_item_rule = present_data_item.1;
println!("FRN: {:02}: {}, {}", p_frn, p_data_item_rule.name, p_data_item_rule.title );
}
println!();
}