Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion libasi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ edition = "2024"

[dependencies]
libasi-sys = { version = "0.1.0", path = "../libasi-sys" }
log = "0.4"
log = "0.4"
thiserror = "1"
255 changes: 178 additions & 77 deletions libasi/src/camera.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
pub use libasi_sys::camera::*;
use log::error;

pub type AsiCameraInfo = _ASI_CAMERA_INFO;
pub type AsiControlCaps = _ASI_CONTROL_CAPS;
Expand All @@ -13,100 +12,154 @@ pub struct ROIFormat {
pub img_type: i32,
}

fn check_error_code(code: i32) {
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum AsiCameraError {
#[error("invalid camera index")]
InvalidIndex,
#[error("invalid camera id")]
InvalidId,
#[error("invalid control type")]
InvalidControlType,
#[error("camera not open")]
CameraClosed,
#[error("camera removed")]
CameraRemoved,
#[error("invalid path")]
InvalidPath,
#[error("invalid file format")]
InvalidFileFormat,
#[error("invalid size")]
InvalidSize,
#[error("invalid image type")]
InvalidImgType,
#[error("start position out of boundary")]
OutOfBoundary,
#[error("communication timeout")]
Timeout,
#[error("invalid sequence — stop capture first")]
InvalidSequence,
#[error("buffer too small")]
BufferTooSmall,
#[error("video mode active")]
VideoModeActive,
#[error("exposure in progress")]
ExposureInProgress,
#[error("general error")]
GeneralError,
#[error("invalid mode")]
InvalidMode,
#[error("end sentinel")]
End,
#[error("unknown error code: {0}")]
Unknown(i32),
}

fn check_error_code(code: i32) -> Result<(), AsiCameraError> {
match code {
// Success
0 => (),
// No camera connected or index value out of boundary
1 => error!("ASI_ERROR_INVALID_INDEX"),
2 => error!("ASI_ERROR_INVALID_ID"),
3 => error!("ASI_ERROR_INVALID_CONTROL_TYPE"),
// Camera didn't open
4 => error!("ASI_ERROR_CAMERA_CLOSED"),
// Failed to find the camera, maybe the camera has been removed
5 => error!("ASI_ERROR_CAMERA_REMOVED"),
// Cannot find the path of the file
6 => error!("ASI_ERROR_INVALID_PATH"),
7 => error!("ASI_ERROR_INVALID_FILEFORMAT"),
// Wrong video format size
8 => error!("ASI_ERROR_INVALID_SIZE"),
9 => error!("ASI_ERROR_INVALID_IMGTYPE"), //unsupported image formate
10 => error!("ASI_ERROR_OUTOF_BOUNDARY"), //the startpos is out of boundary
// Communication timeout
11 => error!("ASI_ERROR_TIMEOUT"),
12 => error!("ASI_ERROR_INVALID_SEQUENCE"), //stop capture first!
13 => error!("ASI_ERROR_BUFFER_TOO_SMALL"), //buffer size is not big enough
14 => error!("ASI_ERROR_VIDEO_MODE_ACTIVE"),
15 => error!("ASI_ERROR_EXPOSURE_IN_PROGRESS"),
16 => error!("ASI_ERROR_GENERAL_ERROR"), //general error, eg: value is out of valid range
17 => error!("ASI_ERROR_INVALID_MODE"), //the current mode is wrong
18 => error!("ASI_ERROR_END"),
e => error!("unknown error {}", e),
0 => Ok(()),
1 => Err(AsiCameraError::InvalidIndex),
2 => Err(AsiCameraError::InvalidId),
3 => Err(AsiCameraError::InvalidControlType),
4 => Err(AsiCameraError::CameraClosed),
5 => Err(AsiCameraError::CameraRemoved),
6 => Err(AsiCameraError::InvalidPath),
7 => Err(AsiCameraError::InvalidFileFormat),
8 => Err(AsiCameraError::InvalidSize),
9 => Err(AsiCameraError::InvalidImgType),
10 => Err(AsiCameraError::OutOfBoundary),
11 => Err(AsiCameraError::Timeout),
12 => Err(AsiCameraError::InvalidSequence),
13 => Err(AsiCameraError::BufferTooSmall),
14 => Err(AsiCameraError::VideoModeActive),
15 => Err(AsiCameraError::ExposureInProgress),
16 => Err(AsiCameraError::GeneralError),
17 => Err(AsiCameraError::InvalidMode),
18 => Err(AsiCameraError::End),
n => Err(AsiCameraError::Unknown(n)),
}
}

pub fn start_exposure(camera_id: i32) {
check_error_code(unsafe { libasi_sys::camera::ASIStartExposure(camera_id, 0) });
pub fn start_exposure(camera_id: i32) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASIStartExposure(camera_id, 0) })
}

