//! # GribLib->GribTool //! //! `gribtool` is a collection of functions to write GRIB2 data file //! use lazy_static::lazy_static; use libc::{c_char, c_double, c_int, c_uint, c_ulonglong}; use std::{ env, ffi::CString, ptr, sync::{Arc, Mutex}, }; pub mod mi4tool; pub const GRIB_ELEMENTS: [&str; 10] = [ "ECT", "EDA10", "ER03", "ER06", "ER12", "ER24", "ERHA", "TMP", "TMAX", "TMIN", ]; const LIB_NAME: &str = "gribtool_lib"; const LIB_MODE: u8 = 2; static mut DEBUG: bool = false; type FuncInitContext = unsafe extern "C" fn() -> c_ulonglong; type FuncMi4textGrib = unsafe extern "C" fn( *const c_char, *const c_char, *const c_char, c_uint, *const c_char, ) -> c_int; type FuncMi4dataGrib = unsafe extern "C" fn( *const c_char, *const c_char, *const c_char, c_uint, *const c_char, ) -> c_int; type FuncGribIndex = extern "C" fn(c_ulonglong, *const c_char, *const c_char) -> c_ulonglong; type FuncFreeGribIndex = extern "C" fn(c_ulonglong) -> c_int; type FuncAddGribSample = unsafe extern "C" fn( c_ulonglong, *const c_char, *const c_double, c_uint, c_uint, *const c_double, *const c_double, c_uint, c_uint, c_uint, *const c_char, ) -> c_int; type FuncAddGribTempl = unsafe extern "C" fn( c_ulonglong, *const c_char, *const c_double, c_uint, c_uint, *const c_double, *const c_double, c_uint, c_uint, c_uint, c_uint, *const c_char, ) -> c_int; #[derive(Debug)] struct GribLib { _lib: libloading::Library, //init_context: FuncInitContext, mi4data_grib: FuncMi4dataGrib, mi4text_grib: FuncMi4textGrib, grib_file_index: FuncGribIndex, free_grib_index: FuncFreeGribIndex, add_grib_sample: FuncAddGribSample, add_grib_templ: FuncAddGribTempl, } impl GribLib { fn load_func(lib: &libloading::Library, name: &str) -> F where F: Copy, { unsafe { let name = name.as_bytes(); let func: libloading::Symbol = lib.get(name).unwrap(); let func = *func.into_raw(); return func; } } fn new(path: &str) -> GribLib { let mut libname = LIB_NAME.to_string(); let mut libext = "dll"; if cfg!(target_os = "linux") { libext = "so"; } if env::var("DEBUG").is_ok() { unsafe { DEBUG = true; } libname = format!("{}-dbg", libname); } let mut libdir = path; if libdir.is_empty() { libdir = "./"; } let libpath = format!("{}{}.{}", libdir, libname, libext); unsafe { let lib = match libloading::Library::new(&libpath) { Ok(lib) => lib, Err(e) => panic!("Load griblib error - {}, {}", libpath, e), }; //static lifecycle (so struct GribLib no need _lib field anymore) //let lib: &'static libloading::Library = Box::leak(Box::new(lib)); let init_context: FuncInitContext = GribLib::load_func(&lib, "init_context"); let mi4data_grib: FuncMi4dataGrib = GribLib::load_func(&lib, "mi4data_grib"); let mi4text_grib: FuncMi4textGrib = GribLib::load_func(&lib, "mi4text_grib"); let grib_file_index: FuncGribIndex = GribLib::load_func(&lib, "grib_file_index"); let free_grib_index: FuncFreeGribIndex = GribLib::load_func(&lib, "free_grib_index"); let add_grib_sample: FuncAddGribSample = GribLib::load_func(&lib, "add_grib_sample"); let add_grib_templ: FuncAddGribTempl = GribLib::load_func(&lib, "add_grib_templ"); let ctx = init_context(); if ctx < 1 { panic!("Init grib context error"); } let glib = GribLib { _lib: lib, //init_context, mi4data_grib, mi4text_grib, grib_file_index, free_grib_index, add_grib_sample, add_grib_templ, }; if DEBUG { println!("load grib lib: {:#?}", glib); } glib } } pub fn gribfile_index(&self, context: c_ulonglong, gribf: &str, etype: &str) -> c_ulonglong { let _lc = GLOCK.lock().unwrap(); let gindex = (self.grib_file_index)(context, cstr_chars(gribf), cstr_chars(etype)); drop(_lc); gindex } } lazy_static! { static ref GLIB: GribLib = GribLib::new(""); static ref GLOCK: Arc> = Arc::new(Mutex::new(())); } fn cstr_chars(s: &str) -> *mut c_char { let s = CString::new(s.as_bytes().to_vec()).unwrap(); return s.into_raw(); } /// MICAPS4 text directory write to GRIB2 file /// # Examples /// ``` /// use griblib_rust::gribtool; /// /// let dir = "/data/mi4text/TMP/"; /// let etype = "TMP"; /// let date_hour = 2023021008; /// let outf = "/data/grib2/tmp.GRB2"; /// let num = gribtool::mi4text_grib(dir, "", etype, date_hour, outf); /// ``` pub fn mi4text_grib(mi4dir: &str, gribf: &str, etype: &str, date_hour: u32, outf: &str) -> i32 { if ["WIU", "WIV"].contains(&etype) { return 0; } let mut count = 0; let c_gribf = cstr_chars(gribf); let c_etype = cstr_chars(etype); if LIB_MODE == 2 { let cindex = GLIB.gribfile_index(0, gribf, etype); if cindex < 1 { eprint!("Create grib file index error - {}, {}", etype, cindex); } let has_templ = cindex > 0; let infos = match mi4tool::read_mi4dir(mi4dir, date_hour) { Ok(o) => o, Err(e) => { eprint!("Read micaps4 directory error - {}, {}", mi4dir, e); return 0; } }; let fnum = infos.len(); let mut step_idx = 0; for n in 0..fnum { let info = &infos[n]; let _longitudes = [info.lon_from, info.lon_to, info.lon_step].as_ptr(); let _latitudes = [info.lat_from, info.lat_to, info.lat_step].as_ptr(); let last_index = if n >= fnum - 1 { 0 } else { step_idx + 1 }; let longitudes = ptr::null::(); let latitudes = ptr::null::(); unsafe { if has_templ { let _lock = GLOCK.lock().unwrap(); let num = (GLIB.add_grib_templ)( cindex, c_etype, info.values.as_ptr(), info.date_hour, info.mtime, longitudes, latitudes, info.width, info.height, step_idx, last_index, cstr_chars(outf), ); drop(_lock); count += num; } else { let num = (GLIB.add_grib_sample)( 0, c_etype, info.values.as_ptr(), info.date_hour, info.mtime, longitudes, latitudes, info.width, info.height, step_idx, cstr_chars(outf), ); count += num; } } step_idx += 1; } (GLIB.free_grib_index)(cindex); } else { unsafe { let mi4dir = cstr_chars(mi4dir); let outf = cstr_chars(outf); count = (GLIB.mi4text_grib)(mi4dir, c_gribf, c_etype, date_hour, outf); } } print!("Mi4Text to Grib: {} - {} = {}\n", mi4dir, outf, count); return count; } /// MICAPS4 data directory write to GRIB2 file /// # Examples /// ``` /// use griblib_rust::gribtool; /// /// let dir = "/data/mi4data/TMP/"; /// let etype = "TMP"; /// let date_hour = 2023021008; /// let outf = "/data/grib2/tmp.GRB2"; /// let num = gribtool::mi4data_grib(dir, "", etype, date_hour, outf); /// ``` pub fn mi4data_grib(mi4data: &str, gribf: &str, etype: &str, date_hour: u32, outf: &str) -> i32 { unsafe { let mi4text = cstr_chars(mi4data); let etype = cstr_chars(etype); let outf = cstr_chars(outf); let gribf = cstr_chars(gribf); let res = (GLIB.mi4data_grib)(mi4text, gribf, etype, date_hour, outf); print!("Mi4Text to Grib: {}\n", res); return res; } } #[cfg(test)] pub mod tests { use super::*; #[test] fn test_load_lib() { GribLib::new(""); } #[test] #[should_panic] fn test_load_error() { GribLib::new("./lib/"); } }