Program Listing for File iio_node.cpp

Return to documentation for file (src/iio_node.cpp)

// Copyright 2025 Analog Devices, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "adi_iio/iio_node.hpp"
#include "adi_iio/iio_path.hpp"
#include "adi_iio/iio_attr_topic.hpp"
#include "adi_iio/iio_buffer.hpp"
#include <memory>

using namespace std::chrono_literals;

#define MAX_ATTR_SIZE 4095

IIONode::IIONode()
: Node("adi_iio_node")
{
  m_initialized = false;
  this->declare_parameter<std::string>("uri", "local:");
  this->declare_parameter<int32_t>("timeout", 0);
  m_uri = this->get_parameter("uri").as_string();
  m_timeout = this->get_parameter("timeout").as_int();

  m_ctx = iio_create_context_from_uri(m_uri.c_str());

  if (m_ctx != nullptr) {
    RCLCPP_INFO(
      rclcpp::get_logger("adi_iio_node"),
      "creating context %p from uri %s",
      (void *)m_ctx, m_uri.c_str());
    m_initialized = true;
  } else {
    RCLCPP_ERROR(
      rclcpp::get_logger("adi_iio_node"),
      "cannot create context from uri %s", m_uri.c_str());
  }

  RCLCPP_INFO(
    rclcpp::get_logger("adi_iio_node"),
    "setting timeout to %d", m_timeout);
  iio_context_set_timeout(m_ctx, m_timeout);
}

void IIONode::initBuffers()
{
  RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Initializing buffers...");

  auto devices_ptr = getDevices(ctx());
  for (iio_device * dev : devices_ptr) {
    const char * dev_name_cstr = iio_device_get_name(dev);

    if (dev_name_cstr == nullptr) {
      RCLCPP_WARN(
        rclcpp::get_logger("rclcpp"),
        "device name is null, skipping buffer initialization");
      continue;
    }

    std::string dev_name = std::string(dev_name_cstr);
    RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Parsing channels of device: %s", dev_name.c_str());

    auto channels_ptr = getChannels(dev);
    for (iio_channel * chn : channels_ptr) {
      if (iio_channel_is_scan_element(chn)) {
        RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Inserting %s into bufferMap", dev_name.c_str());
        m_bufferMap.insert(
          {dev_name, std::make_shared<IIOBuffer>(
              std::dynamic_pointer_cast<IIONode>(shared_from_this()), dev_name)});
        break;
      }
    }
  }
}

IIONode::~IIONode()
{
}

bool IIONode::initialized()
{
  return m_initialized;
}

