sync
This commit is contained in:
parent
c3323841d5
commit
ccfe71f852
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
||||
/target
|
||||
|
||||
/tmp-out
|
||||
@ -23,7 +23,16 @@ impl EntryLoader for BTableLoader {
|
||||
raw.get(index)
|
||||
.map(|s| {
|
||||
let mut s = s.to_string();
|
||||
s.retain(|c| c.is_alphanumeric());
|
||||
s.retain(|c| {
|
||||
c.is_alphanumeric()
|
||||
|| c == '-'
|
||||
|| c == '.'
|
||||
|| c == '+'
|
||||
|| c == 'e'
|
||||
|| c == 'E'
|
||||
|| c == '_'
|
||||
|| c == '/'
|
||||
});
|
||||
s
|
||||
})
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing field at index {}", index))
|
||||
|
||||
@ -2,8 +2,6 @@ pub mod config;
|
||||
pub mod fr;
|
||||
#[cfg(feature = "opera")]
|
||||
pub mod opera;
|
||||
#[cfg(feature = "opera")]
|
||||
pub use opera::bitmap::OPERABitmap;
|
||||
pub mod pattern;
|
||||
pub mod prelude;
|
||||
pub mod tables;
|
||||
@ -15,14 +13,13 @@ use ph::fmph::GOFunction;
|
||||
use rkyv::api::high::HighValidator;
|
||||
use rkyv::bytecheck::CheckBytes;
|
||||
use rkyv::rancor::Error;
|
||||
use rkyv::{Archive, Archived, Deserialize, Serialize};
|
||||
use rkyv::{Archive, Deserialize, Serialize};
|
||||
use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize};
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::fmt::Debug;
|
||||
use std::io::{Cursor, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::tables::{TableEntry, TableEntryFull, TableTypeTrait};
|
||||
use rkyv::{api::high::to_bytes_with_alloc, ser::allocator::Arena};
|
||||
use crate::tables::{TableEntryFull, TableTypeTrait};
|
||||
|
||||
pub trait TableConverter {
|
||||
type OutputEntry: TableEntryFull;
|
||||
@ -159,13 +156,13 @@ where
|
||||
)]
|
||||
#[rkyv(compare(PartialEq), derive(Debug, Clone, Copy))]
|
||||
pub struct FXY {
|
||||
pub f: u16,
|
||||
pub x: u16,
|
||||
pub y: u16,
|
||||
pub f: i32,
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
}
|
||||
|
||||
impl FXY {
|
||||
pub fn new(f: u16, x: u16, y: u16) -> Self {
|
||||
pub fn new(f: i32, x: i32, y: i32) -> Self {
|
||||
FXY { f, x, y }
|
||||
}
|
||||
pub fn from_str(fxy_str: &str) -> anyhow::Result<Self> {
|
||||
@ -176,14 +173,14 @@ impl FXY {
|
||||
}
|
||||
|
||||
let f = fxy_str[0..2]
|
||||
.parse::<u16>()
|
||||
.parse::<i32>()
|
||||
.with_context(|| format!("Failed to parse F from FXY: {}", fxy_str))?;
|
||||
|
||||
let x = fxy_str[2..4]
|
||||
.parse::<u16>()
|
||||
.parse::<i32>()
|
||||
.with_context(|| format!("Failed to parse X from FXY: {}", fxy_str))?;
|
||||
let y = fxy_str[4..6]
|
||||
.parse::<u16>()
|
||||
.parse::<i32>()
|
||||
.with_context(|| format!("Failed to parse Y from FXY: {}", fxy_str))?;
|
||||
|
||||
Ok(FXY { f, x, y })
|
||||
@ -195,14 +192,13 @@ impl FXY {
|
||||
((self.f as u32) << 14) | ((self.x as u32) << 8) | (self.y as u32)
|
||||
}
|
||||
|
||||
/// Convert u32 back to FXY
|
||||
pub fn from_u32(value: u32) -> Self {
|
||||
FXY {
|
||||
f: ((value >> 14) & 0x3) as u16,
|
||||
x: ((value >> 8) & 0x3F) as u16,
|
||||
y: (value & 0xFF) as u16,
|
||||
}
|
||||
}
|
||||
// pub fn from_u32(value: u32) -> Self {
|
||||
// FXY {
|
||||
// f: ((value >> 14) & 0x3) as u16,
|
||||
// x: ((value >> 8) & 0x3F) as u16,
|
||||
// y: (value & 0xFF) as u16,
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
pub struct BUFRTableMPH<T: TableTypeTrait> {
|
||||
@ -248,14 +244,18 @@ where
|
||||
pub enum TableType {
|
||||
B,
|
||||
D,
|
||||
BitMap,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
BUFRTableMPH, BufrTableMph, FXY,
|
||||
BUFRTableMPH,
|
||||
BufrTableMph,
|
||||
FXY,
|
||||
// wmo::{TableLoader, btable::BTableCsvLoader},
|
||||
fr::{TableLoader, btable::BTableLoader as BTableCsvLoader},
|
||||
prelude::{BUFRTableB, BUFRTableD},
|
||||
wmo::{TableLoader, btable::BTableCsvLoader},
|
||||
};
|
||||
|
||||
#[test]
|
||||
@ -263,7 +263,8 @@ mod test {
|
||||
let table_loader = TableLoader::<BTableCsvLoader>::default();
|
||||
BUFRTableB::build_from_csv(
|
||||
table_loader,
|
||||
"/Users/xiang.li1/projects/rbufr/BUFR4/BUFRCREX_TableB_en_42.csv",
|
||||
// "/Users/xiang.li1/projects/rbufr/BUFR4/BUFRCREX_TableB_en_42.csv",
|
||||
"/Users/xiang.li1/Downloads/tables 2/bufrtabb_16.csv",
|
||||
"./test.bufrtbl",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@ -6,6 +6,8 @@ use genlib::{
|
||||
pattern::{TableKind, TableScanner},
|
||||
prelude::{BUFRTableB, BUFRTableD},
|
||||
};
|
||||
#[cfg(feature = "opera")]
|
||||
use genlib::{BUFRTableMPH, opera, tables::BitMap};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Parser)]
|
||||
@ -78,6 +80,28 @@ enum Commands {
|
||||
#[arg(short, long, default_value = "scan-config.toml")]
|
||||
output: PathBuf,
|
||||
},
|
||||
/// Convert Opera bitmap file to BUFR format
|
||||
#[cfg(feature = "opera")]
|
||||
ConvertOperaBitmap {
|
||||
/// Input Opera bitmap CSV file
|
||||
#[arg(short, long)]
|
||||
input: PathBuf,
|
||||
|
||||
/// Output path (without extension)
|
||||
#[arg(short, long)]
|
||||
output: PathBuf,
|
||||
},
|
||||
/// Print Opera bitmap table
|
||||
#[cfg(feature = "opera")]
|
||||
PrintOperaBitmap {
|
||||
/// Path to Opera bitmap .bufrtbl file (without extension)
|
||||
#[arg(short, long)]
|
||||
input: PathBuf,
|
||||
|
||||
/// Maximum number of entries to print (optional)
|
||||
#[arg(short, long)]
|
||||
limit: Option<usize>,
|
||||
},
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
@ -111,6 +135,14 @@ fn main() -> Result<()> {
|
||||
Commands::GenConfig { output } => {
|
||||
generate_config_file(&output)?;
|
||||
}
|
||||
#[cfg(feature = "opera")]
|
||||
Commands::ConvertOperaBitmap { input, output } => {
|
||||
convert_opera_bitmap(&input, &output)?;
|
||||
}
|
||||
#[cfg(feature = "opera")]
|
||||
Commands::PrintOperaBitmap { input, limit } => {
|
||||
print_opera_bitmap(&input, limit)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -477,3 +509,49 @@ fn generate_config_file(output_path: &Path) -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "opera")]
|
||||
fn convert_opera_bitmap(input_path: &Path, output_path: &Path) -> Result<()> {
|
||||
println!(
|
||||
"Converting Opera bitmap from {} to {}",
|
||||
input_path.display(),
|
||||
output_path.display()
|
||||
);
|
||||
|
||||
let loader = opera::TableLoader {};
|
||||
BUFRTableMPH::<BitMap>::build_from_csv(loader, input_path, output_path)?;
|
||||
|
||||
println!("Conversion completed successfully!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "opera")]
|
||||
fn print_opera_bitmap(input_path: &Path, limit: Option<usize>) -> Result<()> {
|
||||
println!("Loading Opera bitmap from: {}", input_path.display());
|
||||
|
||||
let table = BUFRTableMPH::<BitMap>::load_from_disk(input_path)?;
|
||||
let entries = table.get_all_entries();
|
||||
|
||||
println!("\nOpera Bitmap Entries (Total: {})", entries.len());
|
||||
println!("{}", "=".repeat(60));
|
||||
println!("{:<10} | {}", "FXY", "Depth");
|
||||
println!("{}", "-".repeat(60));
|
||||
|
||||
let display_entries = if let Some(max) = limit {
|
||||
&entries[..entries.len().min(max)]
|
||||
} else {
|
||||
&entries[..]
|
||||
};
|
||||
|
||||
for entry in display_entries {
|
||||
println!("{}", entry);
|
||||
}
|
||||
|
||||
if let Some(max) = limit {
|
||||
if entries.len() > max {
|
||||
println!("\n... ({} more entries omitted)", entries.len() - max);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1,82 +0,0 @@
|
||||
use csv::ReaderBuilder;
|
||||
use std::{fs::File, path::Path};
|
||||
|
||||
pub struct OPERABitmap {
|
||||
bitmap: Vec<OPERABitmapEntry>,
|
||||
}
|
||||
|
||||
impl OPERABitmap {
|
||||
pub fn load<P: AsRef<Path>>(p: P) -> anyhow::Result<Self> {
|
||||
let mut rdr = ReaderBuilder::new()
|
||||
.has_headers(false)
|
||||
.delimiter(b';')
|
||||
.from_reader(File::open(p)?);
|
||||
|
||||
let mut results = default_bitmap();
|
||||
let mut line: usize = 0;
|
||||
|
||||
for result in rdr.records() {
|
||||
let record = result?;
|
||||
|
||||
let parse_field = |idx: usize| {
|
||||
record
|
||||
.get(idx)
|
||||
.map(|s| s.trim().to_string())
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("Parse Opera Bitmap File failed at index {}", idx)
|
||||
})
|
||||
};
|
||||
|
||||
let f = parse_field(0)?.parse()?;
|
||||
let x = parse_field(1)?.parse()?;
|
||||
let y = parse_field(2)?.parse()?;
|
||||
let dw = parse_field(3)?.parse()?;
|
||||
|
||||
let entry = OPERABitmapEntry {
|
||||
f,
|
||||
x,
|
||||
y,
|
||||
datawidth_bits: dw,
|
||||
};
|
||||
if let Some(line) = results.get_mut(line) {
|
||||
*line = entry;
|
||||
} else {
|
||||
results.push(entry);
|
||||
}
|
||||
|
||||
line += 1;
|
||||
}
|
||||
|
||||
Ok(OPERABitmap { bitmap: results })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OPERABitmapEntry {
|
||||
pub f: u16,
|
||||
pub x: u16,
|
||||
pub y: u16,
|
||||
pub datawidth_bits: u8,
|
||||
}
|
||||
|
||||
fn default_bitmap() -> Vec<OPERABitmapEntry> {
|
||||
const VALUES: [(u16, u16, u16, u8); 8] = [
|
||||
(3, 21, 192, 1),
|
||||
(3, 21, 193, 1),
|
||||
(3, 21, 194, 1),
|
||||
(3, 21, 195, 1),
|
||||
(3, 21, 196, 1),
|
||||
(3, 21, 197, 1),
|
||||
(3, 21, 200, 2),
|
||||
(3, 21, 202, 2),
|
||||
];
|
||||
|
||||
VALUES
|
||||
.iter()
|
||||
.map(|(f, x, y, dw)| OPERABitmapEntry {
|
||||
f: *f,
|
||||
x: *x,
|
||||
y: *y,
|
||||
datawidth_bits: *dw,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -1 +1,51 @@
|
||||
pub mod bitmap;
|
||||
use crate::{
|
||||
FXY, TableConverter, TableType,
|
||||
tables::{BitMap, BitMapEntry},
|
||||
};
|
||||
|
||||
use csv::ReaderBuilder;
|
||||
|
||||
pub struct TableLoader {}
|
||||
|
||||
impl TableConverter for TableLoader {
|
||||
type OutputEntry = BitMapEntry;
|
||||
type TableType = BitMap;
|
||||
|
||||
fn convert<P: AsRef<std::path::Path>>(
|
||||
&self,
|
||||
path: P,
|
||||
) -> anyhow::Result<Vec<Self::OutputEntry>> {
|
||||
let mut rdr = ReaderBuilder::new()
|
||||
.has_headers(false)
|
||||
.delimiter(b';')
|
||||
.flexible(true) // Allow variable number of fields
|
||||
.from_path(path.as_ref())?;
|
||||
|
||||
let mut entries = vec![];
|
||||
|
||||
for result in rdr.records() {
|
||||
let record = result?;
|
||||
|
||||
let parse_field = |idx: usize| {
|
||||
record
|
||||
.get(idx)
|
||||
.map(|s| s.trim().to_string())
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("Parse Opera Bitmap File failed at index {}", idx)
|
||||
})
|
||||
};
|
||||
|
||||
let f = parse_field(0)?.parse()?;
|
||||
let x = parse_field(1)?.parse()?;
|
||||
let y = parse_field(2)?.parse()?;
|
||||
let dw = parse_field(3)?.parse()?;
|
||||
|
||||
let entry = BitMapEntry {
|
||||
fxy: FXY::new(f, x, y),
|
||||
depth: dw,
|
||||
};
|
||||
entries.push(entry);
|
||||
}
|
||||
Ok(entries)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ use crate::tables::DTable;
|
||||
pub use crate::wmo;
|
||||
pub type BUFRTableD = crate::BUFRTableMPH<DTable>;
|
||||
pub type BUFRTableB = crate::BUFRTableMPH<BTable>;
|
||||
pub type BUFRTableBitMap = crate::BUFRTableMPH<crate::tables::BitMap>;
|
||||
pub use crate::BUFRTableMPH;
|
||||
pub use crate::FXY;
|
||||
pub use crate::TableType;
|
||||
|
||||
@ -7,10 +7,13 @@ use rkyv::rancor::{Error, Strategy};
|
||||
use serde::Serialize as SerdeSerialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::fmt::{Debug, Display};
|
||||
use toml::Table;
|
||||
|
||||
pub struct BTable;
|
||||
pub struct DTable;
|
||||
|
||||
pub struct BitMap;
|
||||
|
||||
pub trait TableTypeTrait
|
||||
where
|
||||
<Self::EntryType as Archive>::Archived: for<'a> CheckBytes<HighValidator<'a, Error>>,
|
||||
@ -28,6 +31,11 @@ impl TableTypeTrait for DTable {
|
||||
const TABLE_TYPE: crate::TableType = crate::TableType::D;
|
||||
}
|
||||
|
||||
impl TableTypeTrait for BitMap {
|
||||
type EntryType = crate::tables::BitMapEntry;
|
||||
const TABLE_TYPE: crate::TableType = crate::TableType::BitMap;
|
||||
}
|
||||
|
||||
pub trait TableEntry:
|
||||
SerdeSerialize
|
||||
+ DeserializeOwned
|
||||
@ -295,6 +303,41 @@ impl Display for ArchivedDTableEntry {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Clone, serde::Deserialize, serde::Serialize, Archive, rkyv::Serialize, rkyv::Deserialize,
|
||||
)]
|
||||
#[rkyv(compare(PartialEq), derive(Debug))]
|
||||
pub struct BitMapEntry {
|
||||
pub fxy: FXY,
|
||||
pub depth: u8,
|
||||
}
|
||||
|
||||
impl Display for BitMapEntry {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:02}{:02}{:03} | Depth: {}",
|
||||
self.fxy.f, self.fxy.x, self.fxy.y, self.depth
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ArchivedBitMapEntry {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:02}{:02}{:03} | Depth: {}",
|
||||
self.fxy.f, self.fxy.x, self.fxy.y, self.depth
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl TableEntry for BitMapEntry {
|
||||
fn fxy(&self) -> FXY {
|
||||
self.fxy
|
||||
}
|
||||
}
|
||||
|
||||
impl TableEntry for DTableEntry {
|
||||
fn fxy(&self) -> FXY {
|
||||
self.fxy
|
||||
|
||||
@ -16,3 +16,7 @@ serde = { version = "1.0.228", features = ["derive"] }
|
||||
thiserror = "2.0.17"
|
||||
gentools = { path = "../gen" }
|
||||
anyhow = "1.0.100"
|
||||
|
||||
[features]
|
||||
default = ["opera"]
|
||||
opera = ["gentools/opera"]
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
use genlib::BUFRTableMPH;
|
||||
#[cfg(feature = "opera")]
|
||||
use genlib::prelude::BUFRTableBitMap;
|
||||
use genlib::tables::TableTypeTrait;
|
||||
|
||||
use crate::errors::Result;
|
||||
#[cfg(feature = "opera")]
|
||||
use crate::structs::GENCENTER;
|
||||
use crate::structs::data_parser::DataParser;
|
||||
use crate::structs::versions::{BUFRMessage, MessageVersion};
|
||||
use crate::tables::*;
|
||||
@ -52,7 +56,25 @@ impl MessageBlock {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let mut parser = DataParser::new(master_b, master_d, local_b, local_d);
|
||||
#[cfg(feature = "opera")]
|
||||
let opera_bitmap_table = self
|
||||
.load_opera_bitmap_table(
|
||||
table_info.center_id,
|
||||
GENCENTER,
|
||||
table_info.local_table_version,
|
||||
master_table_version,
|
||||
)
|
||||
.ok();
|
||||
|
||||
let mut parser = DataParser::new(
|
||||
self.message.version(),
|
||||
master_b,
|
||||
master_d,
|
||||
local_b,
|
||||
local_d,
|
||||
#[cfg(feature = "opera")]
|
||||
opera_bitmap_table,
|
||||
);
|
||||
|
||||
parser.parse(&self.message)?;
|
||||
|
||||
@ -77,6 +99,22 @@ impl MessageBlock {
|
||||
})
|
||||
.ok_or(crate::errors::Error::TableNotFoundEmpty)
|
||||
}
|
||||
|
||||
#[cfg(feature = "opera")]
|
||||
fn load_opera_bitmap_table(
|
||||
&self,
|
||||
subcenter: u16,
|
||||
center: u16,
|
||||
local_version: u8,
|
||||
master_version: u8,
|
||||
) -> Result<BUFRTableBitMap> {
|
||||
TableLoader.load_table(BitmapTable::new(
|
||||
center,
|
||||
subcenter,
|
||||
local_version,
|
||||
master_version,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BUFRFile {
|
||||
|
||||
@ -1,3 +1,18 @@
|
||||
use crate::structs::data_parser::Value;
|
||||
|
||||
/// This Module contains functions specific to handling BUFR Opera files.
|
||||
|
||||
pub struct OperaBitmapParser;
|
||||
pub struct OperaBitmapParser {
|
||||
values: Vec<Value>,
|
||||
dw: u8,
|
||||
}
|
||||
|
||||
impl OperaBitmapParser {
|
||||
pub fn new(dw: u8) -> Self {
|
||||
OperaBitmapParser { values: vec![], dw }
|
||||
}
|
||||
|
||||
pub fn values(&mut self) -> &mut Vec<Value> {
|
||||
&mut self.values
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +1,22 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::{
|
||||
errors::{Error, Result},
|
||||
structs::versions::MessageVersion,
|
||||
};
|
||||
#[cfg(feature = "opera")]
|
||||
use genlib::tables::ArchivedBitMapEntry;
|
||||
use genlib::{
|
||||
FXY, opera,
|
||||
prelude::{BUFRTableB, BUFRTableD},
|
||||
prelude::{BUFRTableB, BUFRTableBitMap, BUFRTableD},
|
||||
tables::{ArchivedBTableEntry, ArchivedDTableEntry, BTableEntry},
|
||||
};
|
||||
use serde::de;
|
||||
|
||||
const MISS_VAL: f64 = 99999.999999;
|
||||
|
||||
pub struct DataParser {
|
||||
bufr_edition: u8,
|
||||
master_b: BUFRTableB,
|
||||
master_d: BUFRTableD,
|
||||
// local
|
||||
@ -17,22 +25,28 @@ pub struct DataParser {
|
||||
// Common State
|
||||
common_scale: Option<i32>,
|
||||
common_ref_value: Option<i32>,
|
||||
common_data_width: Option<u32>,
|
||||
common_data_width: Option<i32>,
|
||||
common_str_width: Option<usize>,
|
||||
// Localized State
|
||||
local_data_width: Option<u32>,
|
||||
local_data_width: Option<i32>,
|
||||
// Temporary storage
|
||||
temp_operator: Option<u32>,
|
||||
temp_operator: Option<i32>,
|
||||
// OPERA Bitmap Table
|
||||
#[cfg(feature = "opera")]
|
||||
opera_bitmap_table: Option<BUFRTableBitMap>,
|
||||
}
|
||||
|
||||
impl DataParser {
|
||||
pub fn new(
|
||||
edition: u8,
|
||||
master_b: BUFRTableB,
|
||||
master_d: BUFRTableD,
|
||||
local_b: Option<BUFRTableB>,
|
||||
local_d: Option<BUFRTableD>,
|
||||
#[cfg(feature = "opera")] opera_bitmap_table: Option<BUFRTableBitMap>,
|
||||
) -> Self {
|
||||
DataParser {
|
||||
bufr_edition: edition,
|
||||
master_b,
|
||||
master_d,
|
||||
local_b,
|
||||
@ -43,6 +57,8 @@ impl DataParser {
|
||||
common_str_width: None,
|
||||
local_data_width: None,
|
||||
temp_operator: None,
|
||||
#[cfg(feature = "opera")]
|
||||
opera_bitmap_table,
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,27 +80,24 @@ impl DataParser {
|
||||
fn parser_inner<'a>(
|
||||
&mut self,
|
||||
values: &mut Vec<Value>,
|
||||
mut descriptors: Vec<genlib::FXY>,
|
||||
mut data: BitInput<'a>,
|
||||
) -> Result<(Vec<genlib::FXY>, BitInput<'a>)> {
|
||||
mut descriptors: VecDeque<genlib::FXY>,
|
||||
data: BitInput<'a>,
|
||||
) -> Result<(VecDeque<genlib::FXY>, BitInput<'a>)> {
|
||||
if descriptors.is_empty() {
|
||||
return Ok((descriptors, data));
|
||||
}
|
||||
let des = descriptors[0];
|
||||
|
||||
let des = descriptors.pop_front().unwrap();
|
||||
println!("Processing descriptor {:?}", des);
|
||||
|
||||
match des.f {
|
||||
0 => {
|
||||
// Element descriptor - parse data
|
||||
if let Some(e) = self.lookup_b_descriptor(des) {
|
||||
// let (value, remaining) = e.parse(data);
|
||||
let (value, remaining) = self.evalute(data, &e)?;
|
||||
|
||||
println!("Parsed value: {}", value);
|
||||
println!("Evaluated descriptor {}\nTo value {}", e, value);
|
||||
values.push(value);
|
||||
data = remaining;
|
||||
descriptors.remove(0);
|
||||
return Ok((descriptors, remaining));
|
||||
} else {
|
||||
return Err(Error::ParseError(format!(
|
||||
"Descriptor {:?} not found in Table B",
|
||||
@ -94,27 +107,57 @@ impl DataParser {
|
||||
}
|
||||
1 => {
|
||||
let genlib::FXY { x, y, .. } = des;
|
||||
descriptors.remove(0);
|
||||
let (mut descriptors, mut data, x, y) = if y == 0 {
|
||||
// Delayed repetition: parse the next descriptor to get repeat count
|
||||
let (descriptors, updated_data) =
|
||||
self.parser_inner(values, descriptors, data)?;
|
||||
let count = if let Some(count) = values.pop() {
|
||||
let count = count.as_f64().ok_or_else(|| {
|
||||
Error::ParseError("Expected numeric value for repeat count".to_string())
|
||||
})?;
|
||||
count.floor() as usize
|
||||
} else {
|
||||
return Err(Error::ParseError(format!(
|
||||
"Expected UInt value for repeat count"
|
||||
)));
|
||||
};
|
||||
|
||||
for i in 0..y {
|
||||
let descriptors_clone = descriptors.clone();
|
||||
let (cde, cd) =
|
||||
self.repeat_parser(values, descriptors_clone, data, x as usize)?;
|
||||
(descriptors, updated_data, x as usize, count)
|
||||
} else {
|
||||
(descriptors, data, x as usize, y as usize)
|
||||
};
|
||||
|
||||
if i == y - 1 {
|
||||
descriptors = cde;
|
||||
if x > descriptors.len() {
|
||||
return Err(Error::ParseError(format!(
|
||||
"Not enough descriptors to repeat: requested {}, available {}",
|
||||
x,
|
||||
descriptors.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let seq = descriptors.split_off(x);
|
||||
|
||||
for _ in 0..y {
|
||||
let mut descriptors_clone = descriptors.clone();
|
||||
while !descriptors_clone.is_empty() {
|
||||
let (_desc, cd) = self.parser_inner(values, descriptors_clone, data)?;
|
||||
descriptors_clone = _desc;
|
||||
data = cd;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok((seq, data));
|
||||
}
|
||||
2 => {
|
||||
let data = self.deal_with_operator(values, des, data)?;
|
||||
descriptors.remove(0);
|
||||
return self.parser_inner(values, descriptors, data);
|
||||
return Ok((descriptors, data));
|
||||
}
|
||||
3 => {
|
||||
#[cfg(feature = "opera")]
|
||||
let opera_dw = self.parse_opera_bitmap(des).map(|e| e.depth);
|
||||
|
||||
if let Some(seq) = self.lookup_d_descriptor(des) {
|
||||
let mut fxy_chain: Vec<FXY> = seq
|
||||
let mut fxy_chain: VecDeque<FXY> = seq
|
||||
.fxy_chain
|
||||
.iter()
|
||||
.map(|f| {
|
||||
@ -123,8 +166,16 @@ impl DataParser {
|
||||
result
|
||||
})
|
||||
.collect();
|
||||
fxy_chain.extend(descriptors[1..].into_iter());
|
||||
descriptors = fxy_chain;
|
||||
|
||||
#[cfg(feature = "opera")]
|
||||
if opera_dw.is_some() {
|
||||
let (_, data) =
|
||||
self.parse_opera_array(opera_dw.unwrap(), fxy_chain, data)?;
|
||||
return Ok((descriptors, data));
|
||||
} else {
|
||||
fxy_chain.extend(descriptors);
|
||||
return Ok((fxy_chain, data));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::ParseError(format!(
|
||||
"Sequence descriptor {:?} not found in Table D",
|
||||
@ -182,20 +233,6 @@ impl DataParser {
|
||||
.or(self.local_d.as_ref().and_then(|t| t.lookup(fxy)))
|
||||
}
|
||||
|
||||
fn repeat_parser<'a>(
|
||||
&mut self,
|
||||
values: &mut Vec<Value>,
|
||||
descriptors: Vec<genlib::FXY>,
|
||||
data: BitInput<'a>,
|
||||
count: usize,
|
||||
) -> Result<(Vec<genlib::FXY>, BitInput<'a>)> {
|
||||
if count == 0 || descriptors.is_empty() {
|
||||
return Ok((descriptors, data));
|
||||
}
|
||||
let (desc, data) = self.parser_inner(values, descriptors, data)?;
|
||||
return self.repeat_parser(values, desc, data, count - 1);
|
||||
}
|
||||
|
||||
fn evalute<'a>(
|
||||
&self,
|
||||
data: BitInput<'a>,
|
||||
@ -210,19 +247,89 @@ impl DataParser {
|
||||
return Ok((Value::String(s), data));
|
||||
}
|
||||
_ => {
|
||||
let datawidth = self
|
||||
.common_data_width
|
||||
.unwrap_or(e.bufr_datawidth_bits.to_native());
|
||||
|
||||
let scale = 10f32.powi(-self.common_scale.unwrap_or(e.bufr_scale.to_native()));
|
||||
let reference_value =
|
||||
self.common_ref_value
|
||||
.unwrap_or(e.bufr_reference_value.to_native()) as f32;
|
||||
|
||||
let datawidth = self.datawidth(e);
|
||||
let scale = self.scale(e) as f64;
|
||||
let reference_value = self.reference_value(e) as f64;
|
||||
let (value, data) = data.get_arbitary_bits(datawidth as usize)?;
|
||||
let result = (value as f32) / scale + reference_value;
|
||||
return Ok((Value::Float(result), data));
|
||||
let mv = (1 << datawidth) - 1;
|
||||
if value == mv && e.fxy.x != 31 {
|
||||
return Ok((Value::Missing, data));
|
||||
}
|
||||
let result = ((value as f64) + reference_value) * 10.0f64.powi(-scale as i32);
|
||||
return Ok((Value::Number(result), data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn no_change(&self, e: &ArchivedBTableEntry) -> bool {
|
||||
let is_flag = match e.bufr_unit.as_str() {
|
||||
"flag table" | "flag-table" => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let is_code = match e.bufr_unit.as_str() {
|
||||
"code table" | "code-table" => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let delay_repeat_count = match (e.fxy.f.to_native(), e.fxy.x.to_native()) {
|
||||
(0, 31) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
is_flag || is_code || delay_repeat_count
|
||||
}
|
||||
|
||||
fn datawidth(&self, e: &ArchivedBTableEntry) -> u32 {
|
||||
let v = if self.no_change(e) {
|
||||
e.bufr_datawidth_bits.to_native()
|
||||
} else {
|
||||
self.common_data_width
|
||||
.map(|c| {
|
||||
let (v, _) = e
|
||||
.bufr_datawidth_bits
|
||||
.to_native()
|
||||
.overflowing_add_signed(c - 128);
|
||||
v
|
||||
})
|
||||
.unwrap_or(e.bufr_datawidth_bits.to_native())
|
||||
};
|
||||
|
||||
if self.temp_operator.is_some() {
|
||||
e.bufr_datawidth_bits.to_native() + (10 * self.temp_operator.unwrap()) as u32
|
||||
} else {
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
fn scale(&self, e: &ArchivedBTableEntry) -> i32 {
|
||||
let v = if self.no_change(e) {
|
||||
e.bufr_scale.to_native()
|
||||
} else {
|
||||
self.common_scale
|
||||
.map(|c| {
|
||||
let (v, _) = e.bufr_scale.to_native().overflowing_add(128 - c);
|
||||
v
|
||||
})
|
||||
.unwrap_or(e.bufr_scale.to_native())
|
||||
};
|
||||
|
||||
if self.temp_operator.is_some() {
|
||||
e.bufr_scale.to_native() + self.temp_operator.unwrap()
|
||||
} else {
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
fn reference_value(&self, e: &ArchivedBTableEntry) -> i32 {
|
||||
let v = e.bufr_reference_value.to_native();
|
||||
|
||||
if self.temp_operator.is_some() {
|
||||
(e.bufr_reference_value.to_native() as f32 * 10_f32.powi(self.temp_operator.unwrap()))
|
||||
as i32
|
||||
} else {
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,7 +345,7 @@ impl DataParser {
|
||||
self.common_data_width = None;
|
||||
}
|
||||
_ => {
|
||||
self.common_data_width = Some(operator.y as u32);
|
||||
self.common_data_width = Some(operator.y);
|
||||
}
|
||||
},
|
||||
2 => match operator.y {
|
||||
@ -264,10 +371,10 @@ impl DataParser {
|
||||
|
||||
6 => {
|
||||
let localized_width = operator.y;
|
||||
self.local_data_width = Some(localized_width as u32);
|
||||
self.local_data_width = Some(localized_width);
|
||||
}
|
||||
7 => {
|
||||
self.temp_operator = Some(operator.y as u32);
|
||||
self.temp_operator = Some(operator.y);
|
||||
}
|
||||
8 => match operator.y {
|
||||
0 => {
|
||||
@ -283,26 +390,75 @@ impl DataParser {
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
#[cfg(feature = "opera")]
|
||||
fn parse_opera_bitmap(&self, des: FXY) -> Option<&ArchivedBitMapEntry> {
|
||||
self.opera_bitmap_table
|
||||
.as_ref()
|
||||
.map(|t| t.lookup(des))
|
||||
.flatten()
|
||||
}
|
||||
|
||||
#[cfg(feature = "opera")]
|
||||
fn parse_opera_array<'a>(
|
||||
&mut self,
|
||||
dw: u8,
|
||||
mut descs: VecDeque<FXY>,
|
||||
mut data: BitInput<'a>,
|
||||
) -> Result<(VecDeque<FXY>, BitInput<'a>)> {
|
||||
use crate::opera::OperaBitmapParser;
|
||||
|
||||
let mut opera_bitmap = OperaBitmapParser::new(dw);
|
||||
|
||||
while !descs.is_empty() {
|
||||
let (_descs, _data) = self.parser_inner(opera_bitmap.values(), descs, data)?;
|
||||
descs = _descs;
|
||||
data = _data;
|
||||
}
|
||||
Ok((descs, data))
|
||||
}
|
||||
|
||||
// fn seq_parser(descriptors: &[genlib::FXY]) -> Result<()> {}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
pub enum Value {
|
||||
Float(f32),
|
||||
Double(f64),
|
||||
Int(i32),
|
||||
UInt(u32),
|
||||
Number(f64),
|
||||
Missing,
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Value::Float(v) => write!(f, "{}", v),
|
||||
Value::Double(v) => write!(f, "{}", v),
|
||||
Value::Int(v) => write!(f, "{}", v),
|
||||
Value::UInt(v) => write!(f, "{}", v),
|
||||
Value::Number(v) => write!(f, "{}", v),
|
||||
Value::String(v) => write!(f, "{}", v),
|
||||
Value::Missing => write!(f, "MISSING"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn as_f64(&self) -> Option<f64> {
|
||||
match self {
|
||||
Value::Number(v) => Some(*v),
|
||||
Value::Missing => Some(MISS_VAL),
|
||||
Value::String(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
match self {
|
||||
Value::String(v) => Some(v),
|
||||
Value::Number(_) => None,
|
||||
Value::Missing => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> Option<Vec<u8>> {
|
||||
match self {
|
||||
Value::String(_) => None,
|
||||
Value::Number(n) => Some(n.to_le_bytes().to_vec()),
|
||||
Value::Missing => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -335,12 +491,12 @@ impl<'a> BitInput<'a> {
|
||||
Ok((s, remaining_input))
|
||||
}
|
||||
|
||||
pub fn get_arbitary_bits(self, nbits: usize) -> Result<(u32, BitInput<'a>)> {
|
||||
pub fn get_arbitary_bits(self, nbits: usize) -> Result<(u64, BitInput<'a>)> {
|
||||
if nbits == 0 {
|
||||
return Ok((0, self));
|
||||
}
|
||||
|
||||
let mut value: u32 = 0;
|
||||
let mut value: u64 = 0;
|
||||
let mut remaining_bits = nbits;
|
||||
let mut bit_offset = self.1; // Current bit position in the first byte (0-7)
|
||||
let mut byte_data = self.0; // Remaining bytes
|
||||
@ -364,7 +520,7 @@ impl<'a> BitInput<'a> {
|
||||
let extracted_bits = (current_byte >> shift) & mask;
|
||||
|
||||
// Add to value
|
||||
value = (value << bits_to_read) | extracted_bits as u32;
|
||||
value = (value << bits_to_read) | extracted_bits as u64;
|
||||
|
||||
remaining_bits -= bits_to_read;
|
||||
bit_offset += bits_to_read;
|
||||
|
||||
@ -8,6 +8,9 @@ pub mod data_parser;
|
||||
pub(super) mod tools;
|
||||
pub mod versions;
|
||||
|
||||
#[cfg(feature = "opera")]
|
||||
pub(super) const GENCENTER: u16 = 247;
|
||||
|
||||
#[inline]
|
||||
pub fn skip(n: usize) -> impl Fn(&[u8]) -> IResult<&[u8], ()> {
|
||||
move |input: &[u8]| {
|
||||
|
||||
@ -1,18 +1,20 @@
|
||||
use crate::errors::{Error, Result};
|
||||
use crate::structs::bit::{self, BitInput, parse_arbitrary_bits};
|
||||
use nom::{IResult, Parser, multi::many1};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub(super) fn parse_descriptors(input: &[u8]) -> Result<Vec<genlib::FXY>> {
|
||||
use crate::errors::{Error, Result};
|
||||
use crate::structs::bit::{BitInput, parse_arbitrary_bits};
|
||||
use nom::IResult;
|
||||
|
||||
pub(super) fn parse_descriptors(input: &[u8]) -> Result<VecDeque<genlib::FXY>> {
|
||||
parse_descriptors_inner(input)
|
||||
.map(|(_, v)| v)
|
||||
.map_err(|_| Error::ParseError(format!("Can't parse descriptors from section3")))
|
||||
}
|
||||
|
||||
fn parse_descriptors_inner(mut input: &[u8]) -> IResult<BitInput, Vec<genlib::FXY>> {
|
||||
let mut results = Vec::new();
|
||||
fn parse_descriptors_inner(mut input: &[u8]) -> IResult<BitInput, VecDeque<genlib::FXY>> {
|
||||
let mut results = VecDeque::new();
|
||||
while input.len() > 1 {
|
||||
let ((finput, _), fxy) = take_fxy((input, 0))?;
|
||||
results.push(fxy);
|
||||
results.push_back(fxy);
|
||||
input = finput;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
pub mod v2;
|
||||
pub mod v4;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub(super) use super::{skip, skip1, skip2};
|
||||
use crate::errors::{Error, Result};
|
||||
use genlib::FXY;
|
||||
@ -95,7 +97,7 @@ macro_rules! message {
|
||||
}
|
||||
}
|
||||
|
||||
fn descriptors(&self) -> Result<Vec<FXY>> {
|
||||
fn descriptors(&self) -> Result<VecDeque<FXY>> {
|
||||
match self {
|
||||
$(
|
||||
BUFRMessage::$version(msg) => msg.descriptors(),
|
||||
@ -161,7 +163,7 @@ pub trait MessageVersion: Sized {
|
||||
|
||||
fn ndescs(&self) -> usize;
|
||||
|
||||
fn descriptors(&self) -> Result<Vec<FXY>>;
|
||||
fn descriptors(&self) -> Result<VecDeque<FXY>>;
|
||||
|
||||
fn data_block(&self) -> Result<&[u8]>;
|
||||
}
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use genlib::FXY;
|
||||
use nom::{
|
||||
IResult,
|
||||
bytes::complete::{tag, take},
|
||||
@ -62,7 +65,7 @@ impl MessageVersion for BUFRMessageV2 {
|
||||
self.section3.data.len() / 2
|
||||
}
|
||||
|
||||
fn descriptors(&self) -> Result<Vec<genlib::FXY>> {
|
||||
fn descriptors(&self) -> Result<VecDeque<FXY>> {
|
||||
parse_descriptors(&self.section3.data)
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::errors::Result;
|
||||
use crate::structs::{tools::parse_descriptors, versions::MessageVersion};
|
||||
use nom::{
|
||||
@ -64,7 +66,7 @@ impl MessageVersion for BUFRMessageV4 {
|
||||
self.section3.data.len() / 2
|
||||
}
|
||||
|
||||
fn descriptors(&self) -> Result<Vec<genlib::FXY>> {
|
||||
fn descriptors(&self) -> Result<VecDeque<genlib::FXY>> {
|
||||
parse_descriptors(&self.section3.data)
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,25 @@ pub struct LocalTable {
|
||||
version: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BitmapTable {
|
||||
center: u16,
|
||||
subcenter: u16,
|
||||
local_version: u8,
|
||||
master_version: u8,
|
||||
}
|
||||
|
||||
impl BitmapTable {
|
||||
pub fn new(center: u16, subcenter: u16, local_version: u8, master_version: u8) -> Self {
|
||||
BitmapTable {
|
||||
center,
|
||||
subcenter,
|
||||
local_version,
|
||||
master_version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalTable {
|
||||
pub fn new(sub_center: Option<u16>, version: u8) -> Self {
|
||||
LocalTable {
|
||||
@ -83,6 +102,22 @@ impl TableTrait for LocalTable {
|
||||
}
|
||||
}
|
||||
|
||||
impl TableTrait for BitmapTable {
|
||||
fn file_path(&self, table_type: TableType) -> PathBuf {
|
||||
match table_type {
|
||||
TableType::BitMap => {
|
||||
let mut base_dir = PathBuf::new();
|
||||
base_dir.push("tables/opera");
|
||||
let file_name = format!("BUFR_Opera_Bitmap_{}.bufrtbl", self.center);
|
||||
base_dir.join(file_name)
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Table type not supported for BitmapTable")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TableLoader;
|
||||
|
||||
impl TableLoader {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user