pub fn stop_exposure(camera_id: i32) {
check_error_code(unsafe { libasi_sys::camera::ASIStopExposure(camera_id) });
pub fn stop_exposure(camera_id: i32) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASIStopExposure(camera_id) })
}

#[cfg(windows)]
pub fn exposure_status(camera_id: i32, status: *mut i32) {
check_error_code(unsafe { libasi_sys::camera::ASIGetExpStatus(camera_id, status) });
pub fn exposure_status(camera_id: i32, status: *mut i32) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASIGetExpStatus(camera_id, status) })
}

#[cfg(unix)]
pub fn exposure_status(camera_id: i32, status: *mut u32) {
check_error_code(unsafe { libasi_sys::camera::ASIGetExpStatus(camera_id, status) });
pub fn exposure_status(camera_id: i32, status: *mut u32) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASIGetExpStatus(camera_id, status) })
}

#[cfg(windows)]
pub fn download_exposure(camera_id: i32, buffer: *mut u8, buf_size: i32) {
check_error_code(unsafe { libasi_sys::camera::ASIGetDataAfterExp(camera_id, buffer, buf_size) });
pub fn download_exposure(
camera_id: i32,
buffer: *mut u8,
buf_size: i32,
) -> Result<(), AsiCameraError> {
check_error_code(unsafe {
libasi_sys::camera::ASIGetDataAfterExp(camera_id, buffer, buf_size)
})
}

#[cfg(unix)]
pub fn download_exposure(camera_id: i32, buffer: *mut u8, buf_size: i64) {
check_error_code(unsafe { libasi_sys::camera::ASIGetDataAfterExp(camera_id, buffer, buf_size) });
pub fn download_exposure(
camera_id: i32,
buffer: *mut u8,
buf_size: i64,
) -> Result<(), AsiCameraError> {
check_error_code(unsafe {
libasi_sys::camera::ASIGetDataAfterExp(camera_id, buffer, buf_size)
})
}

pub fn get_num_of_connected_cameras() -> i32 {
unsafe { libasi_sys::camera::ASIGetNumOfConnectedCameras() }
}

pub fn get_cam_id(camera_id: i32, asi_id: *mut AsiID) {
check_error_code(unsafe { libasi_sys::camera::ASIGetID(camera_id, asi_id) });
pub fn get_cam_id(camera_id: i32, asi_id: *mut AsiID) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASIGetID(camera_id, asi_id) })
}

pub fn set_cam_id(camera_id: i32, asi_id: AsiID) {
check_error_code(unsafe { libasi_sys::camera::ASISetID(camera_id, asi_id) });
pub fn set_cam_id(camera_id: i32, asi_id: AsiID) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASISetID(camera_id, asi_id) })
}

pub fn open_camera(camera_index: i32) {
check_error_code(unsafe { libasi_sys::camera::ASIOpenCamera(camera_index) });
pub fn open_camera(camera_index: i32) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASIOpenCamera(camera_index) })
}

pub fn init_camera(camera_index: i32) {
check_error_code(unsafe { libasi_sys::camera::ASIInitCamera(camera_index) });
pub fn init_camera(camera_index: i32) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASIInitCamera(camera_index) })
}

pub fn close_camera(camera_index: i32) {
check_error_code(unsafe { libasi_sys::camera::ASICloseCamera(camera_index) });
pub fn close_camera(camera_index: i32) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASICloseCamera(camera_index) })
}

pub fn get_control_caps(camera_id: i32, index: i32, noc: *mut AsiControlCaps) {
check_error_code(unsafe { libasi_sys::camera::ASIGetControlCaps(camera_id, index, noc) });
pub fn get_control_caps(
camera_id: i32,
index: i32,
noc: *mut AsiControlCaps,
) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASIGetControlCaps(camera_id, index, noc) })
}

pub fn get_num_of_controls(camera_index: i32, noc: *mut i32) {
check_error_code(unsafe { libasi_sys::camera::ASIGetNumOfControls(camera_index, noc) });
pub fn get_num_of_controls(camera_index: i32, noc: *mut i32) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASIGetNumOfControls(camera_index, noc) })
}

pub fn get_camera_info(asi_info: *mut AsiCameraInfo, camera_index: i32) {
check_error_code(unsafe { libasi_sys::camera::ASIGetCameraProperty(asi_info, camera_index) });
pub fn get_camera_info(
asi_info: *mut AsiCameraInfo,
camera_index: i32,
) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASIGetCameraProperty(asi_info, camera_index) })
}

#[cfg(windows)]
Expand All @@ -115,10 +168,10 @@ pub fn get_control_value(
control_type: i32,
value: &mut i32,
is_auto_set: &mut i32,
) {
) -> Result<(), AsiCameraError> {
check_error_code(unsafe {
libasi_sys::camera::ASIGetControlValue(camera_index, control_type, value, is_auto_set)
});
})
}