bool IIONode::rwAttrPath(std::string path, std::string & result, bool write, std::string value)
{
  bool ret = false;
  IIOPath iio_path(path);

  iio_device * dev = nullptr;
  iio_channel * ch = nullptr;

  char * val;
  char attr_val[MAX_ATTR_SIZE];
  int ret1;

  if (iio_path.isValid(CONTEXT_ATTR)) {
    val =
      const_cast<char *>(iio_context_get_attr_value(
        m_ctx,
        iio_path.getContextAttrSegment().c_str()));
    if (val) {
      ret = true;
      result = val;
      RCLCPP_DEBUG(
        rclcpp::get_logger("adi_iio_node"),
        "read context attribute \"%s\" with value \"%s\"",
        iio_path.getContextAttrSegment().c_str(), val);
    } else {
      ret = false;
      result = "";
      RCLCPP_WARN(
        rclcpp::get_logger("adi_iio_node"),
        "could not find context attribute \"%s\"", iio_path.getContextAttrSegment().c_str());
    }

    if (write) {
      ret = false;
      RCLCPP_WARN(rclcpp::get_logger("adi_iio_node"), "context attributes cannot be written");
    }
  } else if (iio_path.isValid(DEVICE_ATTR)) {
    dev = iio_context_find_device(m_ctx, iio_path.getDeviceSegment().c_str());
    if (!dev) {
      RCLCPP_WARN(
        rclcpp::get_logger("adi_iio_node"), "could not find device \"%s\"",
        iio_path.getDeviceSegment().c_str());
      return ret;
    }

    if (write) {
      ret1 = iio_device_attr_write(dev, iio_path.getDeviceAttrSegment().c_str(), value.c_str());
      if (ret1 <= 0) {
        ret1 = iio_device_debug_attr_write(
          dev, iio_path.getDeviceAttrSegment().c_str(), value.c_str());
      }
      if (ret1 > 0) {
        RCLCPP_DEBUG(
          rclcpp::get_logger("adi_iio_node"),
          "wrote device attribute \"%s\" from device \"%s\" with value \"%s\"",
          iio_path.getDeviceAttrSegment().c_str(), iio_path.getDeviceSegment().c_str(),
          value.c_str());
      } else {
        result = strerror(-ret1);
        ret = false;
        RCLCPP_WARN(
          rclcpp::get_logger("adi_iio_node"),
          "could not write attribute \"%s\" in device \"%s\" with value \"%s\" - errno %d - %s",
          iio_path.getDeviceAttrSegment().c_str(), iio_path.getDeviceSegment().c_str(),
          value.c_str(), ret1, result.c_str());
        return ret;
      }
    }

    ret1 = iio_device_attr_read(
      dev, iio_path.getDeviceAttrSegment().c_str(), attr_val, MAX_ATTR_SIZE);
    if (ret1 <= 0) {
      ret1 = iio_device_debug_attr_read(
        dev, iio_path.getDeviceAttrSegment().c_str(), attr_val, MAX_ATTR_SIZE
      );
    }

    if (ret1 > 0) {
      result = attr_val;
      ret = true;
      RCLCPP_DEBUG(
        rclcpp::get_logger("adi_iio_node"),
        "read device attribute \"%s\" from device \"%s\" with value \"%s\"",
        iio_path.getDeviceAttrSegment().c_str(), iio_path.getDeviceSegment().c_str(), attr_val);
    } else {
      result = strerror(-ret1);
      ret = false;
      RCLCPP_WARN(
        rclcpp::get_logger("adi_iio_node"),
        "could not read attribute \"%s\" in device \"%s\" - errno %d - %s",
        iio_path.getDeviceAttrSegment().c_str(),
        iio_path.getDeviceSegment().c_str(), ret1, result.c_str());
    }
  } else if (iio_path.isValid(CHANNEL_ATTR)) {
    dev = iio_context_find_device(m_ctx, iio_path.getDeviceSegment().c_str());
    if (!dev) {
      RCLCPP_WARN(
        rclcpp::get_logger("adi_iio_node"), "could not find device \"%s\"",
        iio_path.getDeviceSegment().c_str());
      return ret;
    }

    if (iio_path.hasExtendedChannelFormat()) {
      auto [is_output, chn_name] = iio_path.getExtendedChannelSegment();
      ch = iio_device_find_channel(dev, chn_name.c_str(), is_output);
    } else {
      ch = iio_device_find_channel(dev, iio_path.getChannelSegment().c_str(), false);
      if (!ch) {
        ch = iio_device_find_channel(dev, iio_path.getChannelSegment().c_str(), true);
      }
    }

    if (!ch) {
      RCLCPP_WARN(
        rclcpp::get_logger("adi_iio_node"), "could not find channel \"%s\"",
        iio_path.getChannelSegment().c_str());
      return ret;
    }

    if (write) {
      ret1 = iio_channel_attr_write(ch, iio_path.getChannelAttrSegment().c_str(), value.c_str());
      if (ret1 > 0) {
        RCLCPP_DEBUG(
          rclcpp::get_logger("adi_iio_node"),
          "wrote channel attribute \"%s\" from channel \"%s\" device \"%s\" with value \"%s\"",
          iio_path.getChannelAttrSegment().c_str(),
          iio_path.getChannelSegment().c_str(), iio_path.getDeviceSegment().c_str(), value.c_str());
      } else {
        result = strerror(-ret1);
        ret = false;
        RCLCPP_WARN(
          rclcpp::get_logger(
            "adi_iio_node"),
          "could not write attribute \"%s\" from channel \"%s\" device \"%s\" "
          "with value \"%s\"- errno %d - %s",
          iio_path.getChannelAttrSegment().c_str(),
          iio_path.getChannelSegment().c_str(),
          iio_path.getDeviceSegment().c_str(),
          value.c_str(),
          ret1,
          result.c_str());
        return ret;
      }
    }

    ret1 = iio_channel_attr_read(
      ch, iio_path.getChannelAttrSegment().c_str(), attr_val, MAX_ATTR_SIZE);
    if (ret1 > 0) {
      result = attr_val;
      ret = true;
      RCLCPP_DEBUG(
        rclcpp::get_logger("adi_iio_node"),
        "read channel attribute \"%s\" from channel \"%s\" device \"%s\" with value \"%s\"",
        iio_path.getChannelAttrSegment().c_str(), iio_path.getChannelSegment().c_str(),
        iio_path.getDeviceSegment().c_str(), attr_val);
    } else {
      result = strerror(-ret1);
      ret = false;
      RCLCPP_WARN(
        rclcpp::get_logger("adi_iio_node"),
        "could not read attribute \"%s\" from channel \"%s\" device \"%s\" - errno %d - %s",
        iio_path.getChannelAttrSegment().c_str(), iio_path.getChannelSegment().c_str(),
        iio_path.getDeviceSegment().c_str(), ret1, result.c_str());
    }
  } else {
    result = "Service requires a valid attr_path";
    ret = false;
  }

  return ret;
}

