This commit is contained in:
tsuki 2026-01-01 23:17:37 +08:00
parent c3323841d5
commit ccfe71f852
116 changed files with 547 additions and 185 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
/target /target
/tmp-out

View File

@ -23,7 +23,16 @@ impl EntryLoader for BTableLoader {
raw.get(index) raw.get(index)
.map(|s| { .map(|s| {
let mut s = s.to_string(); 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 s
}) })
.ok_or_else(|| anyhow::anyhow!("Missing field at index {}", index)) .ok_or_else(|| anyhow::anyhow!("Missing field at index {}", index))

View File

@ -2,8 +2,6 @@ pub mod config;
pub mod fr; pub mod fr;
#[cfg(feature = "opera")] #[cfg(feature = "opera")]
pub mod opera; pub mod opera;
#[cfg(feature = "opera")]
pub use opera::bitmap::OPERABitmap;
pub mod pattern; pub mod pattern;
pub mod prelude; pub mod prelude;
pub mod tables; pub mod tables;
@ -15,14 +13,13 @@ use ph::fmph::GOFunction;
use rkyv::api::high::HighValidator; use rkyv::api::high::HighValidator;
use rkyv::bytecheck::CheckBytes; use rkyv::bytecheck::CheckBytes;
use rkyv::rancor::Error; use rkyv::rancor::Error;
use rkyv::{Archive, Archived, Deserialize, Serialize}; use rkyv::{Archive, Deserialize, Serialize};
use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize}; use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize};
use std::fmt::{Debug, Display}; use std::fmt::Debug;
use std::io::{Cursor, Write}; use std::io::{Cursor, Write};
use std::path::Path; use std::path::Path;
use crate::tables::{TableEntry, TableEntryFull, TableTypeTrait}; use crate::tables::{TableEntryFull, TableTypeTrait};
use rkyv::{api::high::to_bytes_with_alloc, ser::allocator::Arena};
pub trait TableConverter { pub trait TableConverter {
type OutputEntry: TableEntryFull; type OutputEntry: TableEntryFull;
@ -159,13 +156,13 @@ where
)] )]
#[rkyv(compare(PartialEq), derive(Debug, Clone, Copy))] #[rkyv(compare(PartialEq), derive(Debug, Clone, Copy))]
pub struct FXY { pub struct FXY {
pub f: u16, pub f: i32,
pub x: u16, pub x: i32,
pub y: u16, pub y: i32,
} }
impl FXY { 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 } FXY { f, x, y }
} }
pub fn from_str(fxy_str: &str) -> anyhow::Result<Self> { pub fn from_str(fxy_str: &str) -> anyhow::Result<Self> {
@ -176,14 +173,14 @@ impl FXY {
} }
let f = fxy_str[0..2] let f = fxy_str[0..2]
.parse::<u16>() .parse::<i32>()
.with_context(|| format!("Failed to parse F from FXY: {}", fxy_str))?; .with_context(|| format!("Failed to parse F from FXY: {}", fxy_str))?;
let x = fxy_str[2..4] let x = fxy_str[2..4]
.parse::<u16>() .parse::<i32>()
.with_context(|| format!("Failed to parse X from FXY: {}", fxy_str))?; .with_context(|| format!("Failed to parse X from FXY: {}", fxy_str))?;
let y = fxy_str[4..6] let y = fxy_str[4..6]
.parse::<u16>() .parse::<i32>()
.with_context(|| format!("Failed to parse Y from FXY: {}", fxy_str))?; .with_context(|| format!("Failed to parse Y from FXY: {}", fxy_str))?;
Ok(FXY { f, x, y }) 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) ((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 {
pub fn from_u32(value: u32) -> Self { // FXY {
FXY { // f: ((value >> 14) & 0x3) as u16,
f: ((value >> 14) & 0x3) as u16, // x: ((value >> 8) & 0x3F) as u16,
x: ((value >> 8) & 0x3F) as u16, // y: (value & 0xFF) as u16,
y: (value & 0xFF) as u16, // }
} // }
}
} }
pub struct BUFRTableMPH<T: TableTypeTrait> { pub struct BUFRTableMPH<T: TableTypeTrait> {
@ -248,14 +244,18 @@ where
pub enum TableType { pub enum TableType {
B, B,
D, D,
BitMap,
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{ use crate::{
BUFRTableMPH, BufrTableMph, FXY, BUFRTableMPH,
BufrTableMph,
FXY,
// wmo::{TableLoader, btable::BTableCsvLoader},
fr::{TableLoader, btable::BTableLoader as BTableCsvLoader},
prelude::{BUFRTableB, BUFRTableD}, prelude::{BUFRTableB, BUFRTableD},
wmo::{TableLoader, btable::BTableCsvLoader},
}; };
#[test] #[test]
@ -263,7 +263,8 @@ mod test {
let table_loader = TableLoader::<BTableCsvLoader>::default(); let table_loader = TableLoader::<BTableCsvLoader>::default();
BUFRTableB::build_from_csv( BUFRTableB::build_from_csv(
table_loader, 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", "./test.bufrtbl",
) )
.unwrap(); .unwrap();

View File

@ -6,6 +6,8 @@ use genlib::{
pattern::{TableKind, TableScanner}, pattern::{TableKind, TableScanner},
prelude::{BUFRTableB, BUFRTableD}, prelude::{BUFRTableB, BUFRTableD},
}; };
#[cfg(feature = "opera")]
use genlib::{BUFRTableMPH, opera, tables::BitMap};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
#[derive(Parser)] #[derive(Parser)]
@ -78,6 +80,28 @@ enum Commands {
#[arg(short, long, default_value = "scan-config.toml")] #[arg(short, long, default_value = "scan-config.toml")]
output: PathBuf, 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<()> { fn main() -> Result<()> {
@ -111,6 +135,14 @@ fn main() -> Result<()> {
Commands::GenConfig { output } => { Commands::GenConfig { output } => {
generate_config_file(&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(()) Ok(())
@ -477,3 +509,49 @@ fn generate_config_file(output_path: &Path) -> Result<()> {
Ok(()) 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(())
}

View File

@ -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()
}

View File

@ -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)
}
}

View File

@ -3,6 +3,7 @@ use crate::tables::DTable;
pub use crate::wmo; pub use crate::wmo;
pub type BUFRTableD = crate::BUFRTableMPH<DTable>; pub type BUFRTableD = crate::BUFRTableMPH<DTable>;
pub type BUFRTableB = crate::BUFRTableMPH<BTable>; pub type BUFRTableB = crate::BUFRTableMPH<BTable>;
pub type BUFRTableBitMap = crate::BUFRTableMPH<crate::tables::BitMap>;
pub use crate::BUFRTableMPH; pub use crate::BUFRTableMPH;
pub use crate::FXY; pub use crate::FXY;
pub use crate::TableType; pub use crate::TableType;

View File

@ -7,10 +7,13 @@ use rkyv::rancor::{Error, Strategy};
use serde::Serialize as SerdeSerialize; use serde::Serialize as SerdeSerialize;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use std::fmt::{Debug, Display}; use std::fmt::{Debug, Display};
use toml::Table;
pub struct BTable; pub struct BTable;
pub struct DTable; pub struct DTable;
pub struct BitMap;
pub trait TableTypeTrait pub trait TableTypeTrait
where where
<Self::EntryType as Archive>::Archived: for<'a> CheckBytes<HighValidator<'a, Error>>, <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; 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: pub trait TableEntry:
SerdeSerialize SerdeSerialize
+ DeserializeOwned + 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 { impl TableEntry for DTableEntry {
fn fxy(&self) -> FXY { fn fxy(&self) -> FXY {
self.fxy self.fxy

View File

@ -16,3 +16,7 @@ serde = { version = "1.0.228", features = ["derive"] }
thiserror = "2.0.17" thiserror = "2.0.17"
gentools = { path = "../gen" } gentools = { path = "../gen" }
anyhow = "1.0.100" anyhow = "1.0.100"
[features]
default = ["opera"]
opera = ["gentools/opera"]

View File

@ -1,7 +1,11 @@
use genlib::BUFRTableMPH; use genlib::BUFRTableMPH;
#[cfg(feature = "opera")]
use genlib::prelude::BUFRTableBitMap;
use genlib::tables::TableTypeTrait; use genlib::tables::TableTypeTrait;
use crate::errors::Result; use crate::errors::Result;
#[cfg(feature = "opera")]
use crate::structs::GENCENTER;
use crate::structs::data_parser::DataParser; use crate::structs::data_parser::DataParser;
use crate::structs::versions::{BUFRMessage, MessageVersion}; use crate::structs::versions::{BUFRMessage, MessageVersion};
use crate::tables::*; use crate::tables::*;
@ -52,7 +56,25 @@ impl MessageBlock {
(None, None) (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)?; parser.parse(&self.message)?;
@ -77,6 +99,22 @@ impl MessageBlock {
}) })
.ok_or(crate::errors::Error::TableNotFoundEmpty) .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 { pub struct BUFRFile {

View File

@ -1,3 +1,18 @@
use crate::structs::data_parser::Value;
/// This Module contains functions specific to handling BUFR Opera files. /// 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
}
}

View File

@ -1,14 +1,22 @@
use std::collections::VecDeque;
use crate::{ use crate::{
errors::{Error, Result}, errors::{Error, Result},
structs::versions::MessageVersion, structs::versions::MessageVersion,
}; };
#[cfg(feature = "opera")]
use genlib::tables::ArchivedBitMapEntry;
use genlib::{ use genlib::{
FXY, opera, FXY, opera,
prelude::{BUFRTableB, BUFRTableD}, prelude::{BUFRTableB, BUFRTableBitMap, BUFRTableD},
tables::{ArchivedBTableEntry, ArchivedDTableEntry, BTableEntry}, tables::{ArchivedBTableEntry, ArchivedDTableEntry, BTableEntry},
}; };
use serde::de;
const MISS_VAL: f64 = 99999.999999;
pub struct DataParser { pub struct DataParser {
bufr_edition: u8,
master_b: BUFRTableB, master_b: BUFRTableB,
master_d: BUFRTableD, master_d: BUFRTableD,
// local // local
@ -17,22 +25,28 @@ pub struct DataParser {
// Common State // Common State
common_scale: Option<i32>, common_scale: Option<i32>,
common_ref_value: Option<i32>, common_ref_value: Option<i32>,
common_data_width: Option<u32>, common_data_width: Option<i32>,
common_str_width: Option<usize>, common_str_width: Option<usize>,
// Localized State // Localized State
local_data_width: Option<u32>, local_data_width: Option<i32>,
// Temporary storage // Temporary storage
temp_operator: Option<u32>, temp_operator: Option<i32>,
// OPERA Bitmap Table
#[cfg(feature = "opera")]
opera_bitmap_table: Option<BUFRTableBitMap>,
} }
impl DataParser { impl DataParser {
pub fn new( pub fn new(
edition: u8,
master_b: BUFRTableB, master_b: BUFRTableB,
master_d: BUFRTableD, master_d: BUFRTableD,
local_b: Option<BUFRTableB>, local_b: Option<BUFRTableB>,
local_d: Option<BUFRTableD>, local_d: Option<BUFRTableD>,
#[cfg(feature = "opera")] opera_bitmap_table: Option<BUFRTableBitMap>,
) -> Self { ) -> Self {
DataParser { DataParser {
bufr_edition: edition,
master_b, master_b,
master_d, master_d,
local_b, local_b,
@ -43,6 +57,8 @@ impl DataParser {
common_str_width: None, common_str_width: None,
local_data_width: None, local_data_width: None,
temp_operator: None, temp_operator: None,
#[cfg(feature = "opera")]
opera_bitmap_table,
} }
} }
@ -64,27 +80,24 @@ impl DataParser {
fn parser_inner<'a>( fn parser_inner<'a>(
&mut self, &mut self,
values: &mut Vec<Value>, values: &mut Vec<Value>,
mut descriptors: Vec<genlib::FXY>, mut descriptors: VecDeque<genlib::FXY>,
mut data: BitInput<'a>, data: BitInput<'a>,
) -> Result<(Vec<genlib::FXY>, BitInput<'a>)> { ) -> Result<(VecDeque<genlib::FXY>, BitInput<'a>)> {
if descriptors.is_empty() { if descriptors.is_empty() {
return Ok((descriptors, data)); return Ok((descriptors, data));
} }
let des = descriptors[0];
let des = descriptors.pop_front().unwrap();
println!("Processing descriptor {:?}", des); println!("Processing descriptor {:?}", des);
match des.f { match des.f {
0 => { 0 => {
// Element descriptor - parse data // Element descriptor - parse data
if let Some(e) = self.lookup_b_descriptor(des) { if let Some(e) = self.lookup_b_descriptor(des) {
// let (value, remaining) = e.parse(data);
let (value, remaining) = self.evalute(data, &e)?; let (value, remaining) = self.evalute(data, &e)?;
println!("Evaluated descriptor {}\nTo value {}", e, value);
println!("Parsed value: {}", value);
values.push(value); values.push(value);
data = remaining; return Ok((descriptors, remaining));
descriptors.remove(0);
} else { } else {
return Err(Error::ParseError(format!( return Err(Error::ParseError(format!(
"Descriptor {:?} not found in Table B", "Descriptor {:?} not found in Table B",
@ -94,27 +107,57 @@ impl DataParser {
} }
1 => { 1 => {
let genlib::FXY { x, y, .. } = des; 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 { (descriptors, updated_data, x as usize, count)
let descriptors_clone = descriptors.clone(); } else {
let (cde, cd) = (descriptors, data, x as usize, y as usize)
self.repeat_parser(values, descriptors_clone, data, x as usize)?; };
if i == y - 1 { if x > descriptors.len() {
descriptors = cde; 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; data = cd;
} }
} }
return Ok((seq, data));
} }
2 => { 2 => {
let data = self.deal_with_operator(values, des, data)?; let data = self.deal_with_operator(values, des, data)?;
descriptors.remove(0); return Ok((descriptors, data));
return self.parser_inner(values, descriptors, data);
} }
3 => { 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) { 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 .fxy_chain
.iter() .iter()
.map(|f| { .map(|f| {
@ -123,8 +166,16 @@ impl DataParser {
result result
}) })
.collect(); .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 { } else {
return Err(Error::ParseError(format!( return Err(Error::ParseError(format!(
"Sequence descriptor {:?} not found in Table D", "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))) .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>( fn evalute<'a>(
&self, &self,
data: BitInput<'a>, data: BitInput<'a>,
@ -210,22 +247,92 @@ impl DataParser {
return Ok((Value::String(s), data)); return Ok((Value::String(s), data));
} }
_ => { _ => {
let datawidth = self let datawidth = self.datawidth(e);
.common_data_width let scale = self.scale(e) as f64;
.unwrap_or(e.bufr_datawidth_bits.to_native()); let reference_value = self.reference_value(e) as f64;
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 (value, data) = data.get_arbitary_bits(datawidth as usize)?; let (value, data) = data.get_arbitary_bits(datawidth as usize)?;
let result = (value as f32) / scale + reference_value; let mv = (1 << datawidth) - 1;
return Ok((Value::Float(result), data)); 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
}
}
fn deal_with_operator<'a>( fn deal_with_operator<'a>(
&mut self, &mut self,
values: &mut Vec<Value>, values: &mut Vec<Value>,
@ -238,7 +345,7 @@ impl DataParser {
self.common_data_width = None; 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 { 2 => match operator.y {
@ -264,10 +371,10 @@ impl DataParser {
6 => { 6 => {
let localized_width = operator.y; let localized_width = operator.y;
self.local_data_width = Some(localized_width as u32); self.local_data_width = Some(localized_width);
} }
7 => { 7 => {
self.temp_operator = Some(operator.y as u32); self.temp_operator = Some(operator.y);
} }
8 => match operator.y { 8 => match operator.y {
0 => { 0 => {
@ -283,26 +390,75 @@ impl DataParser {
Ok(data) 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<()> {} // fn seq_parser(descriptors: &[genlib::FXY]) -> Result<()> {}
} }
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub enum Value { pub enum Value {
Float(f32), Number(f64),
Double(f64), Missing,
Int(i32),
UInt(u32),
String(String), String(String),
} }
impl std::fmt::Display for Value { impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Value::Float(v) => write!(f, "{}", v), Value::Number(v) => write!(f, "{}", v),
Value::Double(v) => write!(f, "{}", v),
Value::Int(v) => write!(f, "{}", v),
Value::UInt(v) => write!(f, "{}", v),
Value::String(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)) 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 { if nbits == 0 {
return Ok((0, self)); return Ok((0, self));
} }
let mut value: u32 = 0; let mut value: u64 = 0;
let mut remaining_bits = nbits; let mut remaining_bits = nbits;
let mut bit_offset = self.1; // Current bit position in the first byte (0-7) let mut bit_offset = self.1; // Current bit position in the first byte (0-7)
let mut byte_data = self.0; // Remaining bytes let mut byte_data = self.0; // Remaining bytes
@ -364,7 +520,7 @@ impl<'a> BitInput<'a> {
let extracted_bits = (current_byte >> shift) & mask; let extracted_bits = (current_byte >> shift) & mask;
// Add to value // 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; remaining_bits -= bits_to_read;
bit_offset += bits_to_read; bit_offset += bits_to_read;

View File

@ -8,6 +8,9 @@ pub mod data_parser;
pub(super) mod tools; pub(super) mod tools;
pub mod versions; pub mod versions;
#[cfg(feature = "opera")]
pub(super) const GENCENTER: u16 = 247;
#[inline] #[inline]
pub fn skip(n: usize) -> impl Fn(&[u8]) -> IResult<&[u8], ()> { pub fn skip(n: usize) -> impl Fn(&[u8]) -> IResult<&[u8], ()> {
move |input: &[u8]| { move |input: &[u8]| {

View File

@ -1,18 +1,20 @@
use crate::errors::{Error, Result}; use std::collections::VecDeque;
use crate::structs::bit::{self, BitInput, parse_arbitrary_bits};
use nom::{IResult, Parser, multi::many1};
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) parse_descriptors_inner(input)
.map(|(_, v)| v) .map(|(_, v)| v)
.map_err(|_| Error::ParseError(format!("Can't parse descriptors from section3"))) .map_err(|_| Error::ParseError(format!("Can't parse descriptors from section3")))
} }
fn parse_descriptors_inner(mut input: &[u8]) -> IResult<BitInput, Vec<genlib::FXY>> { fn parse_descriptors_inner(mut input: &[u8]) -> IResult<BitInput, VecDeque<genlib::FXY>> {
let mut results = Vec::new(); let mut results = VecDeque::new();
while input.len() > 1 { while input.len() > 1 {
let ((finput, _), fxy) = take_fxy((input, 0))?; let ((finput, _), fxy) = take_fxy((input, 0))?;
results.push(fxy); results.push_back(fxy);
input = finput; input = finput;
} }

View File

@ -1,5 +1,7 @@
pub mod v2; pub mod v2;
pub mod v4; pub mod v4;
use std::collections::VecDeque;
pub(super) use super::{skip, skip1, skip2}; pub(super) use super::{skip, skip1, skip2};
use crate::errors::{Error, Result}; use crate::errors::{Error, Result};
use genlib::FXY; use genlib::FXY;
@ -95,7 +97,7 @@ macro_rules! message {
} }
} }
fn descriptors(&self) -> Result<Vec<FXY>> { fn descriptors(&self) -> Result<VecDeque<FXY>> {
match self { match self {
$( $(
BUFRMessage::$version(msg) => msg.descriptors(), BUFRMessage::$version(msg) => msg.descriptors(),
@ -161,7 +163,7 @@ pub trait MessageVersion: Sized {
fn ndescs(&self) -> usize; fn ndescs(&self) -> usize;
fn descriptors(&self) -> Result<Vec<FXY>>; fn descriptors(&self) -> Result<VecDeque<FXY>>;
fn data_block(&self) -> Result<&[u8]>; fn data_block(&self) -> Result<&[u8]>;
} }

View File

@ -1,3 +1,6 @@
use std::collections::VecDeque;
use genlib::FXY;
use nom::{ use nom::{
IResult, IResult,
bytes::complete::{tag, take}, bytes::complete::{tag, take},
@ -62,7 +65,7 @@ impl MessageVersion for BUFRMessageV2 {
self.section3.data.len() / 2 self.section3.data.len() / 2
} }
fn descriptors(&self) -> Result<Vec<genlib::FXY>> { fn descriptors(&self) -> Result<VecDeque<FXY>> {
parse_descriptors(&self.section3.data) parse_descriptors(&self.section3.data)
} }

View File

@ -1,3 +1,5 @@
use std::collections::VecDeque;
use crate::errors::Result; use crate::errors::Result;
use crate::structs::{tools::parse_descriptors, versions::MessageVersion}; use crate::structs::{tools::parse_descriptors, versions::MessageVersion};
use nom::{ use nom::{
@ -64,7 +66,7 @@ impl MessageVersion for BUFRMessageV4 {
self.section3.data.len() / 2 self.section3.data.len() / 2
} }
fn descriptors(&self) -> Result<Vec<genlib::FXY>> { fn descriptors(&self) -> Result<VecDeque<genlib::FXY>> {
parse_descriptors(&self.section3.data) parse_descriptors(&self.section3.data)
} }

View File

@ -23,6 +23,25 @@ pub struct LocalTable {
version: u8, 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 { impl LocalTable {
pub fn new(sub_center: Option<u16>, version: u8) -> Self { pub fn new(sub_center: Option<u16>, version: u8) -> Self {
LocalTable { 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; pub struct TableLoader;
impl TableLoader { impl TableLoader {

Some files were not shown because too many files have changed in this diff Show More