use futuresdr_types::PmtConversionError;
use std::fmt;
use std::fmt::Display;
use std::fmt::Formatter;
use thiserror::Error;
use crate::runtime::channel::mpsc;
use crate::runtime::channel::oneshot;
mod add_to_flowgraph;
mod block;
mod block_inbox;
mod block_meta;
pub mod buffer;
pub mod channel;
pub mod config;
pub mod dev;
#[cfg(all(not(target_arch = "wasm32"), feature = "ctrl_port"))]
mod ctrl_port;
#[cfg(all(not(target_arch = "wasm32"), feature = "ctrl_port"))]
use crate::runtime::ctrl_port::ControlPort;
mod logging;
mod flowgraph;
mod flowgraph_handle;
mod flowgraph_task;
mod kernel;
mod kernel_interface;
#[cfg(not(target_arch = "wasm32"))]
mod local_domain;
#[cfg(target_arch = "wasm32")]
#[path = "local_domain_wasm.rs"]
mod local_domain;
mod local_domain_common;
mod message_output;
#[cfg(all(not(target_arch = "wasm32"), feature = "mocker"))]
pub mod mocker;
mod running_flowgraph;
#[allow(clippy::module_inception)]
mod runtime;
pub mod scheduler;
mod tag;
mod timer;
mod work_io;
mod wrapped_kernel;
pub mod macros {
pub use futuresdr_macros::Block;
pub use futuresdr_macros::connect;
pub use futuresdr_macros::connect_async;
}
pub use flowgraph::BlockRef;
pub use flowgraph::Flowgraph;
pub use flowgraph::LocalDomain;
pub use flowgraph::LocalDomainContext;
pub use flowgraph::TerminatedFlowgraph;
pub use flowgraph_handle::FlowgraphBlockHandle;
pub use flowgraph_handle::FlowgraphHandle;
pub use flowgraph_task::FlowgraphTask;
pub use running_flowgraph::RunningFlowgraph;
pub use runtime::DefaultScheduler;
pub use runtime::Runtime;
pub use runtime::RuntimeHandle;
pub use timer::Timer;
pub use futuresdr_types::BlockDescription;
pub use futuresdr_types::BlockId;
pub use futuresdr_types::BlockStatus;
pub use futuresdr_types::Edge;
pub use futuresdr_types::FlowgraphDescription;
pub use futuresdr_types::FlowgraphId;
pub use futuresdr_types::Pmt;
pub use futuresdr_types::PmtKind;
pub use futuresdr_types::PortId;
pub use futuresdr_types::PortIndex;
pub use futuresdr_types::PortName;
pub(crate) fn port_id_matches(port_id: &PortId, index: usize, name: &str) -> bool {
match port_id {
PortId::Index(port_index) => port_index.index() == index,
PortId::Name(port_name) => port_name.as_str() == name,
}
}
pub(crate) fn resolve_port_index<N: AsRef<str>>(
port_id: &PortId,
names: &[N],
) -> Option<PortIndex> {
match port_id {
PortId::Index(index) => (index.index() < names.len()).then_some(*index),
PortId::Name(name) => names
.iter()
.position(|candidate| candidate.as_ref() == name.as_str())
.map(PortIndex::new),
}
}
pub(crate) fn resolve_port_name<N: AsRef<str>>(port_id: &PortId, names: &[N]) -> Option<PortId> {
resolve_port_index(port_id, names).map(|index| PortId::new(names[index.index()].as_ref()))
}
#[cfg(not(target_arch = "wasm32"))]
pub fn block_on<T>(future: impl std::future::Future<Output = T>) -> T {
async_io::block_on(future)
}
#[doc(hidden)]
pub mod __private {
pub use super::add_to_flowgraph::AddToFlowgraph;
pub use super::kernel_interface::KernelInterface;
}
pub type Result<T, E = anyhow::Error> = anyhow::Result<T, E>;
#[cfg(target_arch = "wasm32")]
pub(crate) fn yield_now() -> impl std::future::Future<Output = ()> + Unpin {
WasmYieldNow(false)
}
#[cfg(target_arch = "wasm32")]
pub(crate) fn wasm_event_loop_yield() -> impl std::future::Future<Output = ()> + Send + Unpin {
WasmEventLoopYield(false)
}
#[cfg(target_arch = "wasm32")]
struct WasmEventLoopYield(bool);
#[cfg(target_arch = "wasm32")]
impl std::future::Future for WasmEventLoopYield {
type Output = ();
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
if !self.0 {
self.0 = true;
let waker = cx.waker().clone();
let callback = wasm_bindgen::closure::Closure::once_into_js(move || waker.wake());
let function = wasm_bindgen::JsCast::unchecked_ref::<js_sys::Function>(&callback);
if let Some(window) = web_sys::window() {
let _ = window.set_timeout_with_callback_and_timeout_and_arguments_0(function, 1);
} else if let Ok(scope) =
wasm_bindgen::JsCast::dyn_into::<web_sys::WorkerGlobalScope>(js_sys::global())
{
let _ = scope.set_timeout_with_callback_and_timeout_and_arguments_0(function, 1);
} else {
cx.waker().wake_by_ref();
}
std::task::Poll::Pending
} else {
std::task::Poll::Ready(())
}
}
}
#[cfg(target_arch = "wasm32")]
struct WasmYieldNow(bool);
#[cfg(target_arch = "wasm32")]
impl std::future::Future for WasmYieldNow {
type Output = ();
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
if !self.0 {
self.0 = true;
cx.waker().wake_by_ref();
std::task::Poll::Pending
} else {
std::task::Poll::Ready(())
}
}
}
pub fn init() {
#[cfg(target_arch = "wasm32")]
console_error_panic_hook::set_once();
logging::init();
}
#[derive(Debug)]
pub(crate) enum FlowgraphMessage {
Terminate,
Initialized,
BlockDone {
block_id: BlockId,
},
BlockError {
block_id: BlockId,
error: Error,
},
}
#[doc(hidden)]
#[derive(Debug)]
pub(crate) enum BlockMessage {
Initialize,
Start,
Terminate,
StreamInputDone {
input_id: PortIndex,
},
StreamOutputDone {
output_id: PortIndex,
},
Post {
port_id: PortIndex,
data: Pmt,
},
Call {
port_id: PortIndex,
data: Pmt,
tx: oneshot::Sender<Result<Pmt, Error>>,
},
}
#[derive(Error, Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
#[error("Block {:?} does not exist", 0)]
InvalidBlock(BlockId),
#[error("Flowgraph terminated")]
FlowgraphTerminated,
#[error("Block '{0}' does not have message port '{1:?}'")]
InvalidMessagePort(BlockPortCtx, PortId),
#[error("Block '{0}' does not have stream port '{1:?}'")]
InvalidStreamPort(BlockPortCtx, PortId),
#[error("Invalid Parameter")]
InvalidParameter,
#[error("Error in message handler: {0}")]
HandlerError(String),
#[error("Block already terminated")]
BlockTerminated,
#[error("Runtime error ({0})")]
RuntimeError(String),
#[error("Validation error {0}")]
ValidationError(String),
#[error("PMT conversion error")]
PmtConversionError,
#[cfg(feature = "seify")]
#[error("Seify Args conversion error")]
SeifyArgsConversionError,
#[cfg(feature = "seify")]
#[error("Seify error ({0})")]
SeifyError(String),
}
impl From<anyhow::Error> for Error {
fn from(value: anyhow::Error) -> Self {
match value.downcast::<Self>() {
Ok(error) => error,
Err(error) => Self::RuntimeError(error.to_string()),
}
}
}
#[cfg(feature = "seify")]
impl From<seify::Error> for Error {
fn from(value: seify::Error) -> Self {
Error::SeifyError(value.to_string())
}
}
impl From<oneshot::Canceled> for Error {
fn from(_value: oneshot::Canceled) -> Self {
Error::RuntimeError(
"Couldn't receive from oneshot channel, sender dropped unexpectedly".to_string(),
)
}
}
impl From<mpsc::SendError> for Error {
fn from(_value: mpsc::SendError) -> Self {
Error::RuntimeError(
"Couldn't send to mpsc channel, receiver dropped unexpectedly".to_string(),
)
}
}
impl<T> From<mpsc::TrySendError<T>> for Error {
fn from(_value: mpsc::TrySendError<T>) -> Self {
let message = match _value {
mpsc::TrySendError::Full(_) => "Couldn't send to mpsc channel, channel is full",
mpsc::TrySendError::Disconnected(_) => {
"Couldn't send to mpsc channel, receiver dropped unexpectedly"
}
};
Error::RuntimeError(message.to_string())
}
}
impl From<PmtConversionError> for Error {
fn from(_value: PmtConversionError) -> Self {
Error::PmtConversionError
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BlockPortCtx {
None,
Id(BlockId),
}
impl Display for BlockPortCtx {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
BlockPortCtx::None => write!(f, "<None>"),
BlockPortCtx::Id(id) => write!(f, "{id:?}"),
}
}
}