void IIONode::attrReadSrv(
  const std::shared_ptr<adi_iio::srv::AttrReadString::Request> request,   // CHANGE
  std::shared_ptr<adi_iio::srv::AttrReadString::Response> response)
{
  RCLCPP_INFO(
    rclcpp::get_logger("adi_iio_node"), "Service request /AttrReadString : %s",
    request->attr_path.c_str());
  std::string result;
  response->success = rwAttrPath(request->attr_path, result);
  response->message = result;
}

void IIONode::attrWriteSrv(
  const std::shared_ptr<adi_iio::srv::AttrWriteString::Request> request,   // CHANGE
  std::shared_ptr<adi_iio::srv::AttrWriteString::Response> response)
{
  RCLCPP_INFO(
    rclcpp::get_logger("adi_iio_node"), "Service request /AttrWriteString : %s value %s",
    request->attr_path.c_str(), request->value.c_str());
  std::string result;
  response->success = rwAttrPath(request->attr_path, result, true, request->value);
  response->message = result;
}

void IIONode::attrEnableTopicSrv(
  const std::shared_ptr<adi_iio::srv::AttrEnableTopic::Request> request,
  std::shared_ptr<adi_iio::srv::AttrEnableTopic::Response> response)
{
  RCLCPP_INFO(
    rclcpp::get_logger("adi_iio_node"),
    "Service request /AttrEnableTopic %s with type %d with loop_rate %f Hz",
    request->attr_path.c_str(), request->type, request->loop_rate);

  std::string message;
  response->success = rwAttrPath(request->attr_path, message);
  response->message = message;

  std::string local_topic_name = IIOPath::toTopicName(request->topic_name);
  if (local_topic_name == "") {
    local_topic_name = IIOPath::toTopicName(request->attr_path);
  }

  if (response->success) {
    if (m_attrTopicMap.find(local_topic_name) != m_attrTopicMap.end()) {
      m_attrTopicMap.erase(local_topic_name);
    }

    m_attrTopicMap.insert(
      {local_topic_name,
        std::make_shared<IIOAttrTopic>(
          std::dynamic_pointer_cast<IIONode>(shared_from_this()), local_topic_name,
          request->attr_path, static_cast<IIOAttrTopic::topicType_t>(request->type),
          request->loop_rate)});

    setSuccessResponse(response, "Success");
  } else {
    setErrorResponse(response, message);
  }
}

