Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • bug/335/nettimeadjust
  • bug/feedrecord
  • bug/mlsdestruct2
  • bug/optflow-disparity
  • calibration
  • censuseval
  • cherry-pick-75b9f6b3
  • chromatest
  • enhancement/235
  • exp/327/colourcor
  • exp/candidatemask
  • exp/labcolours
  • exp/multires-sgm
  • exp/triangleperf-2
  • exp/triangleperf3
  • feature/134/usegroup-nb
  • feature/231/segment
  • feature/274/lossyfilter
  • feature/329/dualoptflow-doffs
  • feature/330/audiocompress
  • feature/375/fullres-fstream
  • feature/SKR
  • feature/aruco
  • feature/autocalibration
  • feature/ceres
  • feature/compiletime
  • feature/corr_smooth
  • feature/depth-touch
  • feature/disconflow
  • feature/fixedres
  • feature/gui2-nanogui-mitsuba
  • feature/gui2-pch
  • feature/multiplexer-pose
  • feature/poses
  • feature/python
  • feature/sdk-python
  • feature/sgm-experimental
  • feature/stereocalib
  • feature/use-new-frame
  • feature/use10bit
  • feature/vr
  • feature/warpcorr-costing
  • feature/web-service/camMover
  • feature/web-service/configurations
  • feature/web-service/vanillaClient
  • feature/websocket-pose
  • guirefactor
  • master
  • v0.0.1
  • v0.0.2
  • v0.0.3
  • v0.0.4
  • v0.0.5
  • v0.0.6
54 results

Target

Select target project
  • nicolaspope/ftl
1 result
Select Git revision
  • bug/335/nettimeadjust
  • bug/feedrecord
  • bug/mlsdestruct2
  • bug/optflow-disparity
  • calibration
  • censuseval
  • cherry-pick-75b9f6b3
  • chromatest
  • enhancement/235
  • exp/327/colourcor
  • exp/candidatemask
  • exp/labcolours
  • exp/multires-sgm
  • exp/triangleperf-2
  • exp/triangleperf3
  • feature/134/usegroup-nb
  • feature/231/segment
  • feature/274/lossyfilter
  • feature/329/dualoptflow-doffs
  • feature/330/audiocompress
  • feature/375/fullres-fstream
  • feature/SKR
  • feature/aruco
  • feature/autocalibration
  • feature/ceres
  • feature/compiletime
  • feature/corr_smooth
  • feature/depth-touch
  • feature/disconflow
  • feature/fixedres
  • feature/gui2-nanogui-mitsuba
  • feature/gui2-pch
  • feature/multiplexer-pose
  • feature/poses
  • feature/python
  • feature/sdk-python
  • feature/sgm-experimental
  • feature/stereocalib
  • feature/use-new-frame
  • feature/use10bit
  • feature/vr
  • feature/warpcorr-costing
  • feature/web-service/camMover
  • feature/web-service/configurations
  • feature/web-service/vanillaClient
  • feature/websocket-pose
  • guirefactor
  • master
  • v0.0.1
  • v0.0.2
  • v0.0.3
  • v0.0.4
  • v0.0.5
  • v0.0.6
