| @@ -0,0 +1,56 @@ | |||
| cmake_minimum_required(VERSION 3.21) | |||
| project(cxx-dataflow LANGUAGES CXX) | |||
| set(CMAKE_CXX_STANDARD 17) | |||
| set(CMAKE_CXX_FLAGS "-fPIC") | |||
| set(dora_cxx_include_dir "${CMAKE_CURRENT_BINARY_DIR}/include/cxx") | |||
| set(node_bridge "${CMAKE_CURRENT_BINARY_DIR}/node_bridge.cc") | |||
| include(ExternalProject) | |||
| ExternalProject_Add(Dora | |||
| PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dora | |||
| GIT_REPOSITORY https://github.com/dora-rs/dora.git | |||
| GIT_TAG main | |||
| BUILD_IN_SOURCE True | |||
| CONFIGURE_COMMAND "" | |||
| BUILD_COMMAND | |||
| cargo build | |||
| --package dora-node-api-cxx | |||
| --target-dir ${CMAKE_CURRENT_BINARY_DIR}/dora/src/Dora/target | |||
| INSTALL_COMMAND "" | |||
| ) | |||
| add_custom_command(OUTPUT ${node_bridge} ${dora_cxx_include_dir} | |||
| WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dora/src/Dora/target | |||
| DEPENDS Dora | |||
| COMMAND | |||
| mkdir ${dora_cxx_include_dir} -p | |||
| && | |||
| cp cxxbridge/dora-node-api-cxx/src/lib.rs.cc ${node_bridge} | |||
| && | |||
| cp cxxbridge/dora-node-api-cxx/src/lib.rs.h ${dora_cxx_include_dir}/dora-node-api.h | |||
| ) | |||
| set(dora_link_dirs ${CMAKE_CURRENT_BINARY_DIR}/dora/src/Dora/target/debug) | |||
| add_custom_target(Dora_cxx DEPENDS ${node_bridge} ${dora_cxx_include_dir}) | |||
| link_directories(${dora_link_dirs}) | |||
| add_executable(talker_1 talker_1/node.cc ${node_bridge}) | |||
| add_dependencies(talker_1 Dora_cxx) | |||
| target_include_directories(talker_1 PRIVATE ${dora_cxx_include_dir}) | |||
| target_link_libraries(talker_1 dora_node_api_cxx) | |||
| add_executable(talker_2 talker_2/node.cc ${node_bridge}) | |||
| add_dependencies(talker_2 Dora_cxx) | |||
| target_include_directories(talker_2 PRIVATE ${dora_cxx_include_dir}) | |||
| target_link_libraries(talker_2 dora_node_api_cxx) | |||
| add_executable(listener_1 listener_1/node.cc ${node_bridge}) | |||
| add_dependencies(listener_1 Dora_cxx) | |||
| target_include_directories(listener_1 PRIVATE ${dora_cxx_include_dir}) | |||
| target_link_libraries(listener_1 dora_node_api_cxx) | |||
| install(TARGETS listener_1 talker_1 talker_2 DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/bin) | |||
| @@ -1,23 +1,23 @@ | |||
| nodes: | |||
| - id: runtime-node_1 | |||
| operators: | |||
| - id: op_1 | |||
| shared-library: build/op_1 | |||
| inputs: | |||
| tick: dora/timer/millis/100 | |||
| outputs: | |||
| - some-output | |||
| - id: op_2 | |||
| shared-library: build/op_2 | |||
| inputs: | |||
| tick: dora/timer/secs/2 | |||
| outputs: | |||
| - some-output | |||
| - id: talker_1 | |||
| custom: | |||
| source: build/talker_1 | |||
| inputs: | |||
| tick: dora/timer/millis/100 | |||
| outputs: | |||
| - speech | |||
| - id: talker_2 | |||
| custom: | |||
| source: build/talker_2 | |||
| inputs: | |||
| tick: dora/timer/secs/2 | |||
| outputs: | |||
| - speech | |||
| - id: custom-node_1 | |||
| - id: listener_1 | |||
| custom: | |||
| source: build/node_1 | |||
| source: build/listener_1 | |||
| inputs: | |||
| tick: dora/timer/secs/1 | |||
| input-1: op_1/some-output | |||
| input-2: op_2/some-output | |||
| input-1: talker_1/speech | |||
| input-2: talker_2/speech | |||
| @@ -0,0 +1,34 @@ | |||
| #include "dora-node-api.h" // adjust this path if necessary | |||
| #include <iostream> | |||
| #include <vector> | |||
| int main() | |||
| { | |||
| std::cout << "HELLO FROM C++" << std::endl; | |||
| unsigned char counter = 0; | |||
| auto dora_node = init_dora_node(); | |||
| while (1) | |||
| { | |||
| auto event = dora_node.events->next(); | |||
| auto ty = event_type(event); | |||
| if (ty == DoraEventType::AllInputsClosed) | |||
| { | |||
| break; | |||
| } | |||
| else if (ty == DoraEventType::Input) | |||
| { | |||
| auto input = event_as_input(std::move(event)); | |||
| auto input_id = input.id; | |||
| std::cout << "I heard from " << std::string(input_id) << std::endl; | |||
| } | |||
| else { | |||
| std::cerr << "Unknown event type " << static_cast<int>(ty) << std::endl; | |||
| } | |||
| } | |||
| return 0;; | |||
| } | |||
| @@ -4,6 +4,10 @@ use std::{ | |||
| path::{Path, PathBuf}, | |||
| }; | |||
| const NODE: &str = include_str!("node-template.cc"); | |||
| const TALKER: &str = include_str!("talker-template.cc"); | |||
| const LISTENER: &str = include_str!("listener-template.cc"); | |||
| pub fn create(args: crate::CommandNew) -> eyre::Result<()> { | |||
| let crate::CommandNew { | |||
| kind, | |||
| @@ -14,7 +18,7 @@ pub fn create(args: crate::CommandNew) -> eyre::Result<()> { | |||
| match kind { | |||
| crate::Kind::Operator => { bail!("Operators are going to be depreciated, please don't use it") }, | |||
| crate::Kind::CustomNode => create_custom_node(name, path), | |||
| crate::Kind::CustomNode => create_custom_node(name, path, NODE), | |||
| crate::Kind::Dataflow => create_dataflow(name, path), | |||
| } | |||
| } | |||
| @@ -39,9 +43,10 @@ fn create_dataflow(name: String, path: Option<PathBuf>) -> Result<(), eyre::ErrR | |||
| fs::write(&dataflow_yml_path, dataflow_yml) | |||
| .with_context(|| format!("failed to write `{}`", dataflow_yml_path.display()))?; | |||
| create_operator("op_1".into(), Some(root.join("op_1")))?; | |||
| create_operator("op_2".into(), Some(root.join("op_2")))?; | |||
| create_custom_node("node_1".into(), Some(root.join("node_1")))?; | |||
| create_custom_node("talker_1".into(), Some(root.join("talker_1")), TALKER)?; | |||
| create_custom_node("talker_2".into(), Some(root.join("talker_2")), TALKER)?; | |||
| create_custom_node("listener_1".into(), Some(root.join("listener_1")), LISTENER)?; | |||
| create_cmakefile(root.to_path_buf())?; | |||
| println!( | |||
| "Created new C++ dataflow at `{name}` at {}", | |||
| @@ -51,6 +56,19 @@ fn create_dataflow(name: String, path: Option<PathBuf>) -> Result<(), eyre::ErrR | |||
| Ok(()) | |||
| } | |||
| fn create_cmakefile(root: PathBuf) -> Result<(), eyre::ErrReport> { | |||
| const CMAKEFILE: &str = include_str!("cmake-template.txt"); | |||
| let cmake_path = root.join("CMakeLists.txt"); | |||
| fs::write(&cmake_path, CMAKEFILE) | |||
| .with_context(|| format!("failed to write `{}`", cmake_path.display()))?; | |||
| println!("Created new CMakeLists.txt at {}", cmake_path.display()); | |||
| Ok(()) | |||
| } | |||
| #[deprecated(since = "0.3.4")] | |||
| #[allow(unused)] | |||
| fn create_operator(name: String, path: Option<PathBuf>) -> Result<(), eyre::ErrReport> { | |||
| const OPERATOR: &str = include_str!("operator-template.cc"); | |||
| const HEADER: &str = include_str!("operator-template.h"); | |||
| @@ -84,9 +102,7 @@ fn create_operator(name: String, path: Option<PathBuf>) -> Result<(), eyre::ErrR | |||
| Ok(()) | |||
| } | |||
| fn create_custom_node(name: String, path: Option<PathBuf>) -> Result<(), eyre::ErrReport> { | |||
| const NODE: &str = include_str!("node-template.cc"); | |||
| fn create_custom_node(name: String, path: Option<PathBuf>, template_scripts: &str) -> Result<(), eyre::ErrReport> { | |||
| if name.contains('/') { | |||
| bail!("node name must not contain `/` separators"); | |||
| } | |||
| @@ -100,7 +116,7 @@ fn create_custom_node(name: String, path: Option<PathBuf>) -> Result<(), eyre::E | |||
| .with_context(|| format!("failed to create directory `{}`", root.display()))?; | |||
| let node_path = root.join("node.cc"); | |||
| fs::write(&node_path, NODE) | |||
| fs::write(&node_path, template_scripts) | |||
| .with_context(|| format!("failed to write `{}`", node_path.display()))?; | |||
| // TODO: Makefile? | |||
| @@ -0,0 +1,39 @@ | |||
| #include "dora-node-api.h" // adjust this path if necessary | |||
| #include <iostream> | |||
| #include <vector> | |||
| int main() | |||
| { | |||
| auto dora_node = init_dora_node(); | |||
| for (int i = 0; i < 20; i++) | |||
| { | |||
| auto event = dora_node.events->next(); | |||
| auto ty = event_type(event); | |||
| if (ty == DoraEventType::AllInputsClosed) | |||
| { | |||
| break; | |||
| } | |||
| else if (ty == DoraEventType::Input) | |||
| { | |||
| std::string message{"Hello World!"}; | |||
| rust::Slice<const uint8_t> message_slice{reinterpret_cast<const uint8_t*>(message.c_str()), message.size()}; | |||
| auto result = send_output(dora_node.send_output, "speech", message_slice); | |||
| auto error = std::string(result.error); | |||
| if (!error.empty()) | |||
| { | |||
| std::cerr << "Error: " << error << std::endl; | |||
| return -1; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| std::cerr << "Unknown event type " << static_cast<int>(ty) << std::endl; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||