How to stream ODS data
ods_config.cpp
/*
* Copyright 2021-present ifm electronic, gmbh
* SPDX-License-Identifier: Apache-2.0
*/
// This example showcases how to configure an application
// using the ifm3d API and provides utilities for verbose
// reporting of JSON syntax errors.
#include <cstdlib>
#include <iostream>
#include "ods_config.h"
#include <ifm3d/device/o3r.h>
#include <ifm3d/device/err.h>
int main()
{
///////////////////////////////////////////////////
// Variables needed for the example
///////////////////////////////////////////////////
std::string config_extrinsic_path = "../Configs/extrinsic_one_head.json";
std::string config_app_path = "../Configs/ods_one_head_config.json";
// Get the IP address from the environment variable
// If not defined, use the default IP
const char* IP = std::getenv("IFM3D_IP");
if (!IP) {
IP = ifm3d::DEFAULT_IP.c_str();
std::clog << "Using default IP" << std::endl;
}
std::clog << "IP: " << IP << std::endl;
// Declare the device object (one object only, corresponding to the VPU)
auto o3r = std::make_shared<ifm3d::O3R>(IP);
///////////////////////////////////////////////
// Examples of getting configuration snippets
///////////////////////////////////////////////
// Here we expect a fully booted system.
// Get the full configuration
std::clog << "Getting full config" << std::endl;
std::clog << std::setw(4) << o3r->Get() << std::endl;
// Get a subset of the configuration
std::clog << "Getting partial config" << std::endl;
std::clog << std::setw(4) << o3r->Get({"/device/swVersion/firmware"}) << std::endl;
// Get multiple subsets of the configuration
std::clog << "Getting multiple partial configs" << std::endl;
std::clog << std::setw(4) << o3r->Get({"/device/swVersion/firmware",
"/device/status",
"/ports/port0/info"}) << std::endl;
// Throw an exception if retrieving config in the wrong path
std::clog << "Getting config for wrong path" << std::endl;
try {
o3r->Get({"/device/wrongKey"});
}
catch (const ifm3d::Error& ex) {
std::clog << "Caught exception: " << ex.message() << std::endl;
}
std::clog << "Finished getting configurations" << std::endl;
///////////////////////////////////////////////
// Examples of setting configuration snippets
///////////////////////////////////////////////
// We use a custom class to provide configuration
// facilities and additional error handling.
// The user could directly use the ifm3d library
// native calls to set the configuration.
ODSConfig configurator(o3r);
configurator.SetConfigFromStr(R"(
{"device": { "info": { "description": "I will use this O3R to change the world"}}})");
// Set two configurations at the same time
configurator.SetConfigFromStr(R"(
{"device": {
"info": {
"name": "my_favorite_o3r"
}
},
"ports": {
"port0": {
"info": {
"name": "my_favorite_port"
}
}
}
}
)");
// We expect that this example is run from the Cpp/Examples/build folder.
// Update the path to the config files if using a different setup.
configurator.SetConfigFromFile(config_extrinsic_path);
configurator.SetConfigFromFile(config_app_path);
try {
configurator.SetConfigFromFile("/non/existent/file.json");
}
catch (...) {
// Failing silently to continue with the tutorial
}
std::clog << "You are done with the configuration tutorial!" << std::endl;
return 0;
}
ods_config.h
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <memory>
#include <nlohmann/json-schema.hpp>
#include <nlohmann/json.hpp> // nlohmann json needs to be included before any ifm3d include.
#define IFM3D_JSON_NLOHMANN_COMPAT // enable the nlohmann json converter
#include <ifm3d/device/json.hpp>
#include <ifm3d/device/o3r.h>
#include <ifm3d/device/err.h>
using namespace ifm3d::literals;
class ODSConfig {
public:
ifm3d::O3R::Ptr o3r;
ifm3d::json o3r_schema;
ODSConfig(ifm3d::O3R::Ptr o3r_) : o3r(o3r_) {
o3r_schema = o3r->GetSchema();
try {
validator.set_root_schema(nlohmann::json::parse(o3r_schema.dump(0)));
}
catch (const std::exception &e) {
std::cerr << "Validation of schema failed: "
<< e.what()
<< std::endl;
}
}
void ValidateJson(ifm3d::json config) {
// This validation function will provide verbose
// errors when the provided json configuration
// is wrong.
try {
validator.validate(nlohmann::json::parse(config.dump(0)));
}
catch (const std::exception &e) {
std::cerr << "Validation failed: "
<< e.what()
<< std::endl;
}
}
void SetConfigFromFile(const std::string& config_path) {
// Reading a configuration from file and setting it
std::clog << "Reading configuration file at: "
<< config_path
<< std::endl;
std::ifstream config_file;
config_file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
std::stringstream config_buffer;
try {
config_file.open(config_path);
if (config_file.is_open()) {
config_buffer << config_file.rdbuf();
}
SetConfigFromStr(config_buffer.str());
}
catch (const std::ifstream::failure& e) {
std::cerr << "Caught exception while reading configuration file: "
<< e.what()
<< std::endl;
}
catch (...) {
std::cerr << "Unknown error while reading configuration file."
<< std::endl;
}
}
void SetConfigFromStr(const std::string& config_str) {
try {
ValidateJson(ifm3d::json::parse(config_str));
o3r->Set(ifm3d::json::parse(config_str));
}
catch (const std::exception &e) {
std::cerr << "Caught exception while configuring: "
<< e.what()
<< std::endl;
}
}
private:
nlohmann::json_schema::json_validator validator{ nullptr, CheckJSONFormat };
static void CheckJSONFormat(const std::string& format, const std::string& value) {
// This is necessary because "format" is a keyword both used internally by the schema
// validator and in the O3R schema.
if (format == "ipv4") {
std::clog << "IPV4 formatting";
// if (!std::get<0>(ifm::eucco::network::normalizeIPv4Address(QString::fromStdString(value))))
// {
// throw std::invalid_argument("unknown ip format");
// }
}
else {
throw std::logic_error("Don't know how to validate " + format);
}
}
};