gxci 0.2.0

A safe raw-and-HAL camera interface based on Daheng-Image's GxIAPI(Galaxy Camera SDK)
Documentation
use crate::hal::base::{gxi_check, GXI};
use crate::raw::{gx_enum::*, gx_handle::*, gx_interface::*, gx_struct::*};
use crate::utils::builder::GXDeviceBaseInfoBuilder;
use crate::utils::facade::convert_to_frame_data;
use crate::utils::facade::*;
use opencv::{
    highgui,
    imgcodecs,
    core,
};
use std::slice;
use std::thread::sleep;
use std::time::Duration;
use std::sync::{LazyLock,Arc,Mutex};

//----------------------------------------------------------
//---------------Common Functions---------------------------
//----------------------------------------------------------

pub fn gxi_count_devices(timeout: u32) -> Result<u32> {
    // Use `gxi_check` to ensure GXI is initialized and accessible
    let mut device_num = 0;
    gxi_check(|gxi| {
        gxi.gx_update_device_list(&mut device_num, timeout)?;
        Ok(())
    })?;
    Ok(device_num)
}

pub fn gxi_list_devices() -> Result<Vec<GX_DEVICE_BASE_INFO>> {
    let mut device_num = 0;

    // Ensure GXI is initialized and accessible, and update device list
    gxi_check(|gxi| {
        gxi.gx_update_device_list(&mut device_num, 1000)?;
        Ok(())
    })?;

    let mut base_info: Vec<GX_DEVICE_BASE_INFO> = (0..device_num)
            .map(|_| GXDeviceBaseInfoBuilder::new().build())
            .collect();
    
        let mut size = (device_num as usize) * size_of::<GX_DEVICE_BASE_INFO>();
        
    // Populate the vector with device base information
    let status = GXI.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionError(e)))?
            .as_ref()
            .ok_or_else(|| {
                GxciError::InitializationError(
                    "GXI is None. Please check your gxci_init situation.".to_string(),
                )
            })?
            .gx_get_all_device_base_info(base_info.as_mut_ptr(), &mut size)?;
    

    if status == 0 {
        Ok(base_info)
    } else {
        Err(Error::new(ErrorKind::GxciError(GxciError::GalaxyError(status))))
    }
}


// //---------------Static LAMO  V-------------------------------

pub struct GxiDevice {
    pub device: GX_DEV_HANDLE,
}

unsafe impl Send for GxiDevice {}

pub static GXI_DEVICE: LazyLock<Arc<Mutex<Option<GxiDevice>>>> = LazyLock::new(|| {
    Arc::new(Mutex::new(None))
});

pub struct GxiFrameData {
    pub frame_data: GX_FRAME_DATA,
    pub image_buffer: Vec<u8>,
}

unsafe impl Send for GxiFrameData {}

pub static GXI_FRAME_DATA: LazyLock<Arc<Mutex<Option<GxiFrameData>>>> = LazyLock::new(|| {
    Arc::new(Mutex::new(None))
});

pub static GXI_IMAGE_BUFFER: LazyLock<Arc<Mutex<Option<Vec<u8>>>>> = LazyLock::new(|| {
    Arc::new(Mutex::new(None))
});

pub fn gxi_open_device() -> Result<()> {
    let mut device_num = 0;
    gxi_check(|gxi| {
        gxi.gx_update_device_list(&mut device_num, 1000)?;
        Ok(())
    })?;

    let mut device = std::ptr::null_mut();
    let status = gxi_check(|gxi| gxi.gx_open_device_by_index(1, &mut device))?;

    if status == 0 {
        println!("Successfully opened device index 1");
        *GXI_DEVICE.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionHandleError(e)))? = Some(GxiDevice { device });
        Ok(())
    } else {
        Err(Error::new(ErrorKind::GxciError(GxciError::GalaxyError(status))))
    }
}

pub fn gxi_close_device() -> Result<()> {
    let status = gxi_check(|gxi| gxi.gx_close_device(GXI_DEVICE.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionHandleError(e)))?.as_ref().unwrap().device))?;

    if status == 0 {
        *GXI_DEVICE.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionHandleError(e)))? = None;
        println!("Successfully closed device");
        Ok(())
    } else {
        Err(Error::new(ErrorKind::GxciError(GxciError::GalaxyError(status))))
    }
}

pub fn gxi_send_command(command: GX_FEATURE_ID) -> Result<()> {
    let status = gxi_check(|gxi| gxi.gx_send_command(GXI_DEVICE.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionHandleError(e)))?.as_ref().unwrap().device, command))?;

    if status == 0 {
        println!("Successfully sent command");
        Ok(())
    } else {
        Err(Error::new(ErrorKind::GxciError(GxciError::GalaxyError(status))))
    }
}

