Blueye Robotics open source c++ components
gcc / c++20, cmake
mkdir build
cd build
cmake ..
make
make install
make examples
make tests
(Tests won't get executed when cross compiling.)
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
});
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());
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);
ZeroMQ (libzmq) / Protobuf based tcp communication.
Example:
#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));
#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.
A wrapper around ros. It provides a minimal of lazy methods for sending and receiving ros messages and service calls.
Pub / sub example:
#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");
#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:
#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);
}
#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);
}
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:
#include <tyndall/ipc/ipc.h>
my_struct entry = { .field = 42 };
ipc_write(entry, "my_topic");
#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/.
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