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,
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<F>(lib: &libloading::Library, name: &str) -> F
where
F: Copy,
{
unsafe {
let name = name.as_bytes();
let func: libloading::Symbol<F> = 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),
};
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,
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<Mutex<()>> = 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();
}
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::<f64>();
let latitudes = ptr::null::<f64>();
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;
}
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/");
}
}