void IIONode::attrDisableTopicSrv(
  const std::shared_ptr<adi_iio::srv::AttrDisableTopic::Request> request,
  std::shared_ptr<adi_iio::srv::AttrDisableTopic::Response> response)
{
  RCLCPP_INFO(
    rclcpp::get_logger("adi_iio_node"), "Service request /AttrDisableTopic %s ",
    request->topic_name.c_str());

  std::string message;

  std::string local_topic_name = request->topic_name;
  local_topic_name = IIOPath::toTopicName(request->topic_name);  // for compatibility with enable

  if (m_attrTopicMap.find(local_topic_name) != m_attrTopicMap.end()) {
    m_attrTopicMap.erase(local_topic_name);
    message = "Success";
    setSuccessResponse(response, message);
  } else {
    message = "Topic not found";
    setErrorResponse(response, message);
  }
}

void IIONode::buffRefillSrv(
  const std::shared_ptr<adi_iio::srv::BufferRefill::Request> request,
  std::shared_ptr<adi_iio::srv::BufferRefill::Response> response)
{
  RCLCPP_INFO(
    rclcpp::get_logger("adi_iio_node"), "Service request /BufferRill %s",
    request->device_path.c_str());

  std::string message;
  bool success = true;

  IIOPath path(request->device_path);
  if (!path.isValid(IIOPathType::DEVICE)) {
    message = request->device_path + " is not a valid device path";
    setErrorResponse(response, message);
    return;
  }

  if (m_bufferMap.find(path.getDeviceSegment()) == m_bufferMap.end()) {
    message = "Buffer not found";
    setErrorResponse(response, message);
    return;
  }

  if (!m_bufferMap[path.getDeviceSegment()]->topic_enabled()) {
    success = m_bufferMap[path.getDeviceSegment()]->refill(message);
  }

  if (!success) {
    setErrorResponse(response, message);
    return;
  }

  setSuccessResponse(response, message);
  response->buffer = m_bufferMap[path.getDeviceSegment()]->data();
}

void IIONode::buffReadSrv(
  const std::shared_ptr<adi_iio::srv::BufferRead::Request> request,
  std::shared_ptr<adi_iio::srv::BufferRead::Response> response)
{
  std::string channels;
  for (auto & channel : request->channels) {
    channels += channel + " ";
  }

  RCLCPP_INFO(
    rclcpp::get_logger("adi_iio_node"), "Service request /BufferRead %s - %s - %d samples",
    request->device_path.c_str(), channels.c_str(), request->samples_count);

  std::string message;

  IIOPath path(request->device_path);
  if (!path.isValid(IIOPathType::DEVICE)) {
    message = request->device_path + " is not a valid device path";
    setErrorResponse(response, message);
    return;
  }

  std::shared_ptr<IIOBuffer> buffer;
  if (m_bufferMap.find(path.getDeviceSegment()) == m_bufferMap.end()) {
    message = "Device or buffer not found.";
    setErrorResponse(response, message);
    return;
  }

  buffer = m_bufferMap[path.getDeviceSegment()];
  buffer->destroyIIOBuffer();
  buffer->set_samples_count(request->samples_count);
  buffer->set_channels(request->channels);

  bool success = buffer->createIIOBuffer(message);
  if (!success) {
    setErrorResponse(response, message);
    return;
  }

  success = buffer->refill(message);
  if (!success) {
    setErrorResponse(response, message);
    return;
  }

  setSuccessResponse(response, message);
  response->buffer = buffer->data();
}