54 results
Show changes
Commits on Source (2)
Showing
with 562 additions and 139 deletions
......@@ -46,10 +46,10 @@
using ftl::net::Universe;
using ftl::rgbd::Display;
using ftl::config;
using std::string;
using std::vector;
using ftl::rgbd::RGBDSource;
using ftl::config::json_t;
using json = nlohmann::json;
using std::this_thread::sleep_for;
......@@ -229,22 +229,22 @@ struct Cameras {
};
template <template<class> class Container>
std::map<string, Eigen::Matrix4f> runRegistration(ftl::net::Universe &net, Container<Cameras> &inputs) {
std::map<string, Eigen::Matrix4f> runRegistration(ftl::Configurable *root, ftl::net::Universe &net, Container<Cameras> &inputs) {
std::map<string, Eigen::Matrix4f> registration;
// NOTE: uses config["registration"]
if (!config["registration"].is_object()) {
if (!root->getConfig()["registration"].is_object()) {
LOG(FATAL) << "Configuration missing \"registration\" entry!";
return registration;
}
int iter = (int) config["registration"]["calibration"]["iterations"];
int delay = (int) config["registration"]["calibration"]["delay"];
float max_error = (int) config["registration"]["calibration"]["max_error"];
string ref_uri = (string) config["registration"]["reference-source"];
cv::Size pattern_size( (int) config["registration"]["calibration"]["patternsize"][0],
(int) config["registration"]["calibration"]["patternsize"][1]);
int iter = (int) root->getConfig()["registration"]["calibration"]["iterations"];
int delay = (int) root->getConfig()["registration"]["calibration"]["delay"];
float max_error = (int) root->getConfig()["registration"]["calibration"]["max_error"];
string ref_uri = (string) root->getConfig()["registration"]["reference-source"];
cv::Size pattern_size( (int) root->getConfig()["registration"]["calibration"]["patternsize"][0],
(int) root->getConfig()["registration"]["calibration"]["patternsize"][1]);
// config["registration"] done
......@@ -313,23 +313,25 @@ std::map<string, Eigen::Matrix4f> runRegistration(ftl::net::Universe &net, Conta
}
#endif
static void run() {
Universe net(config["net"]);
static void run(ftl::Configurable *root) {
Universe *net = ftl::create<Universe>(root, "net");
// Make sure connections are complete
sleep_for(milliseconds(500));
//sleep_for(milliseconds(500));
net->waitConnections();
if (!config["sources"].is_array()) {
std::vector<Cameras> inputs;
auto sources = root->get<vector<json_t>>("sources");
if (!sources) {
LOG(ERROR) << "No sources configured!";
return;
}
std::vector<Cameras> inputs;
//std::vector<Display> displays;
// TODO Allow for non-net source types
for (auto &src : config["sources"]) {
RGBDSource *in = ftl::rgbd::RGBDSource::create(src, &net); //new ftl::rgbd::NetSource(src, &net);
for (auto &src : *sources) {
RGBDSource *in = ftl::rgbd::RGBDSource::create(src, net); //new ftl::rgbd::NetSource(src, &net);
if (!in) {
LOG(ERROR) << "Unrecognised source: " << src;
} else {
......@@ -355,8 +357,8 @@ static void run() {
// load point cloud transformations
std::map<string, Eigen::Matrix4f> registration;
if (config["registration"]["calibration"]["run"]) {
registration = runRegistration(net, inputs);
if (root->getConfig()["registration"]["calibration"]["run"]) {
registration = runRegistration(root, *net, inputs);
}
else {
LOG(INFO) << "LOAD REG";
......@@ -367,7 +369,7 @@ static void run() {
// (registration includes every camera)
bool valid_registration = true;
string ref_input = config["registration"]["reference-source"];
string ref_input = root->getConfig()["registration"]["reference-source"];
// check every camera is included in registration
for (auto &input : inputs) {
......@@ -417,28 +419,28 @@ static void run() {
for (auto &input : inputs) { LOG(INFO) << " " + (string) input.source->getConfig()["uri"]; }
//vector<PointCloud<PointXYZRGB>::Ptr> clouds(inputs.size());
ftl::rgbd::Display display(config["display"]); // todo
ftl::rgbd::VirtualSource *virt = new ftl::rgbd::VirtualSource(config["virtual"], &net);
ftl::voxhash::SceneRep scene(config["voxelhash"]);
virt->setScene(&scene);
display.setSource(virt);
ftl::rgbd::Display *display = ftl::create<ftl::rgbd::Display>(root, "display");
ftl::rgbd::VirtualSource *virt = ftl::create<ftl::rgbd::VirtualSource>(root, "virtual", net);
ftl::voxhash::SceneRep *scene = ftl::create<ftl::voxhash::SceneRep>(root, "voxelhash");
virt->setScene(scene);
display->setSource(virt);
unsigned char frameCount = 0;
bool paused = false;
// Keyboard camera controls
display.onKey([&paused](int key) {
display->onKey([&paused](int key) {
if (key == 32) paused = !paused;
});
int active = inputs.size();
while (active > 0 && display.active()) {
while (active > 0 && display->active()) {
active = 0;
if (!paused) {
//net.broadcast("grab"); // To sync cameras
scene.nextFrame();
scene->nextFrame();
for (size_t i = 0; i < inputs.size(); i++) {
// Get the RGB-Depth frame from input
......@@ -460,7 +462,7 @@ static void run() {
// Send to GPU and merge view into scene
inputs[i].gpu.updateParams(inputs[i].params);
inputs[i].gpu.updateData(depth, rgba);
scene.integrate(inputs[i].source->getPose(), inputs[i].gpu, inputs[i].params, nullptr);
scene->integrate(inputs[i].source->getPose(), inputs[i].gpu, inputs[i].params, nullptr);
}
} else {
active = 1;
......@@ -468,11 +470,10 @@ static void run() {
frameCount++;
display.update();
display->update();
}
}
int main(int argc, char **argv) {
ftl::configure(argc, argv, "reconstruction");
run();
run(ftl::configure(argc, argv, "reconstruction_default"));
}
......@@ -50,7 +50,6 @@ using std::mutex;
using std::unique_lock;
using cv::Mat;
using json = nlohmann::json;
using ftl::config;
namespace ftl {
void disparityToDepth(const cv::Mat &disparity, cv::Mat &depth, const cv::Mat &q) {
......@@ -78,44 +77,51 @@ void disparityToDepth(const cv::Mat &disparity, cv::Mat &depth, const cv::Mat &q
};
static void run(const string &file) {
ctpl::thread_pool pool(2);
Universe net(config["net"]);
static void run(ftl::Configurable *root) {
Universe *net = ftl::create<Universe>(root, "net");
LOG(INFO) << "Net started.";
auto paths = root->get<vector<string>>("paths");
string file = "";
if (paths && (*paths).size() > 0) file = (*paths)[0];
StereoVideoSource *source = nullptr;
source = new StereoVideoSource(config, file);
source = ftl::create<StereoVideoSource>(root, "source", file);
Display display(config["display"], "local");
Display *display = ftl::create<Display>(root, "display", "local");
Streamer stream(config["stream"], &net);
stream.add(source);
stream.run();
Streamer *stream = ftl::create<Streamer>(root, "stream", net);
stream->add(source);
stream->run();
while (display.active()) {
while (display->active()) {
cv::Mat rgb, depth;
source->getRGBD(rgb, depth);
if (!rgb.empty()) display.render(rgb, depth, source->getParameters());
display.wait(10);
if (!rgb.empty()) display->render(rgb, depth, source->getParameters());
display->wait(10);
}
stream.stop();
stream->stop();
LOG(INFO) << "Finished.";
delete stream;
delete display;
delete source;
delete net;
}
int main(int argc, char **argv) {
std::cout << "FTL Vision Node " << FTL_VERSION_LONG << std::endl;
auto paths = ftl::configure(argc, argv, "vision");
auto root = ftl::configure(argc, argv, "vision_default");
config["paths"] = paths;
//config["ftl://vision/default"]["paths"] = paths;
// Choose normal or middlebury modes
if (config["middlebury"]["dataset"] == "") {
//if (config["middlebury"]["dataset"] == "") {
std::cout << "Loading..." << std::endl;
run((paths.size() > 0) ? paths[0] : "");
} else {
ftl::middlebury::test(config);
}
run(root);
//} else {
// ftl::middlebury::test(config);
//}
}
set(COMMONSRC
src/config.cpp
src/uri.cpp
src/configuration.cpp
src/configurable.cpp
src/opencv_to_pcl.cpp
)
check_function_exists(uriParseSingleUriA HAVE_URIPARSESINGLE)
add_library(ftlcommon ${COMMONSRC})
target_compile_options(ftlcommon PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-fPIC>)
......@@ -14,7 +17,7 @@ target_include_directories(ftlcommon PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE src)
target_link_libraries(ftlcommon glog::glog ${OpenCV_LIBS} ${PCL_LIBRARIES})
target_link_libraries(ftlcommon glog::glog ${OpenCV_LIBS} ${PCL_LIBRARIES} ${URIPARSER_LIBRARIES})
add_subdirectory(test)
......@@ -27,27 +27,17 @@ namespace ftl {
*/
class Configurable {
public:
Configurable() {}
Configurable();
explicit Configurable(nlohmann::json &config) : config_(config) {
if (config["uri"].is_string()) __changeURI(config["uri"].get<std::string>(), this);
}
virtual ~Configurable() {}
/**
* Force the JSON object to have specific properties with a specific type.
* If not, emit errors and terminate the application.
*/
void required(const char *f, const std::vector<std::tuple<std::string, std::string, std::string>> &r) {
bool diderror = false;
for (auto i : r) {
auto [name, desc, type] = i;
if (config_[name].type_name() != type) {
LOG(ERROR) << "Missing required option in \"" << f << "\": \"" << name << "\" - " << desc;
LOG(ERROR) << " Got type " << config_[name].type_name() << " but expected " << type;
diderror = true;
}
}
if (diderror) LOG(FATAL) << "Cannot continue without required option";
}
void required(const char *f, const std::vector<std::tuple<std::string, std::string, std::string>> &r);
nlohmann::json &getConfig() { return config_; }
......@@ -57,12 +47,15 @@ class Configurable {
* the requested type.
*/
template <typename T>
std::optional<T> get(const std::string &name) {
try {
return config_[name].get<T>();
} catch (...) {
return {};
}
std::optional<T> get(const std::string &name);
/**
* Get a configuration property, but return a default if not found.
*/
template <typename T>
T value(const std::string &name, T def) {
auto r = get<T>(name);
return (r) ? *r : def;
}
/**
......@@ -102,4 +95,33 @@ void Configurable::set<const std::string&>(const std::string &name, const std::s
}
#include <ftl/configuration.hpp>
template <typename T>
std::optional<T> ftl::Configurable::get(const std::string &name) {
if (!config_[name].is_null()) {
try {
return config_[name].get<T>();
} catch (...) {
return {};
}
} else if (config_["$ref"].is_string()) {
// TODO(Nick) Add # if missing
// TODO(Nick) Cache result of ref loopkup
std::string res_uri = config_["$ref"].get<std::string>()+"/"+name;
auto &r = ftl::config::resolve(res_uri);
LOG(INFO) << "GET Resolve: " << res_uri << " = " << r;
try {
return r.get<T>();
} catch (...) {
LOG(ERROR) << "Missing: " << config_["$id"].get<std::string>()+"/"+name;
return {};
}
} else {
return {};
}
}
#endif // _FTL_CONFIGURABLE_HPP_
#pragma once
#ifndef _FTL_COMMON_CONFIGURATION_HPP_
#define _FTL_COMMON_CONFIGURATION_HPP_
#include <nlohmann/json.hpp>
//#include <ftl/configurable.hpp>
#include <string>
#include <vector>
#include <optional>
namespace ftl {
extern nlohmann::json config;
class Configurable;
bool is_directory(const std::string &path);
bool is_file(const std::string &path);
bool create_directory(const std::string &path);
bool is_video(const std::string &file);
namespace config {
typedef nlohmann::json json_t;
std::optional<std::string> locateFile(const std::string &name);
std::vector<std::string> configure(int argc, char **argv, const std::string &app);
Configurable *configure(int argc, char **argv, const std::string &root);
/**
* Resolve a JSON schema reference, but do not wait for a remote reference
* if it is not available. A null entity is returned if not resolved.
*/
json_t &resolve(const std::string &);
/**
* Resolve a reference object, or if not a reference object it simply returns
* the original object. A reference object with additional properties other
* than $ref will result in a new merged object.
*/
json_t &resolve(json_t &ref);
/**
* Resolve a JSON schema reference and block until such a reference can be
* resolved correctly.
*/
json_t &resolveWait(const std::string &);
/**
* Using a JSON schema reference, find an existing instance of a Configurable
* object for that reference. Or return nullptr if not found.
*/
Configurable *find(const std::string &uri);
/**
* Adds a Configurable instance to the database of instances so that it can
* then be resolved using find().
*/
void registerConfigurable(Configurable *cfg);
template <typename T, typename... ARGS>
T *create(json_t &link, ARGS ...args);
template <typename T, typename... ARGS>
T *create(ftl::Configurable *parent, const std::string &name, ARGS ...args);
void set(const std::string &uri, const nlohmann::json &);
} // namespace config
// Deprecated
using config::create;
using config::locateFile;
using config::configure;
} // namespace ftl
#include <ftl/configurable.hpp>
template <typename T, typename... ARGS>
T *ftl::config::create(json_t &link, ARGS ...args) {
auto &r = link; // = ftl::config::resolve(link);
if (!r["$id"].is_string()) {
LOG(FATAL) << "Entity does not have $id or parent: " << r;
return nullptr;
}
ftl::Configurable *cfg = ftl::config::find(r["$id"].get<std::string>());
if (!cfg) {
// try {
cfg = new T(r, args...);
//} catch(...) {
// LOG(FATAL) << "Could not construct " << link;
//}
}
try {
return dynamic_cast<T*>(cfg);
} catch(...) {
LOG(FATAL) << "Configuration URI object is of wrong type: " << link;
return nullptr;
}
}
template <typename T, typename... ARGS>
T *ftl::config::create(ftl::Configurable *parent, const std::string &name, ARGS ...args) {
nlohmann::json &entity = ftl::config::resolve(parent->getConfig()[name]);
if (entity.is_object()) {
if (!entity["$id"].is_string()) {
// TODO(Nick) Check for # in URI
entity["$id"] = *parent->get<std::string>("$id") + std::string("/") + name;
}
} /*else {
nlohmann::json &res = resolve(entity);
if (!res["uri"].is_string()) {
res["uri"] = *parent->get<std::string>("uri") + std::string("/") + name;
LOG(WARNING) << "Creating false URI!!! - " << res["uri"].get<std::string>();
}
}*/
};
return create<T>(entity, args...);
}
#endif // _FTL_COMMON_CONFIGURATION_HPP_
......@@ -39,9 +39,17 @@ namespace ftl {
scheme_t getProtocol() const { return m_proto; };
scheme_t getScheme() const { return m_proto; };
const std::string &getPath() const { return m_path; };
const std::string &getFragment() const { return m_frag; }
std::string getQuery() const;
const std::string &getBaseURI() const { return m_base; };
const std::string &getPathSegment(int n) const { return m_pathseg[n]; };
/**
* Get the URI without query parameters, and limit path to length N.
* If N is negative then it is taken from full path length.
*/
std::string getBaseURI(int n);
std::string getPathSegment(int n) const;
void setAttribute(const std::string &key, const std::string &value);
void setAttribute(const std::string &key, int value);
......@@ -60,10 +68,12 @@ namespace ftl {
bool m_valid;
std::string m_host;
std::string m_path;
std::string m_frag;
std::string m_base;
std::vector<std::string> m_pathseg;
int m_port;
scheme_t m_proto;
std::string m_protostr;
// std::string m_query;
std::map<std::string, std::string> m_qmap;
};
......
......@@ -3977,12 +3977,24 @@ scan_number_done:
return token_type::parse_error;
}
 
bool incomment = false;
// read next character and ignore whitespace
do
{
get();
// Nick: Add support for inline comments
if (!incomment && current == '/') {
get();
if (current != '/') {
error_message = "missing / for inline comment";
return token_type::parse_error;
}
incomment = true;
} else if (incomment && (current == '\n' || current == '\r')) incomment = false;
}
while (current == ' ' or current == '\t' or current == '\n' or current == '\r');
while (incomment or current == ' ' or current == '\t' or current == '\n' or current == '\r');
 
switch (current)
{
......
......@@ -5,6 +5,25 @@ using std::string;
using std::map;
using std::list;
using std::function;
using ftl::config::json_t;
extern nlohmann::json null_json;
Configurable::Configurable() : config_(null_json) {}
void Configurable::required(const char *f, const std::vector<std::tuple<std::string, std::string, std::string>> &r) {
bool diderror = false;
for (auto i : r) {
auto [name, desc, type] = i;
auto ent = get<json_t>(name);
if (!ent || (*ent).type_name() != type) {
LOG(ERROR) << "Missing required option in \"" << f << "\": \"" << name << "\" - " << desc;
//LOG(ERROR) << " Got type " << (*ent).type_name() << " but expected " << type;
diderror = true;
}
}
if (diderror) LOG(FATAL) << "Cannot continue without required option";
}
void Configurable::_trigger(const string &name) {
auto ix = observers_.find(name);
......
......@@ -11,13 +11,15 @@
#include <nlohmann/json.hpp>
#include <ftl/configuration.hpp>
#include <ftl/configurable.hpp>
#include <ftl/uri.hpp>
#include <fstream>
#include <string>
#include <map>
#include <iostream>
using nlohmann::json;
using ftl::config::json_t;
using std::ifstream;
using std::string;
using std::map;
......@@ -25,13 +27,20 @@ using std::vector;
using std::optional;
using ftl::is_file;
using ftl::is_directory;
using ftl::Configurable;
// Store loaded configuration
namespace ftl {
json config;
namespace config {
json_t config;
//json_t root_config;
};
};
using ftl::config::config;
//using ftl::config::root_config;
using ftl::config;
static Configurable *rootCFG = nullptr;
bool ftl::is_directory(const std::string &path) {
#ifdef WIN32
......@@ -85,11 +94,14 @@ bool ftl::create_directory(const std::string &path) {
#endif
}
optional<string> ftl::locateFile(const string &name) {
auto paths = config["paths"];
optional<string> ftl::config::locateFile(const string &name) {
if (is_file(name)) return name;
if (!paths.is_null()) {
for (string p : paths) {
auto paths = rootCFG->getConfig()["paths"];
if (paths.is_array()) {
vector<string> vpaths = paths.get<vector<string>>();
for (string p : vpaths) {
if (is_directory(p)) {
if (is_file(p+"/"+name)) {
return p+"/"+name;
......@@ -116,11 +128,11 @@ static bool mergeConfig(const string path) {
//i.open(path);
if (i.is_open()) {
try {
json t;
nlohmann::json t;
i >> t;
config.merge_patch(t);
return true;
} catch (json::parse_error& e) {
} catch (nlohmann::json::parse_error& e) {
LOG(ERROR) << "Parse error in loading config: " << e.what();
return false;
} catch (...) {
......@@ -131,11 +143,128 @@ static bool mergeConfig(const string path) {
}
}
static std::map<std::string, json_t*> config_index;
static std::map<std::string, ftl::Configurable*> config_instance;
/*
* Recursively URI index the JSON structure.
*/
static void _indexConfig(json_t &cfg) {
if (cfg.is_object()) {
auto id = cfg["$id"];
if (id.is_string()) {
LOG(INFO) << "Indexing: " << id.get<string>();
config_index[id.get<string>()] = &cfg;
}
for (auto i : cfg.items()) {
if (i.value().is_structured()) {
_indexConfig(cfg[i.key()]);
}
}
} // TODO(Nick) Arrays....
}
ftl::Configurable *ftl::config::find(const std::string &uri) {
auto ix = config_instance.find(uri);
if (ix == config_instance.end()) return nullptr;
else return (*ix).second;
}
void ftl::config::registerConfigurable(ftl::Configurable *cfg) {
auto uri = cfg->get<string>("$id");
if (!uri) {
LOG(FATAL) << "Configurable object is missing $id property";
return;
}
auto ix = config_instance.find(*uri);
if (ix == config_instance.end()) {
LOG(FATAL) << "Attempting to create a duplicate object: " << *uri;
} else {
config_instance[*uri] = cfg;
}
}
json_t null_json;
json_t &ftl::config::resolve(const std::string &puri) {
string uri_str = puri;
// TODO(Nick) Must support alternative root (last $id)
if (uri_str.at(0) == '#') {
uri_str = config["$id"].get<string>() + uri_str;
}
ftl::URI uri(uri_str);
if (uri.isValid()) {
std::string u = uri.getBaseURI();
LOG(INFO) << "Resolve URI: " << u;
auto ix = config_index.find(u);
if (ix == config_index.end()) {
LOG(FATAL) << "Cannot find resource: " << u;
}
auto ptr = nlohmann::json::json_pointer("/"+uri.getFragment());
try {
return resolve((*ix).second->at(ptr));
} catch(...) {
return null_json;
}
//}
/*
int n = 0;
while (u.size() != 0) {
LOG(INFO) << "Resolve URI: " << u;
auto ix = config_index.find(u);
if (ix == config_index.end()) {
u = uri.getBaseURI(--n);
continue;
}
//LOG(INFO) << "FOUND URI " << *(*ix).second;
if (n == 0) {
return *(*ix).second;
} else {
// Must apply path segments again...
nlohmann::json *c = (*ix).second;
while (n < 0) {
auto seg = uri.getPathSegment(n++);
c = &(*c)[seg];
// Recursive resolve...
if ((*c).is_string()) {
c = &resolve(*c);
}
}
// Give it the correct URI
if (!(*c)["$id"].is_string()) {
(*c)["$id"] = uri.getBaseURI();
}
return *c;
}
}
LOG(FATAL) << "Unresolvable configuration URI: " << uri.getBaseURI();
return null_json;*/
} else {
return null_json;
}
}
json_t &ftl::config::resolve(json_t &j) {
if (j.is_object() && j["$ref"].is_string()) {
return resolve(j["$ref"].get<string>());
} else {
return j;
}
}
/**
* Find and load a JSON configuration file
*/
static bool findConfiguration(const string &file, const vector<string> &paths,
const std::string &app) {
static bool findConfiguration(const string &file, const vector<string> &paths) {
bool f = false;
bool found = false;
......@@ -169,7 +298,7 @@ static bool findConfiguration(const string &file, const vector<string> &paths,
}
if (found) {
config = config[app];
_indexConfig(config);
return true;
} else {
return false;
......@@ -214,9 +343,10 @@ static map<string, string> read_options(char ***argv, int *argc) {
* Put command line options into json config. If config element does not exist
* or is of a different type then report an error.
*/
static void process_options(const map<string, string> &opts) {
static void process_options(Configurable *root, const map<string, string> &opts) {
for (auto opt : opts) {
if (opt.first == "config") continue;
if (opt.first == "root") continue;
if (opt.first == "version") {
std::cout << "Future-Tech Lab - v" << FTL_VERSION << std::endl;
......@@ -225,21 +355,22 @@ static void process_options(const map<string, string> &opts) {
}
try {
auto ptr = json::json_pointer("/"+opt.first);
auto ptr = nlohmann::json::json_pointer("/"+opt.first);
// TODO(nick) Allow strings without quotes
auto v = json::parse(opt.second);
if (v.type() != config.at(ptr).type()) {
LOG(ERROR) << "Incorrect type for argument " << opt.first;
auto v = nlohmann::json::parse(opt.second);
std::string type = root->getConfig().at(ptr).type_name();
if (type != "null" && v.type_name() != type) {
LOG(ERROR) << "Incorrect type for argument " << opt.first << " - expected '" << type << "', got '" << v.type_name() << "'";
continue;
}
config.at(ptr) = v;
root->getConfig().at(ptr) = v;
} catch(...) {
LOG(ERROR) << "Unrecognised option: " << opt.first;
LOG(ERROR) << "Unrecognised option: " << *root->get<string>("$id") << "#" << opt.first;
}
}
}
vector<string> ftl::configure(int argc, char **argv, const std::string &app) {
Configurable *ftl::config::configure(int argc, char **argv, const std::string &root) {
argc--;
argv++;
......@@ -251,11 +382,22 @@ vector<string> ftl::configure(int argc, char **argv, const std::string &app) {
paths.push_back(argv[0]);
}
if (!findConfiguration(options["config"], paths, app)) {
if (!findConfiguration(options["config"], paths)) {
LOG(FATAL) << "Could not find any configuration!";
}
process_options(options);
return paths;
string root_str = (options.find("root") != options.end()) ? nlohmann::json::parse(options["root"]).get<string>() : root;
Configurable *rootcfg = create<Configurable>(config);
if (root_str.size() > 0) {
LOG(INFO) << "Setting root to " << root_str;
rootcfg = create<Configurable>(rootcfg, root_str);
}
//root_config = rootcfg->getConfig();
rootCFG = rootcfg;
rootcfg->set("paths", paths);
process_options(rootcfg, options);
return rootcfg;
}
......@@ -21,6 +21,7 @@ URI::URI(const URI &c) {
m_pathseg = c.m_pathseg;
m_qmap = c.m_qmap;
m_base = c.m_base;
m_frag = c.m_frag;
}
void URI::_parse(uri_t puri) {
......@@ -39,6 +40,7 @@ void URI::_parse(uri_t puri) {
m_port = -1;
m_proto = SCHEME_NONE;
m_path = "";
m_frag = "";
} else {
m_host = std::string(uri.hostText.first, uri.hostText.afterLast - uri.hostText.first);
......@@ -50,6 +52,7 @@ void URI::_parse(uri_t puri) {
else if (prototext == "ws") m_proto = SCHEME_WS;
else if (prototext == "ipc") m_proto = SCHEME_IPC;
else m_proto = SCHEME_OTHER;
m_protostr = prototext;
std::string porttext = std::string(uri.portText.first, uri.portText.afterLast - uri.portText.first);
m_port = atoi(porttext.c_str());
......@@ -83,10 +86,16 @@ void URI::_parse(uri_t puri) {
uriFreeUriMembersA(&uri);
auto fraglast = (uri.query.first != NULL) ? uri.query.first : uri.fragment.afterLast;
if (uri.fragment.first != NULL && fraglast - uri.fragment.first > 0) {
m_frag = std::string(uri.fragment.first, fraglast - uri.fragment.first);
}
m_valid = m_proto != SCHEME_NONE && m_host.size() > 0;
if (m_valid) {
if (m_qmap.size() > 0) m_base = std::string(uri.scheme.first, uri.query.first - uri.scheme.first - 1);
else if (uri.fragment.first != NULL) m_base = std::string(uri.scheme.first, uri.fragment.first - uri.scheme.first - 1);
else m_base = std::string(uri.scheme.first);
}
}
......@@ -96,6 +105,34 @@ string URI::to_string() const {
return (m_qmap.size() > 0) ? m_base + "?" + getQuery() : m_base;
}
string URI::getPathSegment(int n) const {
int N = (n < 0) ? m_pathseg.size()+n : n;
if (N < 0 || N >= m_pathseg.size()) return "";
else return m_pathseg[N];
}
string URI::getBaseURI(int n) {
if (n >= (int)m_pathseg.size()) return m_base;
if (n >= 0) {
string r = m_protostr + string("://") + m_host + ((m_port != 0) ? string(":") + std::to_string(m_port) : "");
for (int i=0; i<n; i++) {
r += "/";
r += getPathSegment(i);
}
return r;
} else if (m_pathseg.size()+n >= 0) {
string r = m_protostr + string("://") + m_host + ((m_port != 0) ? string(":") + std::to_string(m_port) : "");
int N = m_pathseg.size()+n;
for (int i=0; i<N; i++) {
r += "/";
r += getPathSegment(i);
}
return r;
} else return "";
}
string URI::getQuery() const {
string q;
for (auto x : m_qmap) {
......
......@@ -2,6 +2,9 @@
add_executable(configurable_unit
./tests.cpp
../src/configurable.cpp
../src/uri.cpp
../src/config.cpp
../src/configuration.cpp
./configurable_unit.cpp
)
target_include_directories(configurable_unit PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include")
......@@ -9,8 +12,18 @@ target_link_libraries(configurable_unit
${URIPARSER_LIBRARIES}
glog::glog)
### URI ########################################################################
add_executable(uri_unit
./tests.cpp
../src/uri.cpp
./uri_unit.cpp)
target_include_directories(uri_unit PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include")
target_link_libraries(uri_unit
${URIPARSER_LIBRARIES})
add_test(ConfigurableUnitTest configurable_unit)
add_test(URIUnitTest uri_unit)
......@@ -25,6 +25,53 @@ SCENARIO( "URI() can parse valid URIs", "[utility]" ) {
REQUIRE( uri.getPath() == "/test/case.html" );
}
GIVEN( "a valid fragment" ) {
URI uri("http://localhost:8080/test/case.html#frag");
REQUIRE( uri.isValid() );
REQUIRE( uri.getScheme() == URI::SCHEME_HTTP );
REQUIRE( uri.getHost() == "localhost" );
REQUIRE( (uri.getPort() == 8080) );
REQUIRE( uri.getPath() == "/test/case.html" );
REQUIRE( uri.getFragment() == "frag");
}
GIVEN( "a multipart valid fragment" ) {
URI uri("http://localhost:8080/test/case.html#frag/second");
REQUIRE( uri.isValid() );
REQUIRE( uri.getScheme() == URI::SCHEME_HTTP );
REQUIRE( uri.getHost() == "localhost" );
REQUIRE( (uri.getPort() == 8080) );
REQUIRE( uri.getPath() == "/test/case.html" );
REQUIRE( uri.getFragment() == "frag/second");
REQUIRE( uri.getBaseURI() == "http://localhost:8080/test/case.html");
}
GIVEN( "an empty fragment" ) {
URI uri("http://localhost:8080/test/case.html#");
REQUIRE( uri.isValid() );
REQUIRE( uri.getScheme() == URI::SCHEME_HTTP );
REQUIRE( uri.getHost() == "localhost" );
REQUIRE( (uri.getPort() == 8080) );
REQUIRE( uri.getPath() == "/test/case.html" );
REQUIRE( uri.getFragment() == "");
REQUIRE( uri.getBaseURI() == "http://localhost:8080/test/case.html");
}
/*GIVEN( "a valid fragment with query" ) {
URI uri("http://localhost:8080/test/case.html#frag?q=4");
REQUIRE( uri.isValid() );
REQUIRE( uri.getScheme() == URI::SCHEME_HTTP );
REQUIRE( uri.getHost() == "localhost" );
REQUIRE( (uri.getPort() == 8080) );
REQUIRE( uri.getPath() == "/test/case.html" );
REQUIRE( uri.getQuery() == "q=4" );
REQUIRE( uri.getFragment() == "frag");
}*/
GIVEN( "a valid scheme with path and query" ) {
URI uri("ftl://utu.fi/test/case.html?v=1");
......@@ -89,3 +136,25 @@ SCENARIO( "URI::getAttribute() from query" ) {
}
}
SCENARIO( "URI::getBaseURI(N)" ) {
GIVEN( "an N of 0" ) {
URI uri("http://localhost:1000/hello/world");
REQUIRE( uri.getBaseURI(0) == "http://localhost:1000" );
}
GIVEN( "an N of -1" ) {
URI uri("http://localhost:1000/hello/world");
REQUIRE( uri.getBaseURI(-1) == "http://localhost:1000/hello" );
}
GIVEN( "an N of 1" ) {
URI uri("http://localhost:1000/hello/world");
REQUIRE( uri.getBaseURI(1) == "http://localhost:1000/hello" );
}
GIVEN( "an N of 2" ) {
URI uri("http://localhost:1000/hello/world");
REQUIRE( uri.getBaseURI(2) == "http://localhost:1000/hello/world" );
}
}
......@@ -4,7 +4,6 @@
add_library(ftlnet
src/uri.cpp
src/listener.cpp
src/peer.cpp
src/dispatcher.cpp
......@@ -12,13 +11,11 @@ add_library(ftlnet
src/ws_internal.cpp
)
check_function_exists(uriParseSingleUriA HAVE_URIPARSESINGLE)
target_include_directories(ftlnet PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE src)
target_link_libraries(ftlnet ftlcommon Threads::Threads glog::glog ${UUID_LIBRARIES} ${URIPARSER_LIBRARIES})
target_link_libraries(ftlnet ftlcommon Threads::Threads glog::glog ${UUID_LIBRARIES})
install(TARGETS ftlnet EXPORT ftlnet-config
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
......@@ -27,7 +24,7 @@ install(TARGETS ftlnet EXPORT ftlnet-config
install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
add_executable(net-cli src/main.cpp)
target_link_libraries(net-cli ftlnet ftlcommon glog::glog ${URIPARSER_LIBRARIES} Threads::Threads ${READLINE_LIBRARY} ${UUID_LIBRARIES})
target_link_libraries(net-cli ftlnet ftlcommon glog::glog Threads::Threads ${READLINE_LIBRARY} ${UUID_LIBRARIES})
add_dependencies(net-cli ftlnet)
ADD_SUBDIRECTORY(test)
......@@ -21,6 +21,7 @@ using ftl::UUID;
using std::optional;
using std::unique_lock;
using std::mutex;
using ftl::config::json_t;
Universe::Universe() : Configurable(), active_(true), this_peer(ftl::net::this_peer), thread_(Universe::__start, this) {
_installBindings();
......@@ -28,17 +29,21 @@ Universe::Universe() : Configurable(), active_(true), this_peer(ftl::net::this_p
Universe::Universe(nlohmann::json &config) :
Configurable(config), active_(true), this_peer(ftl::net::this_peer), thread_(Universe::__start, this) {
if (config["listen"].is_array()) {
for (auto &l : config["listen"]) {
listen(l);
auto l = get<json_t>("listen");
if (l && (*l).is_array()) {
for (auto &ll : *l) {
listen(ll);
}
} else if (config["listen"].is_string()) {
listen(config["listen"]);
} else if (l && (*l).is_string()) {
listen((*l).get<string>());
}
if (config["peers"].is_array()) {
for (auto &p : config["peers"]) {
connect(p);
auto p = get<json_t>("peers");
if (p && (*p).is_array()) {
for (auto &pp : *p) {
connect(pp);
}
}
......
......@@ -3,26 +3,16 @@ add_executable(peer_unit
./tests.cpp
../src/ws_internal.cpp
../src/dispatcher.cpp
../src/uri.cpp
./peer_unit.cpp
../../../common/cpp/src/config.cpp
)
target_include_directories(peer_unit PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include" "${CMAKE_CURRENT_SOURCE_DIR}/../../../common/cpp/include")
target_link_libraries(peer_unit
${URIPARSER_LIBRARIES}
ftlcommon
glog::glog
Threads::Threads
${UUID_LIBRARIES})
### URI ########################################################################
add_executable(uri_unit
./tests.cpp
../src/uri.cpp
./uri_unit.cpp)
target_include_directories(uri_unit PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include")
target_link_libraries(uri_unit
${URIPARSER_LIBRARIES})
### P2P Base Unit ##############################################################
# TODO(nick) Actually make this a unit test
......@@ -45,16 +35,13 @@ add_dependencies(net_integration ftlnet)
target_include_directories(net_integration PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}../include")
target_link_libraries(net_integration
ftlnet
${URIPARSER_LIBRARIES}
ftlcommon
glog::glog
Threads::Threads
${UUID_LIBRARIES})
add_test(URIUnitTest uri_unit)
#add_test(ProtocolUnitTest protocol_unit)
add_test(PeerUnitTest peer_unit)
add_test(NetIntegrationTest net_integration)
......
......@@ -6,6 +6,7 @@
#define _FTL_DISPLAY_HPP_
#include <ftl/config.h>
#include <ftl/configurable.hpp>
#include "../../../rgbd-sources/include/ftl/camera_params.hpp"
#include <nlohmann/json.hpp>
......@@ -22,7 +23,7 @@ namespace ftl {
/**
* Multiple local display options for disparity or point clouds.
*/
class Display {
class Display : public ftl::Configurable {
private:
std::string name_;
public:
......@@ -48,8 +49,6 @@ class Display {
void onKey(std::function<void(int)> h) { key_handlers_.push_back(h); }
private:
nlohmann::json config_;
#if defined HAVE_VIZ
cv::viz::Viz3d *window_;
#endif // HAVE_VIZ
......
......@@ -11,7 +11,7 @@ using ftl::Display;
using cv::Mat;
using cv::Vec3f;
Display::Display(nlohmann::json &config, std::string name) : config_(config) {
Display::Display(nlohmann::json &config, std::string name) : ftl::Configurable(config) {
name_ = name;
#if defined HAVE_VIZ
window_ = new cv::viz::Viz3d("FTL: " + name);
......
......@@ -29,7 +29,7 @@ class RGBDSource : public ftl::Configurable {
void getRGBD(cv::Mat &rgb, cv::Mat &depth);
const CameraParameters &getParameters() { return params_; };
std::string getURI() const { return config_["uri"].get<std::string>(); }
std::string getURI() const { return config_["$id"].get<std::string>(); }
virtual void setPose(const Eigen::Matrix4f &pose) { pose_ = pose; };
const Eigen::Matrix4f &getPose() { return pose_; };
......
......@@ -9,6 +9,7 @@
#include <opencv2/opencv.hpp>
#include <elas.h>
#include <ftl/disparity.hpp>
#include <ftl/configuration.hpp>
namespace ftl {
namespace algorithms {
......@@ -24,8 +25,8 @@ class ELAS : public ftl::Disparity {
void compute(const cv::Mat &l, const cv::Mat &r, cv::Mat &disp);
/* Factory creator */
static inline Disparity *create(nlohmann::json &config) {
return new ELAS(config);
static inline Disparity *create(ftl::Configurable *p, const std::string &name) {
return ftl::create<ELAS>(p, name);
}
private:
......
......@@ -10,6 +10,7 @@
#include <libsgm.h>
#include "../disparity.hpp"
#include <opencv2/cudastereo.hpp>
#include <ftl/configuration.hpp>
namespace ftl {
namespace algorithms {
......@@ -29,8 +30,8 @@ class FixstarsSGM : public ftl::Disparity {
void compute(const cv::Mat &l, const cv::Mat &r, cv::Mat &disp, const cv::Mat &mask_l) override;
/* Factory creator */
static inline Disparity *create(nlohmann::json &config) {
return new FixstarsSGM(config);
static inline Disparity *create(ftl::Configurable *p, const std::string &name) {
return ftl::create<FixstarsSGM>(p, name);
}
private:
......