Skip to content

Commit

Permalink
Examples
Browse files Browse the repository at this point in the history
Created 5 examples and a way to compile them with make example. Basic example, content example, holder example, utils example and aggregated file example. All have comprehensive comments and are somewhat interactive.
  • Loading branch information
Daniel Pelanek committed Jun 19, 2024
1 parent cb7ab15 commit b0ef6a1
Show file tree
Hide file tree
Showing 8 changed files with 457 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ option(TELEMETRY_BUILD_SHARED "Build shared library" ON)
option(TELEMETRY_PACKAGE_BUILDER "Enable RPM package builder (make rpm)" ON)
option(TELEMETRY_INSTALL_TARGETS "Generate the install target" ON)
option(TELEMETRY_ENABLE_TESTS "Build Unit tests (make test)" OFF)
option(TELEMETRY_BUILD_EXAMPLES "Build included example files (make example)" ON)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand All @@ -33,6 +34,10 @@ endif()

include(cmake/dependencies.cmake)

if (TELEMETRY_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()

if (TELEMETRY_ENABLE_TESTS)
include(cmake/googletest.cmake)
include(GoogleTest)
Expand Down
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ test: build
@$(MAKE) --no-print-directory -C build
@$(MAKE) test --no-print-directory -C build

example: build
@cd build && $(CMAKE) $(CMAKE_ARGS) -DTELEMETRY_BUILD_EXAMPLES=ON ..
@$(MAKE) --no-print-directory -C build
44 changes: 44 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
add_executable(test1
basicExample.cpp
)

target_link_libraries(test1 PRIVATE
telemetry::telemetry
telemetry::appFs
)

add_executable(test2
contentExample.cpp
)

target_link_libraries(test2 PRIVATE
telemetry::telemetry
telemetry::appFs
)

add_executable(test3
holderExample.cpp
)

target_link_libraries(test3 PRIVATE
telemetry::telemetry
telemetry::appFs
)

add_executable(test4
utilsExample.cpp
)

target_link_libraries(test4 PRIVATE
telemetry::telemetry
telemetry::appFs
)

add_executable(test5
aggregatedFileExample.cpp
)

target_link_libraries(test5 PRIVATE
telemetry::telemetry
telemetry::appFs
)
107 changes: 107 additions & 0 deletions examples/aggregatedFileExample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* @file
* @author Daniel Pelanek <xpeland00@vutbr.cz>
* @brief Shows how the aggregated files are used and it's methods
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include <telemetry.hpp>
#include <appFs.hpp>

// example help
#include <iostream>

telemetry::Content returnValue(int value) {
return telemetry::Scalar(value);
}

telemetry::Content returnValueDict(int value) {
telemetry::Dict dict;
dict["1"] = telemetry::Scalar(value*2);

return dict;
}

int main() {
// Same as basic example
std::shared_ptr<telemetry::Directory> telemetryRootNode;
telemetryRootNode = telemetry::Directory::create();

std::string fusePath = "fusedir";

telemetry::appFs::AppFsFuse fuse = telemetry::appFs::AppFsFuse(telemetryRootNode, fusePath, true, true);
fuse.start();

// We create two fileops that just return a constant value
const telemetry::FileOps fileOps1
= {[]() { return returnValue(2); }, nullptr};

const telemetry::FileOps fileOps2
= {[]() { return returnValue(5); }, nullptr};


// It is better to keep the files in another directory and not
// aggregate them straight from root dir.
auto aggFileDir = telemetryRootNode->addDir("aggDir");
auto file1 = aggFileDir->addFile("file1", fileOps1);
auto file2 = aggFileDir->addFile("file2", fileOps2);

// Here we init three different aggregation operations.
// One of them is join meaning it will add values from all files into an array.
// The two arguments after the method type are source dict field name and result dict field name
// If they are left empty ("") the values are taken directly from file->read() and
// directly returned as telemetry::Scalar, telemetry::Array, ...
// The result here is 1 : [2, 5].
const telemetry::AggOperation aggOp0 = {telemetry::AggMethodType::JOIN, "", "1"};

// Average of read from files in aggfile if there are different types of Scalar(WithUnit)s the return
// type is same as in cpp meaning avg of uint64_t, int64_t and double the result is double.
const telemetry::AggOperation aggOp1 = {telemetry::AggMethodType::AVG};
// Sum of read from files in aggfile
const telemetry::AggOperation aggOp2 = {telemetry::AggMethodType::SUM};

// We have to add these aggregation operations into a vector.
std::vector<telemetry::AggOperation> aggOps0 = {aggOp0};
std::vector<telemetry::AggOperation> aggOps1 = {aggOp1};
std::vector<telemetry::AggOperation> aggOps2 = {aggOp2};

// The method for creating an aggregated file is addAggFile
// You can specify which of the files from a directory are picked
// by regex.
auto aggFile0 = aggFileDir->addAggFile("aggFile", "file[0-9]+", aggOps0);
auto aggFile1 = aggFileDir->addAggFile("aggFile1", "file[0-9]+", aggOps1);
auto aggFile2 = aggFileDir->addAggFile("aggFile2", "file[0-9]+", aggOps2);

// Just for showing the results to terminal. The same results will be in the
// actual filesystem.
std::cout << "Single aggregation operations: \n";
std::cout << telemetry::contentToString(aggFile0->read()) << "\n";
std::cout << telemetry::contentToString(aggFile1->read()) << "\n";
std::cout << telemetry::contentToString(aggFile2->read()) << "\n";


// Using two aggregation operations in a single file is only possible when
// the sources are dicts.
const telemetry::FileOps fileOpsDict1
= {[]() { return returnValueDict(2); }, nullptr};

const telemetry::FileOps fileOpsDict2
= {[]() { return returnValueDict(5); }, nullptr};

auto aggFileDirDict = telemetryRootNode->addDir("aggDirDict");
auto fileDict1 = aggFileDirDict->addFile("fileDict1", fileOpsDict1);
auto fileDict2 = aggFileDirDict->addFile("fileDict2", fileOpsDict2);

// The result dict keys have to be different. If they are the same only
// the first result is valid and is read.
const telemetry::AggOperation aggOpDict0 = {telemetry::AggMethodType::JOIN, "1", "1"};
const telemetry::AggOperation aggOpDict1 = {telemetry::AggMethodType::AVG, "1", "2"};

std::vector<telemetry::AggOperation> aggOpsDict = {aggOpDict0, aggOpDict1};

auto aggFileDict = aggFileDirDict->addAggFile("aggFileDict", "fileDict[0-9]+", aggOpsDict);

std::cout << "\nMultiple aggregation operations: \n";
std::cout << telemetry::contentToString(aggFileDict->read()) << "\n";
}
100 changes: 100 additions & 0 deletions examples/basicExample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* @file
* @author Daniel Pelanek <xpeland00@vutbr.cz>
* @brief Shows the basic usage of telemetry with fuse
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include <telemetry.hpp>
#include <appFs.hpp>
#include <memory>

// example help
#include <chrono>
#include <csignal>
#include <atomic>
std::atomic<bool> g_gotSIGINT(false);
void signalHandler(int signum) {
(void)signum;
g_gotSIGINT.store(true);
}


// The return value has to be telemetry::Content or one of its variants.
telemetry::Content getTimeElapsed(std::chrono::time_point<std::chrono::system_clock>& startTime) {
auto now = std::chrono::system_clock::now();
auto timeElapsed = std::chrono::duration_cast<std::chrono::seconds>(now - startTime).count();

// telemetry::Content is std::variant<Scalar, ScalarWithUnit, Array, Dict>.
// How they are used is shown in another example.
return telemetry::Scalar(timeElapsed);
}

// Resets start time to the current time.
void clearTime(std::chrono::time_point<std::chrono::system_clock>& startTime) {
startTime = std::chrono::system_clock::now();
}

int main() {
// Creating root dir for filesystem.
std::shared_ptr<telemetry::Directory> telemetryRootNode;
telemetryRootNode = telemetry::Directory::create();

// The path to root dir is local to where the program is called from.
std::string fusePath = "fusedir";

// Linking root dir to the chosen directory on disk.
// If the fourth argument is true it means the directory on disk doesn't need to
// exist before starting program.
telemetry::appFs::AppFsFuse fuse = telemetry::appFs::AppFsFuse(telemetryRootNode, fusePath, true, true);
fuse.start();
// The filesystem is still just empty.
// /

// So let's a directory named input into the root dir.
std::shared_ptr<telemetry::Directory> inputDir = telemetryRootNode->addDir("input");
// Now the filesystem looks like this.
// /
// └─ input/


// Every file can have two lambdas attached to it.
//
// One for reading -> What gets called when something tries to read the file on disk.
// Here we write the return value of getTime to the file.
//
// One for clearing -> What gets called when you want to reset telemetry data.
// In this case we reset the startTime.
auto startTime = std::chrono::system_clock::now();
const telemetry::FileOps fileOps
= {[&startTime]() { return getTimeElapsed(startTime); },
[&startTime]() { return clearTime(startTime); }};

// The read and clear functions are optional. In the case you don't use them pass
// a null pointer instead.
const telemetry::FileOps anotherFileOps
= {nullptr, nullptr};


// Files be put into the root directory.
const std::shared_ptr<telemetry::File> timeFile = telemetryRootNode->addFile("time", fileOps);
// Now it looks like this.
// /
// ├─ input/
// └─ time

// Or into another directory.
const std::shared_ptr<telemetry::File> anotherTimeFile = inputDir->addFile("time", anotherFileOps);
// Now it looks like this.
// /
// ├─ input/
// │ └─ time
// └─ time

// Waiting for ctrl+c. In the meantime you can open another terminal and
// navigate to the newly linked directory. Just reading the new time file
// should print the time elapsed in seconds since the start of the program.
std::signal(SIGINT, signalHandler);
while(!g_gotSIGINT.load()){};
}
57 changes: 57 additions & 0 deletions examples/contentExample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* @file
* @author Daniel Pelanek <xpeland00@vutbr.cz>
* @brief Shows how telemetry content data types are used
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include <telemetry.hpp>

// example help
#include <iostream>

// Simplest type. Only contains one value .
// The values can be of type std::monostate, bool, uint64_t, int64_t, double, std::string.
telemetry::Scalar scalarReturn() {
return telemetry::Scalar(static_cast<uint64_t>(100));
}

// Contains not only scalar but also has a unit.
// The unit is of type string.
telemetry::ScalarWithUnit scalartWithUnitReturn() {
return telemetry::ScalarWithUnit(static_cast<double>(42), "%");
}

// Array is a vector of Scalars.
telemetry::Array arrayReturn() {
telemetry::Array arr;

const uint64_t nScalars = 10;

for(uint64_t index = 0; index < nScalars; index++) {
arr.emplace_back(telemetry::Scalar(index));
}

return arr;
}

// Dict is a map with string as a key.
// Value can be std::monostate or any one of the three previous types
telemetry::Dict dictReturn() {
telemetry::Dict dict;

dict["1"] = telemetry::Scalar(static_cast<int64_t>(10));
dict["2"] = telemetry::ScalarWithUnit(static_cast<bool>(10), "bool");
dict["3"] = telemetry::Array(std::vector<telemetry::Scalar>());

return dict;
}

int main() {
// telemetry::Content can be easily printed or stored as string with contentToString()
std::cout << telemetry::contentToString(scalarReturn());
std::cout << telemetry::contentToString(scalartWithUnitReturn());
std::cout << telemetry::contentToString(arrayReturn());
std::cout << telemetry::contentToString(dictReturn());
}
57 changes: 57 additions & 0 deletions examples/holderExample.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* @file
* @author Daniel Pelanek <xpeland00@vutbr.cz>
* @brief Shows how to use the holder class
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include <telemetry.hpp>
#include <appFs.hpp>

// Object that collects it's own telemetry or owns
// telemetry files or directories.
class IOwnTelemetryFiles {
public:
void configTelemetry(const std::shared_ptr<telemetry::Directory>& rootDir) {
// Create a telemetry file
const telemetry::FileOps fileOps
= {nullptr, nullptr};

auto someFile = rootDir->addFile("someFile", fileOps);

// We also need to add the file to the holder.
m_telemetryHolder.add(someFile);
}

// If you want to disable callback of files before the
// is destroyed.
void disableFiles() {
m_telemetryHolder.disableFiles();
}

private:
telemetry::Holder m_telemetryHolder;
};

int main() {
// Same as basic example
std::shared_ptr<telemetry::Directory> telemetryRootNode;
telemetryRootNode = telemetry::Directory::create();

std::string fusePath = "fusedir";

telemetry::appFs::AppFsFuse fuse = telemetry::appFs::AppFsFuse(telemetryRootNode, fusePath, true, true);
fuse.start();

// The files callbacks get disabled when the telemetry holder
// is destroyed and if the files and dirs don't have another reference
// elsewhere they get destroyed too.
IOwnTelemetryFiles object;

// Configuration of telemetry inside of the object.
object.configTelemetry(telemetryRootNode);

// Disable callbacks before the objects destructor
object.disableFiles();
}
Loading

0 comments on commit b0ef6a1

Please sign in to comment.