void IIONode::buffWriteSrv(
  const std::shared_ptr<adi_iio::srv::BufferWrite::Request> request,
  std::shared_ptr<adi_iio::srv::BufferWrite::Response> response)
{
  std::string channels;
  for (auto & channel : request->channels) {
    channels += channel + " ";
  }

  RCLCPP_INFO(
    rclcpp::get_logger("adi_iio_node"), "Service request /BufferWrite %s - %s - %d samples",
    request->device_path.c_str(), channels.c_str(), request->buffer.layout.dim[0].size);

  std::string message;

  IIOPath path(request->device_path);
  if (!path.isValid(IIOPathType::DEVICE)) {
    message = request->device_path + " is not a valid device path";
    setErrorResponse(response, message);
    return;
  }

  std::shared_ptr<IIOBuffer> buffer;
  if (m_bufferMap.find(path.getDeviceSegment()) == m_bufferMap.end()) {
    message = "Device or buffer not found.";
    setErrorResponse(response, message);
    return;
  }

  buffer = m_bufferMap[path.getDeviceSegment()];
  buffer->destroyIIOBuffer();
  buffer->set_samples_count(request->buffer.layout.dim[0].size);
  buffer->set_channels(request->channels);

  bool success = buffer->createIIOBuffer(message, true, request->cyclic);
  if (!success) {
    setErrorResponse(response, message);
    return;
  }

  success = buffer->push(message, request->buffer);
  if (!success) {
    setErrorResponse(response, message);
    return;
  }
  setSuccessResponse(response, message);
}

void IIONode::buffCreateSrv(
  const std::shared_ptr<adi_iio::srv::BufferCreate::Request> request,
  std::shared_ptr<adi_iio::srv::BufferCreate::Response> response)
{
  std::string channels;
  for (auto & channel : request->channels) {
    channels += channel + " ";
  }

  RCLCPP_INFO(
    rclcpp::get_logger("adi_iio_node"),
    "Service request /BufferCreate %s - %s - %d samples",
    request->device_path.c_str(), channels.c_str(), request->samples_count);

  std::string message;

  IIOPath path(request->device_path);
  if (!path.isValid(IIOPathType::DEVICE)) {
    message = request->device_path + " is not a valid device path";
    setErrorResponse(response, message);
    return;
  }

  std::shared_ptr<IIOBuffer> buffer;
  if (m_bufferMap.find(path.getDeviceSegment()) == m_bufferMap.end()) {
    message = "Buffer not found";
    setWarningResponse(response, message);
    return;
  }

  buffer = m_bufferMap[path.getDeviceSegment()];
  buffer->destroyIIOBuffer();
  buffer->set_samples_count(request->samples_count);
  buffer->set_channels(request->channels);

  bool success = buffer->createIIOBuffer(message, false, false);
  if (!success) {
    setErrorResponse(response, message);
    return;
  }

  setSuccessResponse(response, message);
  response->layout = {buffer->data().layout};
}

void IIONode::buffDestroySrv(
  const std::shared_ptr<adi_iio::srv::BufferDestroy::Request> request,
  std::shared_ptr<adi_iio::srv::BufferDestroy::Response> response)
{
  RCLCPP_INFO(
    rclcpp::get_logger("adi_iio_node"),
    "Service request /BufferDestroy %s", request->device_path.c_str());

  std::string msg;

  IIOPath path(request->device_path);
  if (!path.isValid(IIOPathType::DEVICE)) {
    msg = request->device_path + " is not a valid device path";
    setErrorResponse(response, msg);
    return;
  }

  if (m_bufferMap.find(path.getDeviceSegment()) != m_bufferMap.end()) {
    if (m_bufferMap[path.getDeviceSegment()]->buffer()) {
      m_bufferMap[path.getDeviceSegment()]->destroyIIOBuffer();
      msg = "Success";
      setSuccessResponse(response, msg);
      return;
    }
  }

  msg = "Buffer not found";
  setWarningResponse(response, msg);
}

