| @@ -1330,6 +1330,13 @@ dependencies = [ | |||||
| "windows-sys 0.48.0", | "windows-sys 0.48.0", | ||||
| ] | ] | ||||
| [[package]] | |||||
| name = "dora-arrow-convert" | |||||
| version = "0.2.6" | |||||
| dependencies = [ | |||||
| "arrow", | |||||
| ] | |||||
| [[package]] | [[package]] | ||||
| name = "dora-cli" | name = "dora-cli" | ||||
| version = "0.2.6" | version = "0.2.6" | ||||
| @@ -1483,6 +1490,7 @@ dependencies = [ | |||||
| "arrow-schema", | "arrow-schema", | ||||
| "bincode", | "bincode", | ||||
| "capnp", | "capnp", | ||||
| "dora-arrow-convert", | |||||
| "dora-core", | "dora-core", | ||||
| "dora-tracing", | "dora-tracing", | ||||
| "eyre", | "eyre", | ||||
| @@ -1543,6 +1551,7 @@ dependencies = [ | |||||
| name = "dora-operator-api" | name = "dora-operator-api" | ||||
| version = "0.2.6" | version = "0.2.6" | ||||
| dependencies = [ | dependencies = [ | ||||
| "dora-arrow-convert", | |||||
| "dora-operator-api-macros", | "dora-operator-api-macros", | ||||
| "dora-operator-api-types", | "dora-operator-api-types", | ||||
| ] | ] | ||||
| @@ -15,6 +15,7 @@ members = [ | |||||
| "examples/rust-ros2-dataflow/*", | "examples/rust-ros2-dataflow/*", | ||||
| "examples/benchmark/*", | "examples/benchmark/*", | ||||
| "examples/multiple-daemons/*", | "examples/multiple-daemons/*", | ||||
| "libraries/arrow-convert", | |||||
| "libraries/communication-layer/*", | "libraries/communication-layer/*", | ||||
| "libraries/core", | "libraries/core", | ||||
| "libraries/message", | "libraries/message", | ||||
| @@ -45,6 +46,7 @@ dora-operator-api-python = { version = "0.2.6", path = "apis/python/operator" } | |||||
| dora-operator-api-c = { version = "0.2.6", path = "apis/c/operator" } | dora-operator-api-c = { version = "0.2.6", path = "apis/c/operator" } | ||||
| dora-node-api-c = { version = "0.2.6", path = "apis/c/node" } | dora-node-api-c = { version = "0.2.6", path = "apis/c/node" } | ||||
| dora-core = { version = "0.2.6", path = "libraries/core" } | dora-core = { version = "0.2.6", path = "libraries/core" } | ||||
| dora-arrow-convert = { version = "0.2.6", path = "libraries/arrow-convert" } | |||||
| dora-tracing = { version = "0.2.6", path = "libraries/extensions/telemetry/tracing" } | dora-tracing = { version = "0.2.6", path = "libraries/extensions/telemetry/tracing" } | ||||
| dora-metrics = { version = "0.2.6", path = "libraries/extensions/telemetry/metrics" } | dora-metrics = { version = "0.2.6", path = "libraries/extensions/telemetry/metrics" } | ||||
| dora-download = { version = "0.2.6", path = "libraries/extensions/download" } | dora-download = { version = "0.2.6", path = "libraries/extensions/download" } | ||||
| @@ -1,4 +1,8 @@ | |||||
| use dora_node_api::{self, Event, EventStream}; | |||||
| use dora_node_api::{ | |||||
| self, | |||||
| arrow::array::{AsArray, BinaryArray}, | |||||
| Event, EventStream, | |||||
| }; | |||||
| use eyre::bail; | use eyre::bail; | ||||
| #[cxx::bridge] | #[cxx::bridge] | ||||
| @@ -81,9 +85,10 @@ fn event_as_input(event: Box<DoraEvent>) -> eyre::Result<ffi::DoraInput> { | |||||
| let Some(Event::Input { id, metadata: _, data }) = event.0 else { | let Some(Event::Input { id, metadata: _, data }) = event.0 else { | ||||
| bail!("not an input event"); | bail!("not an input event"); | ||||
| }; | }; | ||||
| let data: Option<&BinaryArray> = data.as_binary_opt(); | |||||
| Ok(ffi::DoraInput { | Ok(ffi::DoraInput { | ||||
| id: id.into(), | id: id.into(), | ||||
| data: data.map(|d| d.to_owned()).unwrap_or_default(), | |||||
| data: data.map(|d| d.value(0).to_owned()).unwrap_or_default(), | |||||
| }) | }) | ||||
| } | } | ||||
| @@ -2,7 +2,9 @@ | |||||
| #![warn(unsafe_op_in_unsafe_fn)] | #![warn(unsafe_op_in_unsafe_fn)] | ||||
| use dora_operator_api::{ | use dora_operator_api::{ | ||||
| self, register_operator, DoraOperator, DoraOutputSender, DoraStatus, Event, | |||||
| self, register_operator, | |||||
| types::arrow::array::{AsArray, BinaryArray}, | |||||
| DoraOperator, DoraOutputSender, DoraStatus, Event, | |||||
| }; | }; | ||||
| use ffi::DoraSendOutputResult; | use ffi::DoraSendOutputResult; | ||||
| @@ -45,7 +47,7 @@ pub struct OutputSender<'a, 'b>(&'a mut DoraOutputSender<'b>); | |||||
| fn send_output(sender: &mut OutputSender, id: &str, data: &[u8]) -> DoraSendOutputResult { | fn send_output(sender: &mut OutputSender, id: &str, data: &[u8]) -> DoraSendOutputResult { | ||||
| let error = sender | let error = sender | ||||
| .0 | .0 | ||||
| .send(id.into(), data.to_owned()) | |||||
| .send(id.into(), data.to_owned().into()) | |||||
| .err() | .err() | ||||
| .unwrap_or_default(); | .unwrap_or_default(); | ||||
| DoraSendOutputResult { error } | DoraSendOutputResult { error } | ||||
| @@ -75,6 +77,7 @@ impl DoraOperator for OperatorWrapper { | |||||
| Event::Input { id, data } => { | Event::Input { id, data } => { | ||||
| let operator = self.operator.as_mut().unwrap(); | let operator = self.operator.as_mut().unwrap(); | ||||
| let mut output_sender = OutputSender(output_sender); | let mut output_sender = OutputSender(output_sender); | ||||
| let data: &BinaryArray = data.as_binary(); | |||||
| let result = ffi::on_input(operator, id, data, &mut output_sender); | let result = ffi::on_input(operator, id, data, &mut output_sender); | ||||
| if result.error.is_empty() { | if result.error.is_empty() { | ||||
| @@ -30,6 +30,7 @@ arrow = "45.0.0" | |||||
| arrow-schema = "45.0.0" | arrow-schema = "45.0.0" | ||||
| futures = "0.3.28" | futures = "0.3.28" | ||||
| futures-concurrency = "7.3.0" | futures-concurrency = "7.3.0" | ||||
| dora-arrow-convert = { workspace = true } | |||||
| [dev-dependencies] | [dev-dependencies] | ||||
| tokio = { version = "1.24.2", features = ["rt"] } | tokio = { version = "1.24.2", features = ["rt"] } | ||||
| @@ -14,6 +14,7 @@ | |||||
| //! ``` | //! ``` | ||||
| //! | //! | ||||
| pub use arrow; | pub use arrow; | ||||
| pub use dora_arrow_convert::*; | |||||
| pub use dora_core; | pub use dora_core; | ||||
| pub use dora_core::message::{uhlc, Metadata, MetadataParameters}; | pub use dora_core::message::{uhlc, Metadata, MetadataParameters}; | ||||
| pub use event_stream::{merged, Event, EventStream, MappedInputData}; | pub use event_stream::{merged, Event, EventStream, MappedInputData}; | ||||
| @@ -11,3 +11,4 @@ license.workspace = true | |||||
| [dependencies] | [dependencies] | ||||
| dora-operator-api-macros = { workspace = true } | dora-operator-api-macros = { workspace = true } | ||||
| dora-operator-api-types = { workspace = true } | dora-operator-api-types = { workspace = true } | ||||
| dora-arrow-convert = { workspace = true } | |||||
| @@ -18,6 +18,7 @@ | |||||
| #![warn(unsafe_op_in_unsafe_fn)] | #![warn(unsafe_op_in_unsafe_fn)] | ||||
| #![allow(clippy::missing_safety_doc)] | #![allow(clippy::missing_safety_doc)] | ||||
| pub use dora_arrow_convert::*; | |||||
| pub use dora_operator_api_macros::register_operator; | pub use dora_operator_api_macros::register_operator; | ||||
| pub use dora_operator_api_types as types; | pub use dora_operator_api_types as types; | ||||
| pub use types::DoraStatus; | pub use types::DoraStatus; | ||||
| @@ -1,11 +1,4 @@ | |||||
| use std::iter; | |||||
| use dora_node_api::{ | |||||
| self, | |||||
| arrow::{array::PrimitiveArray, datatypes::UInt64Type}, | |||||
| dora_core::config::DataId, | |||||
| DoraNode, Event, | |||||
| }; | |||||
| use dora_node_api::{self, dora_core::config::DataId, DoraNode, Event, IntoArrow}; | |||||
| fn main() -> eyre::Result<()> { | fn main() -> eyre::Result<()> { | ||||
| println!("hello"); | println!("hello"); | ||||
| @@ -29,8 +22,7 @@ fn main() -> eyre::Result<()> { | |||||
| "tick" => { | "tick" => { | ||||
| let random: u64 = rand::random(); | let random: u64 = rand::random(); | ||||
| println!("tick {i}, sending {random:#x}"); | println!("tick {i}, sending {random:#x}"); | ||||
| let data: PrimitiveArray<UInt64Type> = iter::once(random).collect(); | |||||
| node.send_output(output.clone(), metadata.parameters, data)?; | |||||
| node.send_output(output.clone(), metadata.parameters, random.into_arrow())?; | |||||
| } | } | ||||
| other => eprintln!("Ignoring unexpected input `{other}`"), | other => eprintln!("Ignoring unexpected input `{other}`"), | ||||
| }, | }, | ||||
| @@ -2,11 +2,8 @@ | |||||
| use dora_operator_api::{ | use dora_operator_api::{ | ||||
| register_operator, | register_operator, | ||||
| types::arrow::{ | |||||
| array::{AsArray, StringArray}, | |||||
| datatypes::UInt64Type, | |||||
| }, | |||||
| DoraOperator, DoraOutputSender, DoraStatus, Event, | |||||
| types::arrow::{array::AsArray, datatypes::UInt64Type}, | |||||
| DoraOperator, DoraOutputSender, DoraStatus, Event, IntoArrow, | |||||
| }; | }; | ||||
| register_operator!(ExampleOperator); | register_operator!(ExampleOperator); | ||||
| @@ -33,11 +30,11 @@ impl DoraOperator for ExampleOperator { | |||||
| .ok_or_else(|| "expected u64 value".to_owned())? | .ok_or_else(|| "expected u64 value".to_owned())? | ||||
| .value(0); | .value(0); | ||||
| let output = StringArray::from_iter(std::iter::once(Some(format!( | |||||
| let output = format!( | |||||
| "operator received random value {data:#x} after {} ticks", | "operator received random value {data:#x} after {} ticks", | ||||
| self.ticks | self.ticks | ||||
| )))); | |||||
| output_sender.send("status".into(), output)?; | |||||
| ); | |||||
| output_sender.send("status".into(), output.into_arrow())?; | |||||
| } | } | ||||
| other => eprintln!("ignoring unexpected input {other}"), | other => eprintln!("ignoring unexpected input {other}"), | ||||
| }, | }, | ||||
| @@ -1,9 +1,4 @@ | |||||
| use dora_node_api::{ | |||||
| self, | |||||
| arrow::{array::PrimitiveBuilder, datatypes::UInt64Type}, | |||||
| dora_core::config::DataId, | |||||
| DoraNode, Event, | |||||
| }; | |||||
| use dora_node_api::{self, dora_core::config::DataId, DoraNode, Event, IntoArrow}; | |||||
| fn main() -> eyre::Result<()> { | fn main() -> eyre::Result<()> { | ||||
| println!("hello"); | println!("hello"); | ||||
| @@ -27,12 +22,7 @@ fn main() -> eyre::Result<()> { | |||||
| "tick" => { | "tick" => { | ||||
| let random: u64 = rand::random(); | let random: u64 = rand::random(); | ||||
| println!("tick {i}, sending {random:#x}"); | println!("tick {i}, sending {random:#x}"); | ||||
| let data = { | |||||
| let mut builder: PrimitiveBuilder<UInt64Type> = PrimitiveBuilder::new(); | |||||
| builder.append_value(random); | |||||
| builder.finish() | |||||
| }; | |||||
| node.send_output(output.clone(), metadata.parameters, data)?; | |||||
| node.send_output(output.clone(), metadata.parameters, random.into_arrow())?; | |||||
| } | } | ||||
| other => eprintln!("Ignoring unexpected input `{other}`"), | other => eprintln!("Ignoring unexpected input `{other}`"), | ||||
| }, | }, | ||||
| @@ -3,10 +3,10 @@ | |||||
| use dora_operator_api::{ | use dora_operator_api::{ | ||||
| register_operator, | register_operator, | ||||
| types::arrow::{ | types::arrow::{ | ||||
| array::{AsArray, PrimitiveArray, StringBuilder}, | |||||
| array::{AsArray, PrimitiveArray}, | |||||
| datatypes::UInt64Type, | datatypes::UInt64Type, | ||||
| }, | }, | ||||
| DoraOperator, DoraOutputSender, DoraStatus, Event, | |||||
| DoraOperator, DoraOutputSender, DoraStatus, Event, IntoArrow, | |||||
| }; | }; | ||||
| register_operator!(ExampleOperator); | register_operator!(ExampleOperator); | ||||
| @@ -36,12 +36,7 @@ impl DoraOperator for ExampleOperator { | |||||
| "operator received random value {value:#x} after {} ticks", | "operator received random value {value:#x} after {} ticks", | ||||
| self.ticks | self.ticks | ||||
| ); | ); | ||||
| let output_data = { | |||||
| let mut builder = StringBuilder::new(); | |||||
| builder.append_value(output); | |||||
| builder.finish() | |||||
| }; | |||||
| output_sender.send("status".into(), output_data)?; | |||||
| output_sender.send("status".into(), output.into_arrow())?; | |||||
| } | } | ||||
| other => eprintln!("ignoring unexpected input {other}"), | other => eprintln!("ignoring unexpected input {other}"), | ||||
| }, | }, | ||||
| @@ -0,0 +1,12 @@ | |||||
| [package] | |||||
| name = "dora-arrow-convert" | |||||
| version.workspace = true | |||||
| edition = "2021" | |||||
| documentation.workspace = true | |||||
| description.workspace = true | |||||
| license.workspace = true | |||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||||
| [dependencies] | |||||
| arrow = { version = "45.0.0" } | |||||
| @@ -0,0 +1,82 @@ | |||||
| use arrow::array::{Array, PrimitiveArray, StringArray}; | |||||
| pub trait IntoArrow { | |||||
| type A: Array; | |||||
| fn into_arrow(self) -> Self::A; | |||||
| } | |||||
| impl IntoArrow for bool { | |||||
| type A = arrow::array::BooleanArray; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| std::iter::once(Some(self)).collect() | |||||
| } | |||||
| } | |||||
| impl IntoArrow for u8 { | |||||
| type A = PrimitiveArray<arrow::datatypes::UInt8Type>; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| std::iter::once(self).collect() | |||||
| } | |||||
| } | |||||
| impl IntoArrow for u16 { | |||||
| type A = PrimitiveArray<arrow::datatypes::UInt16Type>; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| std::iter::once(self).collect() | |||||
| } | |||||
| } | |||||
| impl IntoArrow for u32 { | |||||
| type A = PrimitiveArray<arrow::datatypes::UInt32Type>; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| std::iter::once(self).collect() | |||||
| } | |||||
| } | |||||
| impl IntoArrow for u64 { | |||||
| type A = PrimitiveArray<arrow::datatypes::UInt64Type>; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| std::iter::once(self).collect() | |||||
| } | |||||
| } | |||||
| impl IntoArrow for i8 { | |||||
| type A = PrimitiveArray<arrow::datatypes::Int8Type>; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| std::iter::once(self).collect() | |||||
| } | |||||
| } | |||||
| impl IntoArrow for i16 { | |||||
| type A = PrimitiveArray<arrow::datatypes::Int16Type>; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| std::iter::once(self).collect() | |||||
| } | |||||
| } | |||||
| impl IntoArrow for i32 { | |||||
| type A = PrimitiveArray<arrow::datatypes::Int32Type>; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| std::iter::once(self).collect() | |||||
| } | |||||
| } | |||||
| impl IntoArrow for i64 { | |||||
| type A = PrimitiveArray<arrow::datatypes::Int64Type>; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| std::iter::once(self).collect() | |||||
| } | |||||
| } | |||||
| impl IntoArrow for f32 { | |||||
| type A = PrimitiveArray<arrow::datatypes::Float32Type>; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| std::iter::once(self).collect() | |||||
| } | |||||
| } | |||||
| impl IntoArrow for f64 { | |||||
| type A = PrimitiveArray<arrow::datatypes::Float64Type>; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| std::iter::once(self).collect() | |||||
| } | |||||
| } | |||||
| impl IntoArrow for &str { | |||||
| type A = StringArray; | |||||
| fn into_arrow(self) -> Self::A { | |||||
| std::iter::once(Some(self)).collect() | |||||
| } | |||||
| } | |||||