Part 12 (Playback Log Data)
In this section, we’ll build a simple pipeline to playback a data log. We’ll set up the pipeline to run two SecondStages, but there will not be a FirstStage. Instead, we will simulate FirstStage output by using the logged data. The log will be the source of MyString
data, which will trigger the subscriber callbacks in SecondStage. We call this concept “LogSim”, details here
Let’s start by creating a new pipeline ${APP_ROOT}/tutorial/playback/playback_pipeline.cc
:
#include "ark/comms/stages/http_server_stage.hh"
#include "ark/main/main_offboard.hh"
#include "ark/pipeline/config.hh"
#include "ark/pipeline/pipeline.hh"
#include "tutorial/second_stage/second_stage.hh"
#include <iostream>
int main(int argc, const char **argv)
{
try
{
//
// Setup for playback - The LogPlaybackConfiguration will add a LogReaderStage to our pipeline
//
auto config = ark::main::MainOffboardConfiguration::LogPlaybackConfiguration();
//
// Allow for user interaction with this pipeline
//
config.interactive_playback = true;
//
// Construct pipeline
//
ark::pipeline::Pipeline pipeline;
pipeline.add_stage<ark::comms::HttpServerStage>();
//
// Only add in SecondStages
//
pipeline.add_stage<SecondStage>("SecondStage01");
pipeline.add_stage<SecondStage>("SecondStage02");
//
// Execute in realtime, to allow user interaction to pause the pipeline via LogReaderPlugin control
//
return ark::main::execute_realtime_pipeline(argc, argv, std::move(pipeline), config);
}
catch (const std::exception &exception)
{
std::cerr << "FATAL: " << exception.what() << std::endl;
return 1;
}
}
In this implementation, we are setting up the pipeline to use the real time executor, which will execute the data in real-time and allow us to interact with the pipeline.
Now let’s create a ${APP_ROOT}/tutorial/playback/CMakeLists.txt
to set up this pipeline. Note that we are not linking in FirstStage at all.
add_executable(playback_pipeline playback_pipeline.cc)
target_link_libraries(
playback_pipeline
PRIVATE second_stage
ark::main_offboard
ark::http_server_stage
)
Then add this to the end of ${APP_ROOT}/CMakeLists.txt
:
add_subdirectory(tutorial/playback)
Now let’s invoke ‘make.sh’ to build:
./make.sh
Running Playback
Now that we have compiled our playback pipeline, we can execute it:
./build/playback_pipeline
Note that there is an ERROR reported stating that required arguments are missing:
~/ark$ ./build/playback_pipeline
ERROR: The positional 'log-url' must be specified at least once.
Usage:
./build/playback_pipeline [OPTIONS] <log-url>
Options:
--version Print version information about this binary out
-v, --verbose Increase verbosity level (can be set multiple times)
--dot-output Emit a dot-file illustrating the pipeline layout
Video:
--video PATH Write renderable output to the given video file
--video-backend Backend to use for video generation [cairo, magnum].
Catalog:
--register-catalog URL Registers your artifacts with the catalog at the given URL.
Reads from environment 'CATALOG_API_URL' when available
--artifact-id GUID When registering catalog artifacts, use this identifier.
Reads from environment 'ARK_ARTIFACT_ID' when available
Pipeline:
-c, --pipeline-config Path to the pipeline configuration file to load
--throttle-to-system-time ARG The simclock runner will run no faster than system time scaled by this amount
--collect-runtimes OUTPUT_PATH Add stage to calculate the expected runtimes for this pipeline
Log Playback:
-n, --minimum-time Time to start log playback at (in seconds)
-x, --maximum-time Time to stop log playback at (in seconds)
--force-playback-all-channels Forces playback of all channels, not just the subscribed channels
HTTP Server:
--http-server-address ADDRESS The network address to use for the http server stage.
(Default: 0.0.0.0:8080)
--disable-http-server Prevents the HTTP server from initializing.
Positional:
log-url URL or path to the log manifest for playback
The playback_pipeline requires input to the LogReaderStage to point to the path where the log exists.
Let’s run the binary again and point to one of the logs created. The logs will be located on your local machine at `/tmp/ark_logs’. Set the path to one of the manifest
~/ark$ ./build/playback_pipeline /tmp/ark_logs/manifests/615b0adc-3e18-4d9d-87bf-0dc991cca2c8
(2022-03-23 10:32:52.833) [info] (version.cc:16) Starting execution with ARK_V2.0.0-0-g18b30772/x86_64 (18b3077258b9002f8233a410f5e357f72a14c9fc)
(2022-03-23 10:32:52.834) [info] (http_server_stage.cc:874) HTTP Server is listening on "0.0.0.0:8080".
(2022-03-23 10:32:52.854) [info] (log_reader_stage.cc:1116) Playing back log, at time 0s, 0.0% complete.
(2022-03-23 10:32:52.854) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 0.504266066, my message is HELLO!.'
(2022-03-23 10:32:52.854) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 0.504266066, my message is HELLO!.'
(2022-03-23 10:32:53.334) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 1.004266066, my message is HELLO!.'
(2022-03-23 10:32:53.335) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 1.004266066, my message is HELLO!.'
(2022-03-23 10:32:53.834) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 1.504266066, my message is HELLO!.'
(2022-03-23 10:32:53.834) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 1.504266066, my message is HELLO!.'
(2022-03-23 10:32:54.334) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 2.004266066, my message is HELLO!.'
(2022-03-23 10:32:54.334) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 2.004266066, my message is HELLO!.'
(2022-03-23 10:32:54.834) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 2.504266066, my message is HELLO!.'
(2022-03-23 10:32:54.834) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 2.504266066, my message is HELLO!.'
(2022-03-23 10:32:55.334) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 3.004266066, my message is HELLO!.'
(2022-03-23 10:32:55.334) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 3.004266066, my message is HELLO!.'
(2022-03-23 10:32:55.834) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 3.504266066, my message is HELLO!.'
(2022-03-23 10:32:55.834) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 3.504266066, my message is HELLO!.'
(2022-03-23 10:32:56.335) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 4.004266066, my message is HELLO!.'
(2022-03-23 10:32:56.335) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 4.004266066, my message is HELLO!.'
(2022-03-23 10:32:56.834) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 4.504266066, my message is HELLO!.'
(2022-03-23 10:32:56.834) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 4.504266066, my message is HELLO!.'
(2022-03-23 10:32:57.334) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 5.004266066, my message is HELLO!.'
(2022-03-23 10:32:57.334) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 5.004266066, my message is HELLO!.'
(2022-03-23 10:32:57.334) [info] (log_reader_stage.cc:838) Log playback complete.
Note that the two SecondStages execute their callbacks with the logged MyString messages from the log.
Due to setting interactive_playback
on the pipeline configuration, the pipeline will run indefinitely, until the user exits the process.
User Interaction
Let’s add the LogReaderPlugin to the GUI so we can interact with the LogReaderStage pipeline.
Edit the ${APP_ROOT}/tutorial/gui/tutorial_gui.cc
file to add the LogReaderPlugin:
#include "ark/gui/gui_application.hh"
#include "ark/config/gui/config_package_plugin.hh"
#include "ark/debuglog/gui/debug_logger_plugin.hh"
#include "ark/logging/gui/log_writer_plugin.hh"
#include "ark/logging/gui/log_reader_plugin.hh"
#include "ark/perf/gui/tracer_plugin.hh"
#include "ark/pipeline/gui/pipeline_metrics_plugin.hh"
#include "ark/plotting/gui/json_decoder_plugin.hh"
#include "ark/plotting/gui/plotting_host_plugin.hh"
int main(int argc, const char **argv)
{
ark::gui::GuiApplicationConfig app_config;
app_config.name = "Favorite GUI";
ark::gui::GuiApplication application(std::move(app_config), argc, argv);
// Central
application.register_central_plugin<ark::config::ConfigPackagePlugin>();
application.register_central_plugin<ark::perf::TracerPlugin>();
application.register_central_plugin<ark::pipeline::PipelineMetricsPlugin>();
application.register_central_plugin<ark::plotting::PlottingHostPlugin>();
// debug messagse
application.register_bottom_plugin<ark::debuglog::DebugLoggerPlugin>();
// Log Writer
application.register_right_plugin<ark::logging::LogWriterPlugin>();
application.register_right_plugin<ark::plotting::JsonDecoderPlugin>();
// Log Reader
application.register_right_plugin<ark::logging::LogReaderPlugin>();
return application.execute();
}
Next, update the ${APP_ROOT}/tutorial/gui/CMakeLists.txt
file to link in the plugin:
qt_add_resources(resources_source ${QDARKSTYLE_RESOURCE})
add_library(tutorial_resources ${resources_source})
add_executable(tutorial_gui tutorial_gui.cc)
target_link_libraries(
tutorial_gui
PRIVATE ark::gui
ark::config_package_plugin
ark::debug_logger_plugin
ark::tracer_plugin
ark::log_writer_plugin
ark::log_reader_plugin
ark::pipeline_metrics_plugin
ark::plotting_host_plugin
ark::json_decoder_plugin
-Wl,--whole-archive
tutorial_resources
-Wl,--no-whole-archive)
Let’s build the gui and run it to see the LogPlayer plugin
./make.sh
Running playback and gui
Execute both the gui and the playback_pipeline to interact with the LogReader control.
In one terminal, start the gui:
./build/tutorial_gui
In another terminal, start the playback pipeline:
~/ark$ ./build/playback_pipeline /tmp/ark_logs/manifests/615b0adc-3e18-4d9d-87bf-0dc991cca2c8
In the gui window, click on the the Log Player
tab along the right pane. Your window should look something like this.
Note that you can see the current playback time, information about the log (such as name, guid, etc). You can also try pressing
Pause
and Play
to start/stop playback.
This is an example of how to simulate stage executing in a pipeline using logged data.
SimClock playback
Let’s modify our pipeline to run using the SimClock. More details on logsim
Let’s start by creating a new pipeline ${APP_ROOT}/tutorial/playback/playback_pipeline.cc
:
#include "ark/comms/stages/http_server_stage.hh"
#include "ark/main/main_offboard.hh"
#include "ark/pipeline/config.hh"
#include "ark/pipeline/pipeline.hh"
#include "tutorial/second_stage/second_stage.hh"
#include <iostream>
int main(int argc, const char **argv)
{
try
{
//
// Setup for playback - The LogPlaybackConfiguration will add a LogReaderStage to our pipeline
//
auto config = ark::main::MainOffboardConfiguration::LogPlaybackConfiguration();
//
// Disable interaction
//
config.interactive_playback = false;
ark::pipeline::Pipeline pipeline;
pipeline.add_stage<ark::comms::HttpServerStage>();
pipeline.add_stage<SecondStage>("SecondStage01");
pipeline.add_stage<SecondStage>("SecondStage02");
//
// Execute via simclock.
//
return ark::main::execute_simclock_pipeline(argc, argv, std::move(pipeline), config);
}
catch (const std::exception &exception)
{
std::cerr << "FATAL: " << exception.what() << std::endl;
return 1;
}
}
Recompile playback_pipeline and execute it to see the difference in behavior.
$ ./build/playback_pipeline /tmp/ark_logs/manifests/615b0adc-3e18-4d9d-87bf-0dc991cca2c8
(2022-03-23 11:01:40.437) [info] (version.cc:16) Starting execution with ARK_V2.0.0-0-g18b30772/x86_64 (18b3077258b9002f8233a410f5e357f72a14c9fc)
(2022-03-23 11:01:40.438) [info] (simclock_executor.cc:370) The SimClockExecutor is initializing the start time to 0.504644301s.
(2022-03-23 11:01:40.438) [info] (log_reader_stage.cc:1116) Playing back log, at time 0s, 0.0% complete.
(2022-03-23 11:01:40.438) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 0.504266066, my message is HELLO!.'
(2022-03-23 11:01:40.438) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 0.504266066, my message is HELLO!.'
(2022-03-23 11:01:40.438) [info] (http_server_stage.cc:874) HTTP Server is listening on "0.0.0.0:8080".
(2022-03-23 11:01:40.438) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 1.004266066, my message is HELLO!.'
(2022-03-23 11:01:40.438) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 1.004266066, my message is HELLO!.'
(2022-03-23 11:01:40.439) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 1.504266066, my message is HELLO!.'
(2022-03-23 11:01:40.439) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 1.504266066, my message is HELLO!.'
(2022-03-23 11:01:40.439) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 2.004266066, my message is HELLO!.'
(2022-03-23 11:01:40.439) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 2.004266066, my message is HELLO!.'
(2022-03-23 11:01:40.440) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 2.504266066, my message is HELLO!.'
(2022-03-23 11:01:40.440) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 2.504266066, my message is HELLO!.'
(2022-03-23 11:01:40.440) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 3.004266066, my message is HELLO!.'
(2022-03-23 11:01:40.440) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 3.004266066, my message is HELLO!.'
(2022-03-23 11:01:40.441) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 3.504266066, my message is HELLO!.'
(2022-03-23 11:01:40.441) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 3.504266066, my message is HELLO!.'
(2022-03-23 11:01:40.442) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 4.004266066, my message is HELLO!.'
(2022-03-23 11:01:40.442) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 4.004266066, my message is HELLO!.'
(2022-03-23 11:01:40.442) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 4.504266066, my message is HELLO!.'
(2022-03-23 11:01:40.442) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 4.504266066, my message is HELLO!.'
(2022-03-23 11:01:40.442) [info] (log_reader_stage.cc:838) Log playback complete.
(2022-03-23 11:01:40.442) [info] (context.cc:850) 'LogReaderStage' has requested a shutdown: Success.
(2022-03-23 11:01:40.442) [info] (context.cc:884) All tokens have requested a shutdown; the pipeline will exit.
(2022-03-23 11:01:40.442) [info] (second_stage.cc:40) SecondStage01 stage received message: 'At 5.004266066, my message is HELLO!.'
(2022-03-23 11:01:40.442) [info] (second_stage.cc:40) SecondStage02 stage received message: 'At 5.004266066, my message is HELLO!.'
(2022-03-23 11:01:40.442) [info] (simclock_executor.cc:503) Simulation complete (simulated 4.50s in 0.00s for 512 executions, 952.83x sim:wall time).
The pipeline completes and exits on its own when the log data has been fully read.
Note that the SecondStages still execute their subscriber callbacks as they did with the realtime executor, but the time in which they executor is much faster. The execution is run 952x faster than wall time, in this case.
LogSim is a benefical concept to test system behavior using recorded real-world data.
This concludes our tutorial. Please see the other bits of documentation to learn more about using individual Ark features!