diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 81bab136c4b98ad55c84b31e724968fea8597e26..f1d20736146d494afacbceb05a64ef8ee2ccd069 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,6 +1,6 @@ FROM ros:jazzy-ros-base -# Install some dependencies packages +# Install dependencies for COWL and ROS2 RUN apt update -q \ && apt upgrade -q -y \ && apt install -y --no-install-recommends \ @@ -8,11 +8,31 @@ RUN apt update -q \ python3-pip \ build-essential \ libboost-all-dev \ - xauth \ + libxml2-dev \ + libcurl4-openssl-dev \ + libssl-dev \ + cmake \ + git \ + flex \ + bison \ + doxygen \ && apt clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -# Create workspace directory +# Clone COWL repository with submodules +RUN git clone --recursive https://github.com/sisinflab-swot/cowl.git /root/cowl + +# Build COWL +WORKDIR /root/cowl +RUN cmake -B cmake-build -DCMAKE_BUILD_TYPE=Release \ + && cmake --build cmake-build --config Release \ + && cmake --install cmake-build --prefix /root/cowl/install + +# Set environment variables to point to the COWL headers and libraries +ENV COWL_INCLUDE_DIR=/root/cowl/install/include +ENV COWL_LIB_DIR=/root/cowl/install/lib + +# Create workspace directory for ROS 2 RUN mkdir -p /root/ros2_ws/src WORKDIR /root/ros2_ws @@ -23,4 +43,4 @@ RUN echo 'source /opt/ros/$ROS_DISTRO/setup.bash' >> /root/.bashrc \ # Setup entrypoint COPY ./ros_entrypoint.sh / ENTRYPOINT ["/ros_entrypoint.sh"] -CMD ["bash"] \ No newline at end of file +CMD ["bash"] diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b0cf01ca48c700124e9b27534e56f752a5db5d2f..047395f5922d3a5335276b9eb9eb4802fa856d4c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -33,12 +33,9 @@ } }, "workspaceMount": "src=${localWorkspaceFolder},dst=/root/ros2_ws,type=bind", - //"workspaceMount": "src=/home/leolol/Code/dyknow-ros-2,dst=/root/ros2_ws,type=volume", "workspaceFolder": "/root/ros2_ws", - //"mounts": ["src=/home/leolol/Code/dyknow-ros-2,dst=/root/ros2_ws,type=bind"], "mounts": [], "runArgs": [ "--net=host" ] - } - \ No newline at end of file +} diff --git a/src/dyknow_cowl_watchdog/CMakeLists.txt b/src/dyknow_cowl_watchdog/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a5d16c7094c3b16115dd4a983057726bb1c24694 --- /dev/null +++ b/src/dyknow_cowl_watchdog/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 3.8) +project(dyknow_cowl_watchdog) + +find_package(ament_cmake REQUIRED) +find_package(rclcpp REQUIRED) +find_package(std_msgs REQUIRED) +find_package(geometry_msgs REQUIRED) +find_package(message_filters REQUIRED) +find_package(rosidl_default_generators REQUIRED) + +set(msg_files + "msg/Lidar.msg" +) + +rosidl_generate_interfaces(${PROJECT_NAME} + ${msg_files} + DEPENDENCIES std_msgs +) + +ament_export_dependencies(rosidl_default_runtime) + +include_directories( + include + ${rclcpp_INCLUDE_DIRS} + ${std_msgs_INCLUDE_DIRS} + ${geometry_msgs_INCLUDE_DIRS} + ${message_filters_INCLUDE_DIRS} + ${rosidl_default_generators_INCLUDE_DIRS} + ${CMAKE_BINARY_DIR}/rosidl_generator_cpp # Include path for generated message headers + /root/cowl/include + /root/cowl/lib/ulib/include +) + +# Create the library +add_library(dyknow_cowl_watchdog_lib SHARED + src/cowl_watcher.cpp +) + +ament_target_dependencies(dyknow_cowl_watchdog_lib + rclcpp + std_msgs + geometry_msgs + message_filters +) + +# Ensure that the generated message headers are available to this target +add_dependencies(dyknow_cowl_watchdog_lib ${PROJECT_NAME}__rosidl_typesupport_cpp) + +install(TARGETS dyknow_cowl_watchdog_lib + EXPORT export_${PROJECT_NAME} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin +) + +install(DIRECTORY include/ + DESTINATION include/${PROJECT_NAME}/ +) + +# Install the CMake configuration files +install( + FILES cmake/dyknow_cowl_watchdogConfig.cmake + DESTINATION share/${PROJECT_NAME}/cmake +) + +install( + EXPORT export_${PROJECT_NAME} + DESTINATION share/${PROJECT_NAME}/cmake +) + +ament_package() diff --git a/src/dyknow_cowl_watchdog/cmake/dyknow_cowl_watchdogConfig.cmake b/src/dyknow_cowl_watchdog/cmake/dyknow_cowl_watchdogConfig.cmake new file mode 100644 index 0000000000000000000000000000000000000000..98c659f54e13bc5d7876cb813c3b2f85d8694aa8 --- /dev/null +++ b/src/dyknow_cowl_watchdog/cmake/dyknow_cowl_watchdogConfig.cmake @@ -0,0 +1,8 @@ +include(CMakeFindDependencyMacro) +find_dependency(ament_cmake) +find_dependency(rclcpp) +find_dependency(std_msgs) +find_dependency(geometry_msgs) +find_dependency(message_filters) + +include("${CMAKE_CURRENT_LIST_DIR}/dyknow_cowl_watchdogTargets.cmake") \ No newline at end of file diff --git a/src/dyknow_cowl_watchdog/include/dyknow_cowl_watchdog/cowl_watcher.hpp b/src/dyknow_cowl_watchdog/include/dyknow_cowl_watchdog/cowl_watcher.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0600021821fe9d6927d630f199c6f9779131eff1 --- /dev/null +++ b/src/dyknow_cowl_watchdog/include/dyknow_cowl_watchdog/cowl_watcher.hpp @@ -0,0 +1,26 @@ +#ifndef INCLUDE_COWL_WATCHER_HPP_ +#define INCLUDE_COWL_WATCHER_HPP_ + +#include "rclcpp/rclcpp.hpp" +#include "std_msgs/msg/string.hpp" +#include "cowl.h" + +class CowlWatcher : public rclcpp::Node { +public: + CowlWatcher(); + ~CowlWatcher(); + +private: + CowlOntology *ontology_; + std::unordered_map<std::string, rclcpp::SubscriptionBase::SharedPtr> subscriptions_; + rclcpp::TimerBase::SharedPtr discovery_timer_; + rclcpp::CallbackGroup::SharedPtr callback_group_; + std::vector<std::string> active_topics_; + + + void discoverTopics(); + void subscribeToTopic(const std::string &topic_name); + void processMessage(const std::string &topic, const std::string &data); +}; + +#endif /* INCLUDE_COWL_WATCHER_HPP_ */ \ No newline at end of file diff --git a/src/dyknow_cowl_watchdog/msg/Lidar.msg b/src/dyknow_cowl_watchdog/msg/Lidar.msg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/dyknow_cowl_watchdog/ont/my_ontology.ttl b/src/dyknow_cowl_watchdog/ont/my_ontology.ttl new file mode 100644 index 0000000000000000000000000000000000000000..c8f1675bc29e8ea97051c43008d9f77b8e25170e --- /dev/null +++ b/src/dyknow_cowl_watchdog/ont/my_ontology.ttl @@ -0,0 +1,16 @@ +@prefix ex: <http://example.org#> . + +# Define sensor classes +ex:LIDAR a rdfs:Class . +ex:Gyroscope a rdfs:Class . +ex:IR_Sensor a rdfs:Class . + +# Define properties (sensor data) +ex:hasDistance a rdf:Property ; rdfs:domain ex:LIDAR ; rdfs:range xsd:float . +ex:hasAngle a rdf:Property ; rdfs:domain ex:Gyroscope ; rdfs:range xsd:float . +ex:hasTemperature a rdf:Property ; rdfs:domain ex:IR_Sensor ; rdfs:range xsd:float . + +# Instances of sensors +ex:sensor1 a ex:LIDAR ; ex:hasDistance "10.5" . +ex:sensor2 a ex:Gyroscope ; ex:hasAngle "45.0" . +ex:sensor3 a ex:IR_Sensor ; ex:hasTemperature "22.5" . diff --git a/src/dyknow_cowl_watchdog/package.xml b/src/dyknow_cowl_watchdog/package.xml new file mode 100644 index 0000000000000000000000000000000000000000..cf1d17ea438ac2110b58adfe207aa112ca8b1416 --- /dev/null +++ b/src/dyknow_cowl_watchdog/package.xml @@ -0,0 +1,29 @@ +<?xml version="1.0"?> +<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?> +<package format="3"> + <name>dyknow_cowl_watchdog</name> + <version>0.0.0</version> + <description>TODO: Package description</description> + <maintainer email="root@todo.todo">root</maintainer> + <license>TODO: License declaration</license> + + <buildtool_depend>ament_cmake</buildtool_depend> + + <test_depend>ament_lint_auto</test_depend> + <test_depend>ament_lint_common</test_depend> + + <build_depend>rosidl_default_generators</build_depend> + <build_depend>std_msgs</build_depend> + + <depend>builtin_interfaces</depend> + <exec_depend>rosidl_default_runtime</exec_depend> + <exec_depend>std_msgs</exec_depend> + + <buildtool_depend>rosidl_default_generators</buildtool_depend> + <exec_depend>rosidl_default_runtime</exec_depend> + <member_of_group>rosidl_interface_packages</member_of_group> + + <export> + <build_type>ament_cmake</build_type> + </export> +</package> diff --git a/src/dyknow_cowl_watchdog/src/cowl_watcher.cpp b/src/dyknow_cowl_watchdog/src/cowl_watcher.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba36dda3fd9c23bf94d51b693b9875d872e07704 --- /dev/null +++ b/src/dyknow_cowl_watchdog/src/cowl_watcher.cpp @@ -0,0 +1,77 @@ +#include "dyknow_cowl_watchdog/cowl_watcher.hpp" +#include <unordered_map> + +CowlWatcher::CowlWatcher() : Node("cowl_watcher") { + cowl_init(); + ontology_ = cowl_ontology_new(); + + // Use Reentrant callback group for parallel processing + callback_group_ = this->create_callback_group(rclcpp::CallbackGroupType::Reentrant); + + // Timer to check for new topics every 5 seconds + discovery_timer_ = this->create_wall_timer( + std::chrono::seconds(5), + std::bind(&CowlWatcher::discoverTopics, this) + ); + + // Initial topic discovery + discoverTopics(); +} + +CowlWatcher::~CowlWatcher() { + cowl_ontology_free(ontology_); +} + +void CowlWatcher::discoverTopics() { + auto topic_names_and_types = this->get_topic_names_and_types(); + for (const auto &topic : topic_names_and_types) { + const std::string &topic_name = topic.first; + if(std::find(active_topics_.begin(), active_topics_.end(), topic_name) == active_topics_.end()) { + RCLCPP_INFO(this->get_logger(), "New topic detected: %s", topic_name.c_str()); + subscribeToTopic(topic_name); + active_topics_.push_back(topic_name); + } + } +} + +void CowlWatcher::subscribeToTopic(const std::string &topic_name) { + auto sub = this->create_subscription<std_msgs::msg::String>( + topic_name, 10, + [this, topic_name](const std_msgs::msg::String::SharedPtr msg) { + processMessage(topic_name, msg->data); + }, + rclcpp::SubscriptionOptions().callback_group(callback_group_) + ); + subscriptions_[topic_name] = sub; +} + +void CowlWatcher::processMessage(const std::string &topic, const std::string &data) { + // Convert message into RDF triple + std::string subject = "ex:" + topic; + std::string predicate = "ex::hasValue"; + std::string object = '"' + data + '"'; + + // Create Cowl strings for subject, predicate, and object + CowlString *subject_str = cowl_string_new(subject.c_str()); + CowlString *predicate_str = cowl_string_new(predicate.c_str()); + CowlString *object_str = cowl_string_new(object.c_str()); + + CowlAxiom* axiom = cowl_axiom_new(subject_str, predicate_str, object_str); + cowl_ontology_add_axiom(ontology_, axiom); + RCLCPP_INFO(this->get_logger(), "Added RDF: (%s, %s, %s)", subject.c_str(), predicate.c_str(), object.c_str()); + + cowl_release(subject_str); + cowl_release(predicate_str); + cowl_release(object_str); +} + + +int main(int argc, char **argv) { + rclcpp::init(argc, argv); + auto node = std::make_shared<CowlWatcher>(); + rclcpp::executors::MultiThreadedExecutor executor; + executor.add_node(node); + executor.spin(); + rclcpp::shutdown(); + return 0; +} \ No newline at end of file