use crate::c::error::MbusStatusCode;
use crate::c::transport::{CTcpTransport, MbusTransportCallbacks, validate_transport_callbacks};
use mbus_gateway::{DownstreamChannel, GatewayServices};
use super::callbacks::MbusGatewayCallbacks;
use super::event_adapter::CGatewayEventAdapter;
use super::pool::{GatewayInner, MAX_DOWNSTREAM_CHANNELS, pool_allocate, pool_free, with_gateway};
use super::routing::CGatewayRouter;
pub use super::pool::{MBUS_INVALID_GATEWAY_ID, MbusGatewayId};
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_gateway_new(
upstream: *const MbusTransportCallbacks,
events: *const MbusGatewayCallbacks,
out_id: *mut MbusGatewayId,
) -> MbusStatusCode {
if upstream.is_null() || out_id.is_null() {
return MbusStatusCode::MbusErrNullPointer;
}
unsafe { *out_id = MBUS_INVALID_GATEWAY_ID };
let upstream_cb = unsafe { core::ptr::read(upstream) };
if !validate_transport_callbacks(&upstream_cb) {
return MbusStatusCode::MbusErrInvalidConfiguration;
}
let event_cb = if events.is_null() {
MbusGatewayCallbacks::default()
} else {
unsafe { core::ptr::read(events) }
};
let upstream_transport = CTcpTransport::new(upstream_cb);
let router = CGatewayRouter::new();
let event_adapter = CGatewayEventAdapter::new(event_cb);
let mut services: GatewayInner = GatewayServices::new(router, event_adapter, 1000);
if services.add_upstream(upstream_transport).is_err() {
return MbusStatusCode::MbusErrInvalidConfiguration;
}
let services: GatewayInner = services;
match pool_allocate(services) {
Ok(id) => {
unsafe { *out_id = id };
MbusStatusCode::MbusOk
}
Err(status) => status,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn mbus_gateway_add_downstream(
id: MbusGatewayId,
callbacks: *const MbusTransportCallbacks,
out_channel_idx: *mut u16,
) -> MbusStatusCode {
if callbacks.is_null() || out_channel_idx.is_null() {
return MbusStatusCode::MbusErrNullPointer;
}
let cb = unsafe { core::ptr::read(callbacks) };
if !validate_transport_callbacks(&cb) {
return MbusStatusCode::MbusErrInvalidConfiguration;
}
let result = with_gateway(id, |gw| {
let transport = CTcpTransport::new(cb);
let channel = DownstreamChannel::new(transport);
match gw.add_downstream(channel) {
Ok(()) => {
let new_idx = (gw.downstream_count() - 1) as u16;
Ok(new_idx)
}
Err(e) => Err(MbusStatusCode::from(e)),
}
});
match result {
Ok(Ok(idx)) => {
unsafe { *out_channel_idx = idx };
MbusStatusCode::MbusOk
}
Ok(Err(status)) => status,
Err(status) => status,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn mbus_gateway_add_unit_route(
id: MbusGatewayId,
unit_id: u8,
channel_idx: u16,
) -> MbusStatusCode {
if (channel_idx as usize) >= MAX_DOWNSTREAM_CHANNELS {
return MbusStatusCode::MbusErrInvalidConfiguration;
}
let result = with_gateway(id, |gw| {
if (channel_idx as usize) >= gw.downstream_count() {
return MbusStatusCode::MbusErrInvalidConfiguration;
}
if gw.router_mut().add_unit(unit_id, channel_idx as usize) {
MbusStatusCode::MbusOk
} else {
MbusStatusCode::MbusErrInvalidConfiguration
}
});
result.unwrap_or_else(|s| s)
}
#[unsafe(no_mangle)]
pub extern "C" fn mbus_gateway_add_range_route(
id: MbusGatewayId,
unit_min: u8,
unit_max: u8,
channel_idx: u16,
) -> MbusStatusCode {
if (channel_idx as usize) >= MAX_DOWNSTREAM_CHANNELS {
return MbusStatusCode::MbusErrInvalidConfiguration;
}
let result = with_gateway(id, |gw| {
if (channel_idx as usize) >= gw.downstream_count() {
return MbusStatusCode::MbusErrInvalidConfiguration;
}
if gw
.router_mut()
.add_range(unit_min, unit_max, channel_idx as usize)
{
MbusStatusCode::MbusOk
} else {
MbusStatusCode::MbusErrInvalidConfiguration
}
});
result.unwrap_or_else(|s| s)
}
#[unsafe(no_mangle)]
pub extern "C" fn mbus_gateway_poll(id: MbusGatewayId) -> MbusStatusCode {
let now_ms = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64;
let result = with_gateway(id, |gw| {
gw.poll(now_ms);
MbusStatusCode::MbusOk
});
result.unwrap_or_else(|s| s)
}
#[unsafe(no_mangle)]
pub extern "C" fn mbus_gateway_free(id: MbusGatewayId) -> MbusStatusCode {
if pool_free(id) {
MbusStatusCode::MbusOk
} else {
MbusStatusCode::MbusErrInvalidClientId
}
}