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_attr_topic.hpp"
#include "adi_iio/iio_buffer.hpp"
#include <memory>

using namespace std::chrono_literals;

#define MAX_ATTR_SIZE 4095
/*
BufferTopic.srv

string buffer_path
int buffer_size
bool enable
---
bool success
string message


parameter
----
"URI"

*/

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

  m_ctx = iio_create_context_from_uri(m_uri.c_str());

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

void IIONode::initBuffers()
{
  for (unsigned int i = 0; i < iio_context_get_devices_count(m_ctx); i++) {
    iio_device * dev = iio_context_get_device(m_ctx, i);
    std::string dev_name = iio_device_get_name(dev);
    for (unsigned int j = 0; j < iio_device_get_channels_count(dev); j++) {
      iio_channel * ch = iio_device_get_channel(dev, j);
      if (!iio_channel_is_output(ch) && iio_channel_is_scan_element(ch)) {
        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;
}

std::string IIONode::convertAttrPathToTopicName(std::string path)
{
  std::replace(path.begin(), path.end(), '-', '_');
  return path;
}

bool IIONode::rwAttrPath(std::string path, std::string & result, bool write, std::string value)
{
  bool ret = false;
  std::string str = path;
  std::stringstream ss(str);
  std::string segment;
  std::vector<std::string> segments;

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

  while (std::getline(ss, segment, '/')) {
    segments.push_back(segment);  // Push each part of the string into the vector
  }

  char * val;
  char attr_val[MAX_ATTR_SIZE];
  int ret1;
  switch (segments.size()) {
    case 1:  // context_attribute
      val = const_cast<char *>(iio_context_get_attr_value(m_ctx, segments[0].c_str()));
      if (val) {
        ret = true;
        result = val;
        RCLCPP_INFO(
          rclcpp::get_logger("rclcpp"), "read context attribute \"%s\" with value \"%s\"",
          segments[0].c_str(), val);
      } else {
        ret = false;
        result = "";
        RCLCPP_WARN(
          rclcpp::get_logger(
            "rclcpp"), "could not find context attribute \"%s\"", segments[0].c_str());
      }

      if (write) {
        ret = false;
        RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "context attributes cannot be written");
      }
      break;

    case 2:  // device_attribute
      dev = iio_context_find_device(m_ctx, segments[0].c_str());
      if (!dev) {
        RCLCPP_WARN(
          rclcpp::get_logger("rclcpp"), "could not find device \"%s\"",
          segments[0].c_str());
        break;
      }
      if (write) {
        ret1 = iio_device_attr_write(dev, segments[1].c_str(), value.c_str());
        if (ret1 > 0) {
          RCLCPP_INFO(
            rclcpp::get_logger("rclcpp"),
            "wrote device attribute \"%s\" from device \"%s\" with value \"%s\"",
            segments[1].c_str(),
            segments[0].c_str(), value.c_str());
        } else {
          result = strerror(-ret1);
          ret = false;
          RCLCPP_WARN(
            rclcpp::get_logger("rclcpp"),
            "could not write attribute \"%s\" in device \"%s\" with value \"%s\" - errno %d - %s",
            segments[1].c_str(), segments[0].c_str(), value.c_str(), ret1, result.c_str());
          break;
        }
      }

      ret1 = iio_device_attr_read(dev, segments[1].c_str(), attr_val, MAX_ATTR_SIZE);
      if (ret1 > 0) {
        result = attr_val;
        ret = true;
        RCLCPP_INFO(
          rclcpp::get_logger(
            "rclcpp"), "read device attribute \"%s\" from device \"%s\" with value \"%s\"",
          segments[1].c_str(), segments[0].c_str(), attr_val);
      } else {
        result = strerror(-ret1);
        ret = false;
        RCLCPP_WARN(
          rclcpp::get_logger(
            "rclcpp"), "could not read attribute \"%s\" in device \"%s\" - errno %d - %s",
          segments[1].c_str(), segments[0].c_str(), ret1, result.c_str());
      }
      break;

    case 3:  // channel_attribute
      dev = iio_context_find_device(m_ctx, segments[0].c_str());
      if (!dev) {
        RCLCPP_WARN(
          rclcpp::get_logger("rclcpp"), "could not find device \"%s\"",
          segments[0].c_str());
        break;
      }

      if (segments[1].find("input_") == 0) {
        ch = iio_device_find_channel(dev, segments[1].substr(strlen("input_")).c_str(), false);
      } else if (segments[1].find("output_") == 0) {
        ch = iio_device_find_channel(dev, segments[1].substr(strlen("output_")).c_str(), true);
      } else {
        ch = iio_device_find_channel(dev, segments[1].c_str(), false);
        if (!ch) {
          ch = iio_device_find_channel(dev, segments[1].c_str(), true);
        }
      }

      if (!ch) {
        RCLCPP_WARN(
          rclcpp::get_logger("rclcpp"), "could not find channel \"%s\"",
          segments[1].c_str());
        break;
      }

      if (write) {
        ret1 = iio_channel_attr_write(ch, segments[2].c_str(), value.c_str());
        if (ret1 > 0) {
          RCLCPP_INFO(
            rclcpp::get_logger("rclcpp"),
            "wrote channel attribute \"%s\" from channel \"%s\" device \"%s\" with value \"%s\"",
            segments[2].c_str(), segments[1].c_str(), segments[0].c_str(), value.c_str());
        } else {
          result = strerror(-ret1);
          ret = false;
          RCLCPP_WARN(
            rclcpp::get_logger(
              "rclcpp"),
            "could not write attribute \"%s\" from channel \"%s\" device \"%s\" with value \"%s\"- errno %d "
            "- %s",
            segments[2].c_str(), segments[1].c_str(), segments[0].c_str(), value.c_str(), ret1,
            result.c_str());
          break;
        }
      }

      ret1 = iio_channel_attr_read(ch, segments[2].c_str(), attr_val, MAX_ATTR_SIZE);
      if (ret1 > 0) {
        result = attr_val;
        ret = true;
        RCLCPP_INFO(
          rclcpp::get_logger("rclcpp"),
          "read channel attribute \"%s\" from channel \"%s\" device \"%s\" with value \"%s\"",
          segments[2].c_str(), segments[1].c_str(), segments[0].c_str(), attr_val);
      } else {
        result = strerror(-ret1);
        ret = false;
        RCLCPP_WARN(
          rclcpp::get_logger("rclcpp"),
          "could not read attribute \"%s\" from channel \"%s\" device \"%s\" - errno %d - %s",
          segments[2].c_str(), segments[1].c_str(), segments[0].c_str(), ret1, result.c_str());
      }

      break;
    default:
      result = "Service requires attr_path";
      ret = false;
      break;
  }

  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(
      "rclcpp"), "Service request read attr: %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("rclcpp"), "Service request write attr %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;
}

/*AttrTopicEnable.srv
 string topic_name
 string attr_path
 int loop_rate
 int type
 ---------------
 bool success
 string message*/

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(
      "rclcpp"), "Service request enable topic %s with type %s with loop_rate %d Hz",
    request->attr_path.c_str(), request->attr_path.c_str(), request->loop_rate);

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

  std::string local_topic_name = request->topic_name;
  if (local_topic_name == "") {
    local_topic_name = convertAttrPathToTopicName(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)});

    response->success = true;
    response->message = "Success";
  }
}

