Skip to content

Commit

Permalink
feat: add WIP multicast driver
Browse files Browse the repository at this point in the history
  • Loading branch information
willeccles committed May 13, 2024
1 parent 0205626 commit 5c6f570
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ FetchContent_MakeAvailable(fast_float)
add_library(bciabs_scpi
src/TcpDriver.cpp
src/UdpDriver.cpp
src/UdpMulticastDriver.cpp
src/SerialDriver.cpp
src/ScpiUtil.cpp
src/ScpiClient.cpp
Expand Down
36 changes: 36 additions & 0 deletions include/bci/abs/UdpMulticastDriver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef ABS_SCPI_DRIVER_INCLUDE_BCI_ABS_UDPMULTICASTDRIVER_H
#define ABS_SCPI_DRIVER_INCLUDE_BCI_ABS_UDPMULTICASTDRIVER_H

#include <memory>
#include <string>
#include <string_view>

#include "CommDriver.h"
#include "CommonTypes.h"

namespace bci::abs::drivers {

class UdpMcastDriver final : public CommDriver {
public:
UdpMcastDriver();

~UdpMcastDriver();

ErrorCode Open();

void Close() noexcept;

ErrorCode Write(std::string_view data, unsigned int timeout_ms) const;

Result<std::string> ReadLine(unsigned int timeout_ms) const;

bool IsSendOnly() const { return true; }

private:
struct Impl;
std::shared_ptr<Impl> impl_;
};

} // namespace bci::abs::drivers

#endif /* ABS_SCPI_DRIVER_INCLUDE_BCI_ABS_UDPMULTICASTDRIVER_H */
202 changes: 202 additions & 0 deletions src/UdpMulticastDriver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
#include <bci/abs/UdpMulticastDriver.h>

#include <array>
#include <boost/asio.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/asio/write.hpp>
#include <cstdint>
#include <memory>
#include <string>
#include <string_view>

#include "Util.h"

using boost::asio::deadline_timer;
using boost::asio::ip::udp;

namespace bci::abs::drivers {

static constexpr std::string_view kMulticastAddr = "239.188.26.181";

using util::Err;

struct UdpMcastDriver::Impl {
Impl();

ErrorCode Open();

ErrorCode Open(std::string_view local_ip);

void Close() noexcept;

ErrorCode Write(std::string_view data, unsigned int timeout_ms);

Result<std::string> ReadLine(unsigned int timeout_ms);

private:
static constexpr std::size_t kBufLen = 8192;

boost::asio::io_service io_service_;
boost::asio::ip::udp::socket socket_;
boost::asio::deadline_timer deadline_;
boost::asio::ip::udp::endpoint endpoint_;
std::array<std::uint8_t, kBufLen> buf_;

void CheckDeadline();
};

UdpMcastDriver::UdpMcastDriver() : impl_(std::make_shared<Impl>()) {}

UdpMcastDriver::~UdpMcastDriver() { Close(); }

ErrorCode UdpMcastDriver::Open() { return impl_->Open(); }

void UdpMcastDriver::Close() noexcept { impl_->Close(); }

ErrorCode UdpMcastDriver::Write(std::string_view data,
unsigned int timeout_ms) const {
return impl_->Write(data, timeout_ms);
}

Result<std::string> UdpMcastDriver::ReadLine(unsigned int timeout_ms) const {
return impl_->ReadLine(timeout_ms);
}

UdpMcastDriver::Impl::Impl()
: io_service_(),
socket_(io_service_),
deadline_(io_service_),
endpoint_(),
buf_{} {
deadline_.expires_at(boost::posix_time::pos_infin);
CheckDeadline();
}

ErrorCode UdpMcastDriver::Impl::Open() {
return Open("0.0.0.0");
}

ErrorCode UdpMcastDriver::Impl::Open(std::string_view local_ip) {
if (socket_.is_open()) {
return ErrorCode::kAlreadyConnected;
}

boost::system::error_code ec{};
auto local_address = boost::asio::ip::make_address_v4(local_ip, ec);
if (ec) {
return ErrorCode::kInvalidIPAddress;
}

auto remote_address = boost::asio::ip::make_address_v4(kMulticastAddr, ec);
if (ec) {
return ErrorCode::kInvalidIPAddress;
}

endpoint_ = boost::asio::ip::udp::endpoint(remote_address, 5025);

socket_.open(endpoint_.protocol(), ec);
if (ec) {
return ErrorCode::kSocketError;
}

// TODO: is this a reasonable size?
boost::asio::socket_base::receive_buffer_size rx_buf_opt{1024 * 64};
socket_.set_option(rx_buf_opt, ec);
if (ec) {
socket_.close();
return ErrorCode::kSocketError;
}

#if 0
boost::asio::ip::udp::endpoint local_endpoint(local_address, 0);

socket_.bind(local_endpoint, ec);
if (ec) {
socket_.close();
return ErrorCode::kFailedToBindSocket;
}
#endif

return ErrorCode::kSuccess;
}

void UdpMcastDriver::Impl::Close() noexcept {
boost::system::error_code ignored;
if (socket_.is_open()) {
socket_.close(ignored);
}
}

ErrorCode UdpMcastDriver::Impl::Write(std::string_view data,
unsigned int timeout_ms) {
if (!socket_.is_open()) {
return ErrorCode::kNotConnected;
}

deadline_.expires_from_now(boost::posix_time::milliseconds(timeout_ms));

boost::system::error_code ec = boost::asio::error::would_block;

const auto write_handler = [&](auto&& e, auto&&) { ec = e; };
socket_.async_send_to(boost::asio::buffer(data), endpoint_, write_handler);

do {
io_service_.run_one();
} while (ec == boost::asio::error::would_block);

if (ec) {
return ErrorCode::kSendFailed;
}

if (!socket_.is_open()) {
return ErrorCode::kSendTimedOut;
}

return ErrorCode::kSuccess;
}

Result<std::string> UdpMcastDriver::Impl::ReadLine(unsigned int timeout_ms) {
if (!socket_.is_open()) {
return Err(ErrorCode::kNotConnected);
}

deadline_.expires_from_now(boost::posix_time::milliseconds(timeout_ms));

boost::system::error_code ec = boost::asio::error::would_block;

std::size_t read_len{};

const auto read_handler = [&](auto&& e, std::size_t len) {
ec = e;
read_len = len;
};
socket_.async_receive(boost::asio::buffer(buf_), read_handler);

do {
io_service_.run_one();
} while (ec == boost::asio::error::would_block);

if (ec) {
return Err(ErrorCode::kReadFailed);
}

if (!socket_.is_open()) {
return Err(ErrorCode::kReadTimedOut);
}

std::string line((const char*)buf_.data(), read_len);

return line;
}

void UdpMcastDriver::Impl::CheckDeadline() {
if (deadline_.expires_at() <= deadline_timer::traits_type::now()) {
Close();
deadline_.expires_at(boost::posix_time::pos_infin);
}

deadline_.async_wait([&](auto&&) { this->CheckDeadline(); });
}

} // namespace bci::abs::drivers

0 comments on commit 5c6f570

Please sign in to comment.