use std::sync::OnceLock;
use mbus_core::models::coil::Coils;
use mbus_core::models::discrete_input::DiscreteInputs;
use mbus_core::models::fifo_queue::FifoQueue;
use mbus_core::models::register::{HoldingRegisters, InputRegisters};
use pyo3::prelude::*;
use tokio::runtime::Runtime;
static TOKIO_RT: OnceLock<Runtime> = OnceLock::new();
pub fn get_runtime() -> &'static Runtime {
TOKIO_RT.get_or_init(|| {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("Failed to create Tokio runtime for modbus_rs")
})
}
pub fn enter_runtime() -> tokio::runtime::EnterGuard<'static> {
get_runtime().enter()
}
pub fn coils_to_py(py: Python<'_>, coils: Coils) -> PyResult<Py<PyAny>> {
let qty = coils.quantity(); let base = coils.from_address();
let list: Vec<bool> = (0..qty)
.map(|i| coils.value(base + i).unwrap_or(false))
.collect();
Ok(list.into_pyobject(py)?.into_any().unbind())
}
pub fn discrete_inputs_to_py(py: Python<'_>, di: DiscreteInputs) -> PyResult<Py<PyAny>> {
let qty = di.quantity(); let base = di.from_address();
let list: Vec<bool> = (0..qty)
.map(|i| di.value(base + i).unwrap_or(false))
.collect();
Ok(list.into_pyobject(py)?.into_any().unbind())
}
macro_rules! impl_registers_to_py {
($name:ident, $type:ident) => {
#[doc = concat!("Convert a `", stringify!($type), "` value into a Python `list[int]`.")]
pub fn $name<const N: usize>(py: Python<'_>, regs: $type<N>) -> PyResult<Py<PyAny>> {
let qty = regs.quantity(); let base = regs.from_address();
let list: Vec<u16> = (0..qty)
.map(|i| regs.value(base + i).unwrap_or(0))
.collect();
Ok(list.into_pyobject(py)?.into_any().unbind())
}
};
}
impl_registers_to_py!(holding_registers_to_py, HoldingRegisters);
impl_registers_to_py!(input_registers_to_py, InputRegisters);
pub fn fifo_to_py(py: Python<'_>, queue: FifoQueue) -> PyResult<Py<PyAny>> {
let list: Vec<u16> = queue.queue().to_vec();
Ok(list.into_pyobject(py)?.into_any().unbind())
}
pub use crate::python::errors::async_error_to_py;