You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

lib.rs 3.4 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. use dora_node_api::config::{DataId, NodeId};
  2. use dora_node_api::{DoraNode, Input};
  3. use eyre::{Context, Result};
  4. use pyo3::prelude::*;
  5. use pyo3::types::PyBytes;
  6. use std::sync::Arc;
  7. use std::thread;
  8. use tokio::sync::mpsc;
  9. use tokio::sync::mpsc::{Receiver, Sender};
  10. #[pyclass]
  11. pub struct Node {
  12. id: NodeId,
  13. pub rx_input: Receiver<Input>,
  14. pub tx_output: Sender<(String, Vec<u8>)>,
  15. }
  16. pub struct PyInput(Input);
  17. impl IntoPy<PyObject> for PyInput {
  18. fn into_py(self, py: Python) -> PyObject {
  19. (self.0.id.to_string(), PyBytes::new(py, &self.0.data)).into_py(py)
  20. }
  21. }
  22. #[pymethods]
  23. impl Node {
  24. #[new]
  25. pub fn new() -> Result<Self> {
  26. let id = {
  27. let raw =
  28. std::env::var("DORA_NODE_ID").wrap_err("env variable DORA_NODE_ID must be set")?;
  29. serde_yaml::from_str(&raw).context("failed to deserialize operator config")?
  30. };
  31. let (tx_input, rx_input) = mpsc::channel(1);
  32. let (tx_output, mut rx_output) = mpsc::channel::<(String, Vec<u8>)>(1);
  33. // Dispatching a tokio threadpool enables us to conveniently use Dora Future stream
  34. // through tokio channel.
  35. // It would have been difficult to expose the FutureStream of Dora directly.
  36. thread::spawn(move || -> Result<()> {
  37. let rt = tokio::runtime::Builder::new_multi_thread().build()?;
  38. let node = Arc::new(DoraNode::init_from_env()?);
  39. let _node = node.clone();
  40. let receive_handle = tokio::task::spawn_blocking(async move {
  41. let mut inputs = _node.inputs().unwrap();
  42. while let Ok(input) = inputs.recv() {
  43. tx_input.blocking_send(input)?
  44. }
  45. Result::<_, eyre::Error>::Ok(())
  46. });
  47. let send_handle = tokio::task::spawn_blocking(async move {
  48. while let Some((output_str, data)) = rx_output.recv().await {
  49. let output_id = DataId::from(output_str);
  50. node.send_output(&output_id, data.as_slice())?
  51. }
  52. Result::<_, eyre::Error>::Ok(())
  53. });
  54. let (receiver, sender) = tokio::join!(receive_handle, send_handle);
  55. receiver
  56. .wrap_err("Handle to the receiver failed")?
  57. .wrap_err("Receiving messages from receiver channel failed")?;
  58. sender
  59. .wrap_err("Handle to the sender failed")?
  60. .wrap_err("Sending messages using sender channel failed")?;
  61. Ok(())
  62. });
  63. Ok(Node {
  64. id,
  65. rx_input,
  66. tx_output,
  67. })
  68. }
  69. #[allow(clippy::should_implement_trait)]
  70. pub fn next(&mut self) -> PyResult<Option<PyInput>> {
  71. self.__next__()
  72. }
  73. pub fn __next__(&mut self) -> PyResult<Option<PyInput>> {
  74. Ok(self.rx_input.blocking_recv().map(PyInput))
  75. }
  76. fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
  77. slf
  78. }
  79. pub fn send_output(&self, output_str: String, data: &PyBytes) -> Result<()> {
  80. self.tx_output
  81. .blocking_send((output_str, data.as_bytes().to_vec()))
  82. .wrap_err("Could not send output")
  83. }
  84. pub fn id(&self) -> String {
  85. self.id.to_string()
  86. }
  87. }
  88. #[pymodule]
  89. fn dora(_py: Python, m: &PyModule) -> PyResult<()> {
  90. m.add_class::<Node>().unwrap();
  91. Ok(())
  92. }

DORA (Dataflow-Oriented Robotic Architecture) is middleware designed to streamline and simplify the creation of AI-based robotic applications. It offers low latency, composable, and distributed datafl