Skip to content

Latest commit

 

History

History
305 lines (222 loc) · 6.95 KB

README.md

File metadata and controls

305 lines (222 loc) · 6.95 KB

Tyndall

tests

Blueye Robotics open source c++ components

Requirements

gcc / c++20, cmake

ZeroMQ/libzmq

Protobuf (c++)

fmt

Boost.Python

ROS (optional)

Build

mkdir build
cd build
cmake ..
make

Install

make install

Build examples

make examples

Build and run tests

make tests

(Tests won't get executed when cross compiling.)

Highlights

meta/iterate

Function that calls the given callback with integral constants from 0 to the specified index. Useful for iterating at compile time.

Example:

#include <tyndall/meta/iterate.h>

iterate<10>(
[](auto index){
  // index = std::integral_constant<int, 0>(), std::integral_constant<int, 1>(), ... ,std::integral_constant<int, 9>()
  static_assert((index >= 0) && (index < 10));
  printf("%d ", index()); // 0 1 2 3 4 5 6 7 8 9
});

meta/strval

compile time string which ensures "string A"_strval and "string A"_strval have the same type, while "string A"_strval and "string B"_strval have different types.

Example:

constexpr auto s = "hei"_strval;
printf("%s\n", s.c_str());
printf("%s\n", decltype(sv)::c_str());
printf("%s\n", ("belle"_strval).replace<'e', 'a'>().c_str());
printf("%s\n", to_strval<42>::c_str());

meta/typevals

Container where entries can have different types.

Example:

#include <tyndall/meta/typevals.h>

struct A{int a; float b;};
struct B{double x; char y; char z;};

static constexpr auto tv = typevals{} + 5 + A{4,2} + B{3,9,1};
static_assert(tv.size() == 3);

constexpr auto A42 = tv.get<1>();
static_assert(A42.b == 2);
constexpr auto B391 = tv[std::integral_constant<int, 2>()];
static_assert(B391.x == 3);

zmq_proto

ZeroMQ (libzmq) / Protobuf based tcp communication.

Example:

Publisher

#include <tyndall/proto/zmq_proto.h>

zmq_proto::context_t context{1};
zmq_proto::socket_t<zmq_proto::PUB> socket(context, "tcp://*:5444");

google::protobuf::Int32Value msg;
msg.set_value(42);

int rc = zmq_proto::send(msg, socket);
if (rc != 0)
  printf("errno: %s\n", strerror(errno));

Subscriber

#include <tyndall/proto/zmq_proto.h>

zmq_proto::context_t context{1};
zmq_proto::socket_t<zmq_proto::SUB> socket(context, "tcp://127.0.0.1:5444");

while(1)
{
  google::protobuf::Int32Value msg;

  int rc = zmq_proto::recv(msg, socket);
  if (rc != 0)
  {
    printf("errno: %s\n", strerror(errno));
    exit(1);
  }
}

Full examples in examples/proto/zmq_proto_pub.cpp and examples/proto/zmq_proto_sub.cpp.

ros_context

A wrapper around ros. It provides a minimal of lazy methods for sending and receiving ros messages and service calls.

Pub / sub example:

Reader

#include <tyndall/ros_context.h>
#include <std_msgs/Int32.h>

ros_context::init(argc, argv, std::chrono::milliseconds{3}, "ex_ros_context_write");

std_msgs::Int32 msg;
msg.data = 42;

ros_context_write(msg, "/ex_ros_context");

Writer

#include <tyndall/ros_context.h>
#include <std_msgs/Int32.h>

ros_context::init(argc, argv, std::chrono::milliseconds{3}, "ex_ros_context_read");

while(1)
{
  std_msgs::Int32 msg;

  int rc = ros_context_read(msg, "/ex_ros_context");

  if (rc == 0)
    printf("read: %d\n", msg.data);
}

Serve / call example:

Server

#include <tyndall/ros_context.h>
#include <std_srvs/SetBool.h>

ros_context::init(argc, argv, std::chrono::milliseconds{3}, "ex_ros_context_serve");
while(1)
{
  std_srvs::SetBool srv;
  srv.response.success = true;

  int rc = ros_context_serve(srv, "ex_ros_context");

  if (rc == 0)
    printf("got: %d\n", srv.request.data);
}

Caller

#include <tyndall/ros_context.h>
#include <std_srvs/SetBool.h>

ros_context::init(argc, argv, std::chrono::milliseconds{3}, "ex_ros_context_serve");
while(1)
{
  std_srvs::SetBool srv;
  srv.response.success = true;

  std_srvs::SetBool srv;
  srv.request.data = true;

  int rc = ros_context_call(srv, "/ex_ros_context_serve/ex_ros_context");

  if (rc == 0)
    printf("got: %d\n", srv.response.success);
}

ipc

Inter process communication in Linux based on shared memory and lockless data structures.

Shared memory is created (if needed) and mapped in ipc_write and ipc_read on the first call with a new combination of entry type and id.

Lockless seqlock is used for synchronization. It supports a single writer and multiple readers.

Shared memory is left open on shutdown, so it gets reused on restart. You can remove the shared memory using ipc_cleanup(); or rm /dev/shm/ipc*.

Example:

Writer

#include <tyndall/ipc/ipc.h>

my_struct entry = { .field = 42 };

ipc_write(entry, "my_topic");

Reader

#include <tyndall/ipc/ipc.h>

while (1)
{
  my_struct entry;

  if (ipc_read(entry, "my_topic") == 0)
    printf("entry field: %d\n", entry.field);
}

Full example in examples/ipc/.

Read from command line

The ipc_read tool can be used to print a topic, using the name or path:

tyndall_tool_ipc_read ipc1ef42bc4e0bbfeb0ac34bc3642732768cf6f77b7_my_topic_891174619
tyndall_tool_ipc_read /dev/shm/ipc1ef42bc4e0bbfeb0ac34bc3642732768cf6f77b7_my_topic_891174619

For aggregate initializable entry types, this will attempt to print the fields of the entry. If the entry is not aggregate initializable, the hexadecimal representation of the entry will be printed. You can still get a formatted print of non-aggregate-initializables by supplying the format yourself:

tyndall_tool_ipc_read ipc1ef42bc4e0bbfeb0ac34bc3642732768cf6f77b7_my_topic_891174619 f

The format is a list of type ids according to:

s = string (null terminated)
b = bool
f = float
d = double
i = int
j = unsigned int
c = char
h = unsigned char
l = long
m = unsigned long
x = long long
y = unsigned long long

So to print a struct struct S{ int a; unsigned char b; float c, d; S(){}} you would run:

tyndall_tool_ipc_read ipc1ef42bc4e0bbfeb0ac34bc3642732768cf6f77b7_my_topic_891174619 ihff

Note that the entry is assumed to have standard alignment.

You can ignore a specified amount of bytes in the struct by having a number in the format:

tyndall_tool_ipc_read ipc1ef42bc4e0bbfeb0ac34bc3642732768cf6f77b7_my_topic_891174619 i8f

You can use fzf to choose and view topics:

ls /dev/shm/ipc* | fzf --preview 'tyndall_tool_ipc_read {1} | xargs -I{} echo $(clear) {}' --reverse