pub fn gxi_get_image() -> Result<()> {

    gxi_send_command(GX_FEATURE_ID::GX_COMMAND_ACQUISITION_START)?;

    let (frame_data_facade, image_buffer) = fetch_frame_data(&GXI.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionError(e)))?.as_ref().unwrap(), GXI_DEVICE.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionHandleError(e)))?.as_ref().unwrap().device).unwrap();
    let mut frame_data = convert_to_frame_data(&frame_data_facade);

    let status = gxi_check(|gxi| gxi.gx_get_image(GXI_DEVICE.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionHandleError(e)))?.as_ref().unwrap().device, &mut frame_data, 1000))?;

    *GXI_FRAME_DATA.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionFrameDataError(e)))? = Some(GxiFrameData { frame_data, image_buffer });

    gxi_send_command(GX_FEATURE_ID::GX_COMMAND_ACQUISITION_STOP)?;

    if status == 0 {
        println!("Successfully got image");
        Ok(())
    } else {
        Err(Error::new(ErrorKind::GxciError(GxciError::GalaxyError(status))))
    }
}

pub fn gxi_save_image_as_png(filename:&str) -> Result<()> {
    unsafe {
        let frame_data = GXI_FRAME_DATA.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionFrameDataError(e)))?.as_ref().unwrap().frame_data;
        if frame_data.nStatus == 0 {
            let data = slice::from_raw_parts(frame_data.pImgBuf as *const u8, (frame_data.nWidth * frame_data.nHeight) as usize);
            let mat = core::Mat::new_rows_cols_with_data(
                frame_data.nHeight, 
                frame_data.nWidth, 
                data
            ).unwrap();
            let vec = core::Vector::<i32>::new();
            if imgcodecs::imwrite(filename, &mat, &vec).unwrap() {
                println!("Image saved successfully.");
            } else {
                println!("Failed to save the image.");
            }
        }
    }
    Ok(())
}

//---------------Callback Fn-------------------------------

extern "C" fn frame_callback(p_frame_callback_data: *mut GX_FRAME_CALLBACK_PARAM) {
    // For Debug if needed
    // println!("Frame callback triggered.");
    // println!("Frame status: {:?}", unsafe { (*p_frame_callback_data).status });
    // println!("Frame All: {:?}", unsafe { *p_frame_callback_data });

    unsafe {
        let frame_callback_data = &*p_frame_callback_data;
        if frame_callback_data.status == 0 {
            let data = slice::from_raw_parts(frame_callback_data.pImgBuf as *const u8, (frame_callback_data.nWidth * frame_callback_data.nHeight) as usize);
            let mat = core::Mat::new_rows_cols_with_data(
                frame_callback_data.nHeight, 
                frame_callback_data.nWidth, 
                data
            ).unwrap();
            highgui::imshow("Camera Frame", &mat).unwrap();
            if highgui::wait_key(10).unwrap() > 0 {
                highgui::destroy_window("Camera Frame").unwrap();
            }
        }
    }
}

pub fn gxi_open_stream() -> Result<()> {

    let status = gxi_check(|gxi| gxi.gx_register_capture_callback(GXI_DEVICE.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionHandleError(e)))?.as_ref().unwrap().device,frame_callback))?;

    gxi_send_command(GX_FEATURE_ID::GX_COMMAND_ACQUISITION_START)?;

    highgui::named_window("Camera", highgui::WINDOW_AUTOSIZE).unwrap();
    loop {
        sleep(Duration::from_secs(10));
        break;
    }

    if status == 0 {
        println!("Successfully opened stream");
        Ok(())
    } else {
        Err(Error::new(ErrorKind::GxciError(GxciError::GalaxyError(status))))
    }
}

pub fn gxi_close_stream() -> Result<()> {

    gxi_send_command(GX_FEATURE_ID::GX_COMMAND_ACQUISITION_STOP)?;

    let status = gxi_check(|gxi| gxi.gx_unregister_capture_callback(GXI_DEVICE.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionHandleError(e)))?.as_ref().unwrap().device))?;

    if status == 0 {
        println!("Successfully closed stream");
        Ok(())
    } else {
        Err(Error::new(ErrorKind::GxciError(GxciError::GalaxyError(status))))
    }

}

pub fn gxi_open_stream_interval(interval_secs:u64) -> Result<()> {

    gxi_check(|gxi| gxi.gx_register_capture_callback(GXI_DEVICE.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionHandleError(e)))?.as_ref().unwrap().device,frame_callback))?;

    gxi_send_command(GX_FEATURE_ID::GX_COMMAND_ACQUISITION_START)?;

    highgui::named_window("Camera", highgui::WINDOW_AUTOSIZE).unwrap();
    loop {
        sleep(Duration::from_secs(interval_secs));
        break;
    }

    gxi_send_command(GX_FEATURE_ID::GX_COMMAND_ACQUISITION_STOP)?;

    let status = gxi_check(|gxi| gxi.gx_unregister_capture_callback(GXI_DEVICE.lock().map_err(|e| Error::new(ErrorKind::MutexPoisonOptionHandleError(e)))?.as_ref().unwrap().device))?;

    if status == 0 {
        println!("Successfully opened stream");
        Ok(())
    } else {
        Err(Error::new(ErrorKind::GxciError(GxciError::GalaxyError(status))))
    }
}


// //----------------------------------------------------------
// //---------------Multi Camera-------------------------------
// //----------------------------------------------------------