void IIONode::buffEnableTopicSrv(
  const std::shared_ptr<adi_iio::srv::BufferEnableTopic::Request> request,
  std::shared_ptr<adi_iio::srv::BufferEnableTopic::Response> response)
{
  RCLCPP_INFO(
    rclcpp::get_logger("adi_iio_node"),
    "Service request /BufferEnableTopic %s", request->device_path.c_str());

  std::string msg;

  IIOPath path(request->device_path);
  if (!path.isValid(IIOPathType::DEVICE)) {
    msg = request->device_path + " is not a valid device path";
    setErrorResponse(response, msg);
    return;
  }

  if (m_bufferMap.find(path.getDeviceSegment()) != m_bufferMap.end()) {
    m_bufferMap[path.getDeviceSegment()]->enableTopic(request->topic_name, request->loop_rate);
    msg = "Success";
    setSuccessResponse(response, msg);
  } else {
    msg = "Buffer not found";
    setWarningResponse(response, msg);
  }
}

void IIONode::buffDisableTopicSrv(
  const std::shared_ptr<adi_iio::srv::BufferDisableTopic::Request> request,
  std::shared_ptr<adi_iio::srv::BufferDisableTopic::Response> response)
{
  RCLCPP_INFO(
    rclcpp::get_logger("adi_iio_node"),
    "Service request /BufferDisableTopic %s", request->device_path.c_str());

  std::string msg;

  IIOPath path(request->device_path);
  if (!path.isValid(IIOPathType::DEVICE)) {
    msg = request->device_path + " is not a valid device path";
    setErrorResponse(response, msg);
    return;
  }

  if (m_bufferMap.find(path.getDeviceSegment()) != m_bufferMap.end()) {
    m_bufferMap[path.getDeviceSegment()]->disableTopic();
    msg = "Success";
    setSuccessResponse(response, msg);
  } else {
    msg = "Buffer not found";
    setWarningResponse(response, msg);
  }
}

void IIONode::listDevicesSrv(
  const std::shared_ptr<adi_iio::srv::ListDevices::Request> request,
  std::shared_ptr<adi_iio::srv::ListDevices::Response> response)
{
  (void)request;  // unused
  RCLCPP_INFO(rclcpp::get_logger("adi_iio_node"), "Service request /ListDevices");
  std::string msg;

  IIOPath path("");
  std::vector<std::string> data;

  auto devices_ptr = getDevices(ctx());
  for (iio_device * dev : devices_ptr) {
    const char * dev_name_cstr = iio_device_get_name(dev);
    if (dev_name_cstr == nullptr) {
      RCLCPP_WARN(
        rclcpp::get_logger("adi_iio_node"),
        "device name is null, skipping device");
      continue;
    }
    auto dev_name = std::string(dev_name_cstr);
    auto dev_path = path.append(dev_name);
    data.push_back(dev_path);
  }

  msg = "Found " + std::to_string(data.size()) + " devices";
  setSuccessResponse(response, msg);
  response->data = {data};
}

void IIONode::listChannelsSrv(
  const std::shared_ptr<adi_iio::srv::ListChannels::Request> request,
  std::shared_ptr<adi_iio::srv::ListChannels::Response> response)
{
  RCLCPP_INFO(rclcpp::get_logger("adi_iio_node"), "Service request /ListChannels");
  std::string msg;

  IIOPath path(request->iio_path);
  if (!path.isValid(IIOPathType::DEVICE)) {
    msg = "Invalid path: " + request->iio_path;
    setWarningResponse(response, msg);
    return;
  }

  auto dev_name = path.getDeviceSegment();
  iio_device * dev = iio_context_find_device(ctx(), dev_name.c_str());
  if (dev == nullptr) {
    msg = "Could not find device: " + dev_name;
    setWarningResponse(response, msg);
    return;
  }

  std::vector<std::string> data;

  auto channels_ptr = getChannels(dev);
  for (iio_channel * chn : channels_ptr) {
    auto chn_name = std::string(iio_channel_get_id(chn));
    auto is_output = iio_channel_is_output(chn);
    auto chn_path = IIOPath::toExtendedChannelSegment(is_output, chn_name);
    data.push_back(path.append(chn_path));
  }

  msg = "Found " + std::to_string(data.size()) + " channels in device: " + dev_name;
  setSuccessResponse(response, msg);
  response->data = {data};
}

