use core::ffi::{c_char, c_void};
use core::ptr;
use core::slice;
use std::ffi::CStr;
use std::sync::Arc;
use std::time::Duration;
use mbus_client_async::AsyncTcpClient;
#[cfg(feature = "diagnostics")]
use mbus_core::function_codes::public::DiagnosticSubFunction;
#[cfg(feature = "file-record")]
use {
heapless::Vec as HVec, mbus_client_async::SubRequest,
mbus_core::data_unit::common::MAX_PDU_DATA_LEN,
};
use crate::dotnet::runtime;
use crate::dotnet::status::{self, MbusDnStatus};
#[allow(missing_docs)]
pub struct MbusDnTcpClient {
inner: Arc<AsyncTcpClient>,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_new(
host: *const c_char,
port: u16,
) -> *mut MbusDnTcpClient {
if host.is_null() {
return ptr::null_mut();
}
let host_str = match unsafe { CStr::from_ptr(host) }.to_str() {
Ok(s) => s,
Err(_) => return ptr::null_mut(),
};
let rt = runtime::get();
let _guard = rt.enter();
let client = match AsyncTcpClient::new(host_str, port) {
Ok(c) => c,
Err(_) => return ptr::null_mut(),
};
Box::into_raw(Box::new(MbusDnTcpClient {
inner: Arc::new(client),
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_free(handle: *mut MbusDnTcpClient) {
if handle.is_null() {
return;
}
drop(unsafe { Box::from_raw(handle) });
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_connect(handle: *mut MbusDnTcpClient) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
let rt = runtime::get();
match rt.block_on(client.connect()) {
Ok(()) => MbusDnStatus::MbusOk,
Err(e) => status::from_async(e),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_disconnect(
handle: *mut MbusDnTcpClient,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
let rt = runtime::get();
match rt.block_on(client.disconnect()) {
Ok(()) => MbusDnStatus::MbusOk,
Err(e) => status::from_async(e),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_set_request_timeout_ms(
handle: *mut MbusDnTcpClient,
timeout_ms: u64,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if timeout_ms == 0 {
client.clear_request_timeout();
} else {
client.set_request_timeout(Duration::from_millis(timeout_ms));
}
MbusDnStatus::MbusOk
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_has_pending_requests(
handle: *mut MbusDnTcpClient,
) -> u8 {
match unsafe { handle.as_ref() } {
Some(h) => h.inner.has_pending_requests() as u8,
None => 0,
}
}
#[cfg(feature = "registers")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_read_holding_registers(
handle: *mut MbusDnTcpClient,
unit_id: u8,
address: u16,
quantity: u16,
out_buf: *mut u16,
out_buf_len: u16,
out_count: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if out_buf.is_null() || out_count.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
if out_buf_len < quantity {
return MbusDnStatus::MbusErrBufferTooSmall;
}
let rt = runtime::get();
let regs = match rt.block_on(client.read_holding_registers(unit_id, address, quantity)) {
Ok(r) => r,
Err(e) => return status::from_async(e),
};
let qty = regs.quantity();
let base = regs.from_address();
let dst = unsafe { slice::from_raw_parts_mut(out_buf, qty as usize) };
for (i, slot) in dst.iter_mut().enumerate() {
*slot = regs.value(base + i as u16).unwrap_or(0);
}
unsafe { *out_count = qty };
MbusDnStatus::MbusOk
}
#[cfg(feature = "registers")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_write_single_register(
handle: *mut MbusDnTcpClient,
unit_id: u8,
address: u16,
value: u16,
out_address: *mut u16,
out_value: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
let rt = runtime::get();
match rt.block_on(client.write_single_register(unit_id, address, value)) {
Ok((addr, val)) => {
if !out_address.is_null() {
unsafe { *out_address = addr };
}
if !out_value.is_null() {
unsafe { *out_value = val };
}
MbusDnStatus::MbusOk
}
Err(e) => status::from_async(e),
}
}
#[cfg(feature = "registers")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_write_multiple_registers(
handle: *mut MbusDnTcpClient,
unit_id: u8,
address: u16,
values: *const u16,
quantity: u16,
out_address: *mut u16,
out_quantity: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if values.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
if quantity == 0 {
return MbusDnStatus::MbusErrInvalidQuantity;
}
let slice = unsafe { slice::from_raw_parts(values, quantity as usize) };
let rt = runtime::get();
match rt.block_on(client.write_multiple_registers(unit_id, address, slice)) {
Ok((addr, qty)) => {
if !out_address.is_null() {
unsafe { *out_address = addr };
}
if !out_quantity.is_null() {
unsafe { *out_quantity = qty };
}
MbusDnStatus::MbusOk
}
Err(e) => status::from_async(e),
}
}
#[cfg(feature = "coils")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_read_coils(
handle: *mut MbusDnTcpClient,
unit_id: u8,
address: u16,
quantity: u16,
out_buf: *mut u8,
out_buf_len: u16,
out_count: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if out_buf.is_null() || out_count.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
let byte_count = quantity.div_ceil(8);
if out_buf_len < byte_count {
return MbusDnStatus::MbusErrBufferTooSmall;
}
let rt = runtime::get();
let coils = match rt.block_on(client.read_multiple_coils(unit_id, address, quantity)) {
Ok(c) => c,
Err(e) => return status::from_async(e),
};
let src = coils.values();
let dst = unsafe { slice::from_raw_parts_mut(out_buf, byte_count as usize) };
dst.copy_from_slice(&src[..byte_count as usize]);
unsafe { *out_count = byte_count };
MbusDnStatus::MbusOk
}
#[cfg(feature = "coils")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_write_single_coil(
handle: *mut MbusDnTcpClient,
unit_id: u8,
address: u16,
value: u8,
out_address: *mut u16,
out_value: *mut u8,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
let rt = runtime::get();
match rt.block_on(client.write_single_coil(unit_id, address, value != 0)) {
Ok((addr, on)) => {
if !out_address.is_null() {
unsafe { *out_address = addr };
}
if !out_value.is_null() {
unsafe { *out_value = on as u8 };
}
MbusDnStatus::MbusOk
}
Err(e) => status::from_async(e),
}
}
#[cfg(feature = "coils")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_write_multiple_coils(
handle: *mut MbusDnTcpClient,
unit_id: u8,
address: u16,
packed_coils: *const u8,
byte_count: u16,
coil_count: u16,
out_address: *mut u16,
out_quantity: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if packed_coils.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
let packed = unsafe { slice::from_raw_parts(packed_coils, byte_count as usize) };
let coils = match mbus_core::models::coil::Coils::new(address, coil_count) {
Ok(c) => c,
Err(e) => return status::from_mbus(e),
};
let coils = match coils.with_values(packed, coil_count) {
Ok(c) => c,
Err(e) => return status::from_mbus(e),
};
let rt = runtime::get();
match rt.block_on(client.write_multiple_coils(unit_id, address, &coils)) {
Ok((addr, qty)) => {
if !out_address.is_null() {
unsafe { *out_address = addr };
}
if !out_quantity.is_null() {
unsafe { *out_quantity = qty };
}
MbusDnStatus::MbusOk
}
Err(e) => status::from_async(e),
}
}
#[cfg(feature = "discrete-inputs")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_read_discrete_inputs(
handle: *mut MbusDnTcpClient,
unit_id: u8,
address: u16,
quantity: u16,
out_buf: *mut u8,
out_buf_len: u16,
out_count: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if out_buf.is_null() || out_count.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
let byte_count = quantity.div_ceil(8);
if out_buf_len < byte_count {
return MbusDnStatus::MbusErrBufferTooSmall;
}
let rt = runtime::get();
let di = match rt.block_on(client.read_discrete_inputs(unit_id, address, quantity)) {
Ok(d) => d,
Err(e) => return status::from_async(e),
};
let src = di.values();
let n = src.len().min(byte_count as usize);
let dst = unsafe { slice::from_raw_parts_mut(out_buf, n) };
dst.copy_from_slice(&src[..n]);
unsafe { *out_count = n as u16 };
MbusDnStatus::MbusOk
}
#[cfg(feature = "registers")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_read_input_registers(
handle: *mut MbusDnTcpClient,
unit_id: u8,
address: u16,
quantity: u16,
out_buf: *mut u16,
out_buf_len: u16,
out_count: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if out_buf.is_null() || out_count.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
if out_buf_len < quantity {
return MbusDnStatus::MbusErrBufferTooSmall;
}
let rt = runtime::get();
let regs = match rt.block_on(client.read_input_registers(unit_id, address, quantity)) {
Ok(r) => r,
Err(e) => return status::from_async(e),
};
let qty = regs.quantity();
let base = regs.from_address();
let dst = unsafe { slice::from_raw_parts_mut(out_buf, qty as usize) };
for (i, slot) in dst.iter_mut().enumerate() {
*slot = regs.value(base + i as u16).unwrap_or(0);
}
unsafe { *out_count = qty };
MbusDnStatus::MbusOk
}
#[cfg(feature = "registers")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_mask_write_register(
handle: *mut MbusDnTcpClient,
unit_id: u8,
address: u16,
and_mask: u16,
or_mask: u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
let rt = runtime::get();
match rt.block_on(client.mask_write_register(unit_id, address, and_mask, or_mask)) {
Ok(()) => MbusDnStatus::MbusOk,
Err(e) => status::from_async(e),
}
}
#[cfg(feature = "registers")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_read_write_multiple_registers(
handle: *mut MbusDnTcpClient,
unit_id: u8,
read_address: u16,
read_quantity: u16,
write_address: u16,
write_values: *const u16,
write_quantity: u16,
out_buf: *mut u16,
out_buf_len: u16,
out_count: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if write_values.is_null() || out_buf.is_null() || out_count.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
if out_buf_len < read_quantity {
return MbusDnStatus::MbusErrBufferTooSmall;
}
let wv = unsafe { slice::from_raw_parts(write_values, write_quantity as usize) };
let rt = runtime::get();
let regs = match rt.block_on(client.read_write_multiple_registers(
unit_id,
read_address,
read_quantity,
write_address,
wv,
)) {
Ok(r) => r,
Err(e) => return status::from_async(e),
};
let qty = regs.quantity();
let base = regs.from_address();
let dst = unsafe { slice::from_raw_parts_mut(out_buf, qty as usize) };
for (i, slot) in dst.iter_mut().enumerate() {
*slot = regs.value(base + i as u16).unwrap_or(0);
}
unsafe { *out_count = qty };
MbusDnStatus::MbusOk
}
#[cfg(feature = "fifo")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_read_fifo_queue(
handle: *mut MbusDnTcpClient,
unit_id: u8,
address: u16,
out_buf: *mut u16,
out_buf_len: u16,
out_count: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if out_buf.is_null() || out_count.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
let rt = runtime::get();
let fifo = match rt.block_on(client.read_fifo_queue(unit_id, address)) {
Ok(f) => f,
Err(e) => return status::from_async(e),
};
let values = fifo.queue();
if out_buf_len < values.len() as u16 {
return MbusDnStatus::MbusErrBufferTooSmall;
}
let dst = unsafe { slice::from_raw_parts_mut(out_buf, values.len()) };
dst.copy_from_slice(values);
unsafe { *out_count = values.len() as u16 };
MbusDnStatus::MbusOk
}
#[cfg(feature = "diagnostics")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_read_exception_status(
handle: *mut MbusDnTcpClient,
unit_id: u8,
out_status: *mut u8,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if out_status.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
let rt = runtime::get();
match rt.block_on(client.read_exception_status(unit_id)) {
Ok(s) => {
unsafe { *out_status = s };
MbusDnStatus::MbusOk
}
Err(e) => status::from_async(e),
}
}
#[cfg(feature = "diagnostics")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_get_comm_event_counter(
handle: *mut MbusDnTcpClient,
unit_id: u8,
out_status: *mut u16,
out_event_count: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if out_status.is_null() || out_event_count.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
let rt = runtime::get();
match rt.block_on(client.get_comm_event_counter(unit_id)) {
Ok((st, cnt)) => {
unsafe { *out_status = st };
unsafe { *out_event_count = cnt };
MbusDnStatus::MbusOk
}
Err(e) => status::from_async(e),
}
}
#[cfg(feature = "diagnostics")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_get_comm_event_log(
handle: *mut MbusDnTcpClient,
unit_id: u8,
out_buf: *mut u8,
out_buf_len: u16,
out_count: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if out_buf.is_null() || out_count.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
let rt = runtime::get();
match rt.block_on(client.get_comm_event_log(unit_id)) {
Ok((_st, _ec, _mc, events)) => {
if out_buf_len < events.len() as u16 {
return MbusDnStatus::MbusErrBufferTooSmall;
}
let dst = unsafe { slice::from_raw_parts_mut(out_buf, events.len()) };
dst.copy_from_slice(&events);
unsafe { *out_count = events.len() as u16 };
MbusDnStatus::MbusOk
}
Err(e) => status::from_async(e),
}
}
#[cfg(feature = "diagnostics")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_report_server_id(
handle: *mut MbusDnTcpClient,
unit_id: u8,
out_buf: *mut u8,
out_buf_len: u16,
out_count: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if out_buf.is_null() || out_count.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
let rt = runtime::get();
match rt.block_on(client.report_server_id(unit_id)) {
Ok(data) => {
if out_buf_len < data.len() as u16 {
return MbusDnStatus::MbusErrBufferTooSmall;
}
let dst = unsafe { slice::from_raw_parts_mut(out_buf, data.len()) };
dst.copy_from_slice(&data);
unsafe { *out_count = data.len() as u16 };
MbusDnStatus::MbusOk
}
Err(e) => status::from_async(e),
}
}
#[cfg(feature = "diagnostics")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_diagnostics(
handle: *mut MbusDnTcpClient,
unit_id: u8,
sub_function: u16,
data_in: *const u16,
data_in_count: u16,
out_sub_function: *mut u16,
out_buf: *mut u16,
out_buf_len: u16,
out_count: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if out_sub_function.is_null() || out_buf.is_null() || out_count.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
if data_in_count > 0 && data_in.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
let sf = match DiagnosticSubFunction::try_from(sub_function) {
Ok(s) => s,
Err(e) => return status::from_mbus(e),
};
let words: &[u16] = if data_in_count > 0 {
unsafe { slice::from_raw_parts(data_in, data_in_count as usize) }
} else {
&[]
};
let rt = runtime::get();
match rt.block_on(client.diagnostics(unit_id, sf, words)) {
Ok(resp) => {
if out_buf_len < resp.data.len() as u16 {
return MbusDnStatus::MbusErrBufferTooSmall;
}
let dst = unsafe { slice::from_raw_parts_mut(out_buf, resp.data.len()) };
dst.copy_from_slice(&resp.data);
unsafe { *out_sub_function = u16::from(resp.sub_function) };
unsafe { *out_count = resp.data.len() as u16 };
MbusDnStatus::MbusOk
}
Err(e) => status::from_async(e),
}
}
#[cfg(feature = "file-record")]
#[repr(C)]
pub struct MbusDnSubRequest {
pub file_number: u16,
pub record_number: u16,
pub record_length: u16,
pub data: *const u16,
pub data_len: u16,
}
#[cfg(feature = "file-record")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_read_file_record(
handle: *mut MbusDnTcpClient,
unit_id: u8,
sub_reqs: *const MbusDnSubRequest,
sub_req_count: u16,
out_buf: *mut u16,
out_buf_len: u16,
out_count: *mut u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if sub_reqs.is_null() || out_buf.is_null() || out_count.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
let c_slice = unsafe { slice::from_raw_parts(sub_reqs, sub_req_count as usize) };
let mut sub_request = SubRequest::new();
for sr in c_slice {
if let Err(e) =
sub_request.add_read_sub_request(sr.file_number, sr.record_number, sr.record_length)
{
return status::from_mbus(e);
}
}
let rt = runtime::get();
match rt.block_on(client.read_file_record(unit_id, &sub_request)) {
Ok(results) => {
let total: usize = results
.iter()
.map(|r| r.record_data.as_ref().map_or(0, |d| d.len()))
.sum();
if out_buf_len < total as u16 {
return MbusDnStatus::MbusErrBufferTooSmall;
}
let dst = unsafe { slice::from_raw_parts_mut(out_buf, total) };
let mut pos = 0;
for r in &results {
if let Some(ref d) = r.record_data {
dst[pos..pos + d.len()].copy_from_slice(d.as_slice());
pos += d.len();
}
}
unsafe { *out_count = total as u16 };
MbusDnStatus::MbusOk
}
Err(e) => status::from_async(e),
}
}
#[cfg(feature = "file-record")]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_dn_tcp_client_write_file_record(
handle: *mut MbusDnTcpClient,
unit_id: u8,
sub_reqs: *const MbusDnSubRequest,
sub_req_count: u16,
) -> MbusDnStatus {
let client = match unsafe { handle.as_ref() } {
Some(h) => h.inner.clone(),
None => return MbusDnStatus::MbusErrNullPointer,
};
if sub_reqs.is_null() {
return MbusDnStatus::MbusErrNullPointer;
}
let c_slice = unsafe { slice::from_raw_parts(sub_reqs, sub_req_count as usize) };
let mut sub_request = SubRequest::new();
for sr in c_slice {
if sr.data.is_null() || sr.data_len == 0 {
return MbusDnStatus::MbusErrNullPointer;
}
let word_slice = unsafe { slice::from_raw_parts(sr.data, sr.data_len as usize) };
let mut hvec: HVec<u16, MAX_PDU_DATA_LEN> = HVec::new();
if hvec.extend_from_slice(word_slice).is_err() {
return MbusDnStatus::MbusErrBufferTooSmall;
}
if let Err(e) = sub_request.add_write_sub_request(
sr.file_number,
sr.record_number,
sr.record_length,
hvec,
) {
return status::from_mbus(e);
}
}
let rt = runtime::get();
match rt.block_on(client.write_file_record(unit_id, &sub_request)) {
Ok(()) => MbusDnStatus::MbusOk,
Err(e) => status::from_async(e),
}
}
#[doc(hidden)]
#[unsafe(no_mangle)]
pub extern "C" fn mbus_dn_tcp_client_handle_size() -> usize {
core::mem::size_of::<MbusDnTcpClient>()
}
#[doc(hidden)]
fn _opaque_marker(_p: *mut c_void) {}