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
/tmp-out

View File

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

View File

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

View File

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

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 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;

View File

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

View File

@ -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"]

View File

@ -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 {

View File

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

View File

@ -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;

View File

@ -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]| {

View File

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

View File

@ -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]>;
}

View File

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

View File

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

View File

@ -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 {

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