void IIONode::listAttributesSrv(
  const std::shared_ptr<adi_iio::srv::ListAttributes::Request> request,
  std::shared_ptr<adi_iio::srv::ListAttributes::Response> response)
{
  RCLCPP_INFO(rclcpp::get_logger("adi_iio_node"), "Service request /ListAttributes");
  std::string msg;
  std::vector<std::string> data;

  IIOPath path(request->iio_path);
  if (path.isValid(IIOPathType::CONTEXT)) {
    int ret{};
    std::string err_str;

    unsigned int nb_ctx_attrs = iio_context_get_attrs_count(ctx());
    for (unsigned int i = 0; i < nb_ctx_attrs; i++) {
      const char * key, * value;

      ret = iio_context_get_attr(ctx(), i, &key, &value);
      if (ret == 0) {
        data.push_back(path.append(std::string(key)));
      } else {
        err_str = strerror(-ret);
        RCLCPP_WARN(
          rclcpp::get_logger(
            "adi_iio_node"),
          "Unable to read IIO context attribute: %s", err_str.c_str());
      }
    }
    msg = "Found " + std::to_string(data.size()) + " attributes";
  } else if (path.isValid(IIOPathType::DEVICE)) {
    auto dev_name = path.getDeviceSegment();
    iio_device * dev = iio_context_find_device(ctx(), dev_name.c_str());
    if (dev == nullptr) {
      msg = "Could not find device: " + dev_name;
      setWarningResponse(response, msg);
      return;
    }

    unsigned int nb_attrs = iio_device_get_attrs_count(dev);
    for (unsigned int i = 0; i < nb_attrs; i++) {
      std::string attr_key = iio_device_get_attr(dev, i);
      data.push_back(path.append(attr_key));
    }
    msg = "Found " + std::to_string(data.size()) + " attributes in device: " + dev_name;
  } else if (path.isValid(IIOPathType::CHANNEL)) {
    auto dev_name = path.getDeviceSegment();
    iio_device * dev = iio_context_find_device(ctx(), dev_name.c_str());
    if (dev == nullptr) {
      msg = "Could not find device: " + dev_name;
      setWarningResponse(response, msg);
      return;
    }

    iio_channel * chn;
    if (path.hasExtendedChannelFormat()) {
      auto [is_output, chn_name] = path.getExtendedChannelSegment();
      chn = iio_device_find_channel(dev, chn_name.c_str(), is_output);
    } else {
      chn = iio_device_find_channel(dev, path.getChannelSegment().c_str(), false);
      if (chn == nullptr) {
        chn = iio_device_find_channel(dev, path.getChannelSegment().c_str(), true);
      }
    }
    if (chn == nullptr) {
      msg = "Could not find channel: " + path.getChannelSegment();
      setWarningResponse(response, msg);
      return;
    }
    unsigned int nb_attrs = iio_channel_get_attrs_count(chn);
    for (unsigned int i = 0; i < nb_attrs; i++) {
      std::string attr_key = iio_channel_get_attr(chn, i);
      data.push_back(path.append(attr_key));
    }
    msg = "Found " + std::to_string(data.size()) + " attributes in channel: " +
      path.getChannelSegment();
  } else {
    msg = "Invalid path: " + request->iio_path;
    setErrorResponse(response, msg);
    return;
  }

  setSuccessResponse(response, msg);
  response->data = {data};
}

