#![cfg(feature = "c-client")]
use mbus_client::services::ClientServices;
use mbus_core::transport::SerialMode;
use super::app::CApp;
use super::callbacks::MbusCallbacks;
use super::config::{MbusSerialConfig, serial_config_from_c};
use super::pool::{MBUS_INVALID_CLIENT_ID, MbusClientId, pool_free};
#[cfg(feature = "serial-rtu")]
use super::pool::pool_allocate_serial_rtu;
#[cfg(feature = "serial-ascii")]
use super::pool::pool_allocate_serial_ascii;
#[cfg(any(feature = "serial-rtu", feature = "serial-ascii"))]
use super::pool::with_serial_client_uniform;
use crate::c::error::MbusStatusCode;
use crate::c::transport::{MbusTransportCallbacks, validate_transport_callbacks};
#[cfg(feature = "serial-rtu")]
use crate::c::transport::CRtuTransport;
#[cfg(feature = "serial-ascii")]
use crate::c::transport::CAsciiTransport;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_serial_client_new(
config: *const MbusSerialConfig,
transport_callbacks: *const MbusTransportCallbacks,
callbacks: *const MbusCallbacks,
) -> MbusClientId {
if callbacks.is_null() || transport_callbacks.is_null() {
return MBUS_INVALID_CLIENT_ID;
}
let serial_config = match unsafe { serial_config_from_c(config) } {
Ok(c) => c,
Err(_) => return MBUS_INVALID_CLIENT_ID,
};
let cb = unsafe { callbacks.read() };
if cb.on_current_millis.is_none() {
return MBUS_INVALID_CLIENT_ID;
}
let transport_cb = unsafe { transport_callbacks.read() };
if !validate_transport_callbacks(&transport_cb) {
return MBUS_INVALID_CLIENT_ID;
}
#[allow(unreachable_code, unreachable_patterns)]
match serial_config.mode {
#[cfg(feature = "serial-rtu")]
SerialMode::Rtu => {
let app = CApp::new(cb);
let transport = CRtuTransport::new(transport_cb);
let inner = match ClientServices::new_serial(transport, app, serial_config) {
Ok(i) => i,
Err(_) => return MBUS_INVALID_CLIENT_ID,
};
match pool_allocate_serial_rtu(inner) {
Ok(id) => id,
Err(_) => MBUS_INVALID_CLIENT_ID,
}
}
#[cfg(feature = "serial-ascii")]
SerialMode::Ascii => {
let app = CApp::new(cb);
let transport = CAsciiTransport::new(transport_cb);
let inner = match ClientServices::new_serial(transport, app, serial_config) {
Ok(i) => i,
Err(_) => return MBUS_INVALID_CLIENT_ID,
};
match pool_allocate_serial_ascii(inner) {
Ok(id) => id,
Err(_) => MBUS_INVALID_CLIENT_ID,
}
}
_ => MBUS_INVALID_CLIENT_ID,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mbus_serial_client_free(id: MbusClientId) {
if id != MBUS_INVALID_CLIENT_ID {
pool_free(id);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mbus_serial_connect(id: MbusClientId) -> MbusStatusCode {
with_serial_client_uniform!(id, |inner| match inner.reconnect() {
Ok(()) => MbusStatusCode::MbusOk,
Err(e) => MbusStatusCode::from(e),
})
.unwrap_or_else(|e| e)
}
#[unsafe(no_mangle)]
pub extern "C" fn mbus_serial_disconnect(id: MbusClientId) -> MbusStatusCode {
with_serial_client_uniform!(id, |inner| {
inner.disconnect();
MbusStatusCode::MbusOk
})
.unwrap_or_else(|e| e)
}
#[unsafe(no_mangle)]
pub extern "C" fn mbus_serial_is_connected(id: MbusClientId) -> u8 {
with_serial_client_uniform!(id, |inner| if inner.is_connected() { 1 } else { 0 }).unwrap_or(0)
}
#[unsafe(no_mangle)]
pub extern "C" fn mbus_serial_poll(id: MbusClientId) {
let _ = with_serial_client_uniform!(id, |inner| inner.poll());
}
#[unsafe(no_mangle)]
pub extern "C" fn mbus_serial_has_pending_requests(id: MbusClientId) -> u8 {
with_serial_client_uniform!(id, |inner| {
if inner.has_pending_requests() { 1 } else { 0 }
})
.unwrap_or(0)
}
#[unsafe(no_mangle)]
pub extern "C" fn mbus_serial_reconnect(id: MbusClientId) -> MbusStatusCode {
with_serial_client_uniform!(id, |inner| match inner.reconnect() {
Ok(()) => MbusStatusCode::MbusOk,
Err(e) => MbusStatusCode::from(e),
})
.unwrap_or_else(|e| e)
}