use crate::ffi::c::core::status::{clear_last_error, panic_to_status};
use crate::ffi::c::formulation::Topp2Problem;
use crate::ffi::c::solver::topp2::ra::Topp2RaOptions;
use crate::ffi::c::{CoppRobot, CoppStatus, CoppVecF64};
use crate::solver::{
reach_set2::{
ReachSet2, reach_set2_backward as rust_reach_set2_backward,
reach_set2_bidirectional as rust_reach_set2_bidirectional,
},
topp2_ra::Topp2ProblemBuilder,
};
use std::{
panic::{AssertUnwindSafe, catch_unwind},
ptr::NonNull,
};
#[repr(C)]
#[derive(Debug)]
pub struct CoppReachSet2Result {
pub a_max: CoppVecF64,
pub a_min: CoppVecF64,
}
impl CoppReachSet2Result {
const fn empty() -> Self {
Self {
a_max: CoppVecF64::empty(),
a_min: CoppVecF64::empty(),
}
}
fn from_reach_set(reach_set: ReachSet2) -> Self {
Self {
a_max: CoppVecF64::from_vec(reach_set.a_max),
a_min: CoppVecF64::from_vec(reach_set.a_min),
}
}
fn out_ptr(out: *mut Self) -> Result<NonNull<Self>, CoppStatus> {
NonNull::new(out).ok_or(CoppStatus::NullPointer)
}
unsafe fn write_empty_to(out: NonNull<Self>) {
unsafe {
out.as_ptr().write(Self::empty());
}
}
unsafe fn write_reach_set_to(out: NonNull<Self>, reach_set: ReachSet2) {
unsafe {
out.as_ptr().write(Self::from_reach_set(reach_set));
}
}
fn free(self) {
self.a_max.free();
self.a_min.free();
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn copp_reach_set2_backward(
problem: Topp2Problem,
options: Topp2RaOptions,
out_result: *mut CoppReachSet2Result,
) -> CoppStatus {
crate::ffi::c::core::status::clear_last_error();
solve_reach_set2(problem, options, out_result, rust_reach_set2_backward)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn copp_reach_set2_bidirectional(
problem: Topp2Problem,
options: Topp2RaOptions,
out_result: *mut CoppReachSet2Result,
) -> CoppStatus {
crate::ffi::c::core::status::clear_last_error();
solve_reach_set2(problem, options, out_result, rust_reach_set2_bidirectional)
}
fn solve_reach_set2(
problem: Topp2Problem,
options: Topp2RaOptions,
out_result: *mut CoppReachSet2Result,
solve: fn(
&crate::solver::topp2_ra::Topp2Problem<'_>,
&crate::solver::topp2_ra::ReachSet2Options,
) -> Result<ReachSet2, crate::diag::CoppError>,
) -> CoppStatus {
let out_result = match CoppReachSet2Result::out_ptr(out_result) {
Ok(out_result) => out_result,
Err(status) => return status,
};
unsafe {
CoppReachSet2Result::write_empty_to(out_result);
}
match catch_unwind(AssertUnwindSafe(|| {
let robot = unsafe { CoppRobot::robot(problem.robot) }.ok_or(CoppStatus::NullPointer)?;
let problem = Topp2ProblemBuilder::new(
robot,
(problem.idx_s_start, problem.idx_s_final),
(problem.a_start, problem.a_final),
)
.build()
.map_err(|error| CoppStatus::from(&error))?;
let options = options.build()?;
let reach_set = solve(&problem, &options).map_err(|error| CoppStatus::from(&error))?;
unsafe {
CoppReachSet2Result::write_reach_set_to(out_result, reach_set);
}
Ok(CoppStatus::Ok)
})) {
Ok(Ok(status)) | Ok(Err(status)) => status.into_ffi_status(),
Err(payload) => panic_to_status(payload).into_ffi_status(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn copp_reach_set2_result_free(result: CoppReachSet2Result) {
result.free();
clear_last_error();
}