void IIONode::scanContextSrv(
  const std::shared_ptr<adi_iio::srv::ScanContext::Request> request,
  std::shared_ptr<adi_iio::srv::ScanContext::Response> response)
{
  (void)request;  // unused
  RCLCPP_INFO(rclcpp::get_logger("adi_iio_node"), "Service request /ScanContext");
  std::string msg{"Found: "};
  std::vector<std::string> devices;
  std::vector<std::string> channels;
  std::vector<std::string> context_attrs;
  std::vector<std::string> device_attrs;
  std::vector<std::string> channel_attrs;

  // Handle context attributes
  IIOPath ctx_path("");
  int ret{};
  std::string err_str;
  unsigned int nb_ctx_attrs = iio_context_get_attrs_count(ctx());
  for (unsigned int i = 0; i < nb_ctx_attrs; i++) {
    const char * key, * value;
    ret = iio_context_get_attr(ctx(), i, &key, &value);
    if (ret == 0) {
      context_attrs.push_back(ctx_path.append(std::string(key)));
    } else {
      err_str = strerror(-ret);
      RCLCPP_WARN(
        rclcpp::get_logger(
          "adi_iio_node"),
        "Unable to read IIO context attribute: %s", err_str.c_str());
    }
  }

  // Handle devices
  auto devices_ptr = getDevices(ctx());
  for (iio_device * dev : devices_ptr) {
    const char * dev_name_cstr = iio_device_get_name(dev);
    if (dev_name_cstr == nullptr) {
      RCLCPP_WARN(
        rclcpp::get_logger("adi_iio_node"),
        "device name is null, skipping device");
      continue;
    }
    auto dev_name = std::string(dev_name_cstr);
    auto dev_path = IIOPath(ctx_path.append(dev_name));

    devices.push_back(dev_path.basePath());

    // Handle device attributes
    unsigned int nb_attrs = iio_device_get_attrs_count(dev);
    for (unsigned int i = 0; i < nb_attrs; i++) {
      std::string attr_key = iio_device_get_attr(dev, i);
      device_attrs.push_back(dev_path.append(attr_key));
    }

    // Handle device channels
    auto channels_ptr = getChannels(dev);
    for (iio_channel * chn : channels_ptr) {
      auto chn_name = std::string(iio_channel_get_id(chn));
      auto is_output = iio_channel_is_output(chn);
      auto chn_segment = IIOPath::toExtendedChannelSegment(is_output, chn_name);
      auto chn_path = IIOPath(dev_path.append(chn_segment));

      channels.push_back(chn_path.basePath());

      // Handle channel attributes
      unsigned int nb_attrs = iio_channel_get_attrs_count(chn);
      for (unsigned int i = 0; i < nb_attrs; i++) {
        std::string attr_key = iio_channel_get_attr(chn, i);
        channel_attrs.push_back(chn_path.append(attr_key));
      }
    }
  }
  msg += "Context attributes: " + std::to_string(context_attrs.size()) + "; ";
  msg += "Devices: " + std::to_string(devices.size()) + "; ";
  msg += "Channels: " + std::to_string(channels.size()) + "; ";
  msg += "Device attributes: " + std::to_string(device_attrs.size()) + "; ";
  msg += "Channel attributes: " + std::to_string(channel_attrs.size()) + "; ";

  setSuccessResponse(response, msg);
  response->devices = {devices};
  response->channels = {channels};
  response->context_attrs = {context_attrs};
  response->device_attrs = {device_attrs};
  response->channel_attrs = {channel_attrs};
}

std::vector<iio_device *> IIONode::getDevices(iio_context * ctx)
{
  std::vector<iio_device *> devices;
  unsigned int devices_count = iio_context_get_devices_count(ctx);
  for (unsigned int i = 0; i < devices_count; i++) {
    iio_device * dev = iio_context_get_device(ctx, i);
    devices.push_back(dev);
  }
  return devices;
}

std::vector<iio_channel *> IIONode::getChannels(iio_device * device)
{
  std::vector<iio_channel *> channels;
  unsigned int channels_count = iio_device_get_channels_count(device);
  for (unsigned int i = 0; i < channels_count; i++) {
    iio_channel * ch = iio_device_get_channel(device, i);
    channels.push_back(ch);
  }
  return channels;
}

std::string IIONode::uri()
{
  return m_uri;
}

iio_context * IIONode::ctx()
{
  return m_ctx;
}