#[cfg(unix)]
Expand All @@ -127,24 +180,34 @@ pub fn get_control_value(
control_type: i32,
value: &mut i64,
is_auto_set: &mut i32,
) {
) -> Result<(), AsiCameraError> {
check_error_code(unsafe {
libasi_sys::camera::ASIGetControlValue(camera_index, control_type, value, is_auto_set)
});
})
}

#[cfg(windows)]
pub fn set_control_value(camera_index: i32, control_type: i32, value: i32, is_auto_set: i32) {
pub fn set_control_value(
camera_index: i32,
control_type: i32,
value: i32,
is_auto_set: i32,
) -> Result<(), AsiCameraError> {
check_error_code(unsafe {
libasi_sys::camera::ASISetControlValue(camera_index, control_type, value, is_auto_set)
});
})
}

#[cfg(unix)]
pub fn set_control_value(camera_index: i32, control_type: i32, value: ::std::os::raw::c_long, is_auto_set: i32) {
pub fn set_control_value(
camera_index: i32,
control_type: i32,
value: ::std::os::raw::c_long,
is_auto_set: i32,
) -> Result<(), AsiCameraError> {
check_error_code(unsafe {
libasi_sys::camera::ASISetControlValue(camera_index, control_type, value, is_auto_set)
});
})
}

pub fn get_roi_format(
Expand All @@ -153,10 +216,10 @@ pub fn get_roi_format(
height: &mut i32,
bin: &mut i32,
img_type: &mut i32,
) {
) -> Result<(), AsiCameraError> {
check_error_code(unsafe {
libasi_sys::camera::ASIGetROIFormat(camera_id, width, height, bin, img_type)
});
})
}

pub fn set_roi_format(
Expand All @@ -165,20 +228,58 @@ pub fn set_roi_format(
height: i32,
bin: i32,
img_type: i32,
) {
) -> Result<(), AsiCameraError> {
check_error_code(unsafe {
libasi_sys::camera::ASISetROIFormat(camera_id, width, height, bin, img_type)
});
})
}

pub fn get_start_position(cam_idx: i32, start_x: &mut i32, start_y: &mut i32) {
check_error_code(unsafe {
libasi_sys::camera::ASIGetStartPos(cam_idx, start_x, start_y)
});
pub fn get_start_position(
cam_idx: i32,
start_x: &mut i32,
start_y: &mut i32,
) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASIGetStartPos(cam_idx, start_x, start_y) })
}

pub fn get_camera_mode(cam_idx: i32, camera_mode: &mut i32) {
check_error_code(unsafe {
libasi_sys::camera::ASIGetCameraMode(cam_idx, camera_mode)
});
pub fn get_camera_mode(cam_idx: i32, camera_mode: &mut i32) -> Result<(), AsiCameraError> {
check_error_code(unsafe { libasi_sys::camera::ASIGetCameraMode(cam_idx, camera_mode) })
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn success_code_is_ok() {
assert!(check_error_code(0).is_ok());
}

#[test]
fn known_error_codes_map_correctly() {
assert_eq!(check_error_code(1), Err(AsiCameraError::InvalidIndex));
assert_eq!(check_error_code(2), Err(AsiCameraError::InvalidId));
assert_eq!(check_error_code(3), Err(AsiCameraError::InvalidControlType));
assert_eq!(check_error_code(4), Err(AsiCameraError::CameraClosed));
assert_eq!(check_error_code(5), Err(AsiCameraError::CameraRemoved));
assert_eq!(check_error_code(6), Err(AsiCameraError::InvalidPath));
assert_eq!(check_error_code(7), Err(AsiCameraError::InvalidFileFormat));
assert_eq!(check_error_code(8), Err(AsiCameraError::InvalidSize));
assert_eq!(check_error_code(9), Err(AsiCameraError::InvalidImgType));
assert_eq!(check_error_code(10), Err(AsiCameraError::OutOfBoundary));
assert_eq!(check_error_code(11), Err(AsiCameraError::Timeout));
assert_eq!(check_error_code(12), Err(AsiCameraError::InvalidSequence));
assert_eq!(check_error_code(13), Err(AsiCameraError::BufferTooSmall));
assert_eq!(check_error_code(14), Err(AsiCameraError::VideoModeActive));
assert_eq!(check_error_code(15), Err(AsiCameraError::ExposureInProgress));
assert_eq!(check_error_code(16), Err(AsiCameraError::GeneralError));
assert_eq!(check_error_code(17), Err(AsiCameraError::InvalidMode));
assert_eq!(check_error_code(18), Err(AsiCameraError::End));
}

#[test]
fn unknown_code_wraps_value() {
assert_eq!(check_error_code(99), Err(AsiCameraError::Unknown(99)));
assert_eq!(check_error_code(-5), Err(AsiCameraError::Unknown(-5)));
}
}
Loading