/*
AttrTopicDisable.srv
string topic_name
---
bool success
string 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(
      "rclcpp"), "Service request disable topic %s ", request->topic_name.c_str());

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

  if (m_attrTopicMap.find(local_topic_name) != m_attrTopicMap.end()) {
    m_attrTopicMap.erase(local_topic_name);
    response->success = true;
    response->message = "Success";
  } else {
    response->success = false;
    response->message = "Topic not found";
  }
}

void IIONode::buffRefillSrv(
  const std::shared_ptr<adi_iio::srv::BufferRefill::Request> request,
  std::shared_ptr<adi_iio::srv::BufferRefill::Response> response)
{
  if (m_bufferMap.find(request->device_path) == m_bufferMap.end()) {
    response->success = false;
    response->message = "Buffer not found";
  } else {
    std::string message;
    if (m_bufferMap[request->device_path]->topic_enabled()) {
    } else {
      response->success = m_bufferMap[request->device_path]->refill(message);
      response->message = message;
    }
    response->buffer = m_bufferMap[request->device_path]->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("rclcpp"), "Service request buffer read %s - %s - %d samples",
    request->device_path.c_str(), channels.c_str(), request->samples_count);

  std::string message;

  std::shared_ptr<IIOBuffer> buffer;
  if (m_bufferMap.find(request->device_path) == m_bufferMap.end()) {
    response->success = false;
    response->message = "Device or buffer not found";
    return;
  }

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

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

  response->success = buffer->refill(message);
  response->buffer = buffer->data();
  response->message = message;
}


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("rclcpp"), "Service request buffer read %s - %s - %d samples",
    request->device_path.c_str(), channels.c_str(), request->buffer.layout.dim[0].size);

  std::string message;

  std::shared_ptr<IIOBuffer> buffer;
  if (m_bufferMap.find(request->device_path) == m_bufferMap.end()) {
    response->success = false;
    response->message = "Device or buffer not found";
    return;
  }

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

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

  response->success = buffer->push(message, request->buffer);
}

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("rclcpp"), "Service request buffer create %s - %s - %d samples",
    request->device_path.c_str(), channels.c_str(), request->samples_count);

  std::string message;

  std::shared_ptr<IIOBuffer> buffer;
  if (m_bufferMap.find(request->device_path) == m_bufferMap.end()) {
    response->success = false;
    response->message = "Buffer not found";
  }

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

  response->success = buffer->createIIOBuffer(message);
  response->message = message;
}

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(
      "rclcpp"), "Service request buffer destroy %s", request->device_path.c_str());

  if (m_bufferMap.find(request->device_path) != m_bufferMap.end()) {
    if (m_bufferMap[request->device_path]->buffer()) {
      m_bufferMap[request->device_path]->destroyIIOBuffer();
      response->success = true;
      response->message = "Success";
      return;
    }
  }

  response->success = false;
  response->message = "Buffer not found";
}

void IIONode::buffEnableTopicSrv(
  const std::shared_ptr<adi_iio::srv::BufferEnableTopic::Request> request,
  std::shared_ptr<adi_iio::srv::BufferEnableTopic::Response> response)
{
  if (m_bufferMap.find(request->device_path) != m_bufferMap.end()) {
    m_bufferMap[request->device_path]->enableTopic(request->topic_name);
    response->success = true;
    response->message = "Success";
  } else {
    response->success = false;
    response->message = "Buffer not found";
  }
}

void IIONode::buffDisableTopicSrv(
  const std::shared_ptr<adi_iio::srv::BufferDisableTopic::Request> request,
  std::shared_ptr<adi_iio::srv::BufferDisableTopic::Response> response)
{
  if (m_bufferMap.find(request->device_path) != m_bufferMap.end()) {
    m_bufferMap[request->device_path]->disableTopic();
    response->success = true;
    response->message = "Success";
  } else {
    response->success = false;
    response->message = "Buffer not found";
  }
}

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

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