Part 1 (Publishers)
This tutorial will walk you through the creation of a basic pipeline, including the ability to serialize data, log it, and view it in the C++ visualizer.
First, we’ll make a few assumptions:
- Ark is installed in
${APP_ROOT}
/ark - You have successfully built Ark (and all required dependencies are installed)
Creating Your First Stage
The first thing we’ll build is a simple stage that emits a message out at a fixed interface. This will demonstrate compiling a stage and the creation of both publishers and periodic timers.
Notice
For the purposes of this example, we’ll place all of your code into the Ark repository – normally, you would place it externally.First, let’s make a directory for your example and stages:
cd ${APP_ROOT}/ark
mkdir -p tutorial/first_stage
cd tutorial/first_stage
Next, let’s create the stage itself. We’ll be using C++, so the first thing to do
is to define the header file. Place this in ${APP_ROOT}/tutorial/first_stage/first_stage.hh
:
#pragma once
#include "ark/pipeline/stage.hh"
#include <string>
class FirstStage : public ark::pipeline::Stage
{
public:
/// Constructor -- this initializes the parent class (Stage)
/// with a name. Names must be unique.
FirstStage() : ark::pipeline::Stage("FirstStage")
{
}
/// This is invoked when the system starts up, and gives us a chance
/// to register callbacks.
void initialize(ark::pipeline::StageInterface &interface) override;
};
Make sure to see the comments above for rationale as to why that code exists. The key
point is that we have an initialize
method that will be invoked by Ark to give you
a chance to register a timer.
Notice
Note that a stage header will be included by potentially many pipelines or tests. It can be advantangeous to keep these as light-weight as possible to keep your compile times down. Look into forward declarations or the PIMPL pattern for more information.Next up, let’s implement the class. Place this in ${APP_ROOT}/tutorial/first_stage/first_stage.cc
:
#include "tutorial/first_stage/first_stage.hh"
#include "ark/pipeline/publisher.hh"
#include "ark/pipeline/periodic_timer_config.hh"
#include "ark/pipeline/stage_interface.hh"
#include "fmt/format.h"
void FirstStage::initialize(ark::pipeline::StageInterface &interface)
{
// First, create your publisher. This is the 'channel' that you will
// emit strings on. Other stages will be able to receive these strings.
auto publisher = interface.add_publisher<std::string>("/my_strings");
// Next, we'll register a timer. This timer will run at 1Hz and emit
// a string execution.
{
ark::pipeline::PeriodicTimerConfiguration config;
config.name = "emit_string";
config.rate = std::chrono::seconds{1};
config.callback = [publisher](const auto &now) {
// 'now' contains the current "pipeline time". Convert it into
// a double for easier printing.
auto time_s = std::chrono::duration<double>(now.time_since_epoch());
publisher->push(fmt::format("Hello world, it's now {}.", time_s.count()));
};
interface.add_timer(config);
}
}
This populates the initialize
method with two things: a publisher, which writes
messages over the /my_strings
channel, and a periodic timer, which runs every second,
and pushes a string with the current time onto the publisher.
Compiling
To build this stage, we’ll need a cmake file. Place this in ${APP_ROOT}/tutorial/first_stage/CMakeLists.txt
:
add_library(first_stage first_stage.cc)
target_link_libraries(first_stage
PUBLIC ark::pipeline
PRIVATE fmt::fmt)
Then add this line at the bottom of ${APP_ROOT}/CMakeLists.txt
:
add_subdirectory(tutorial/first_stage)
Now, you should be able to build your stage! Execute:
cd ${APP_ROOT}
./make.sh
You should compile without any errors. However, you don’t have a pipeline yet, so you can’t actually run anything.
Move on to Step 2.