#include "api_config.h"
#include "common.h"
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/thread/once.hpp>
using namespace lsl;
using namespace lslboost::algorithm;
std::string expand_tilde(const std::string &filename) {
if (!filename.empty() && filename[0] == '~') {
std::string homedir;
if (getenv("HOME"))
homedir = getenv("HOME");
else if (getenv("USERPROFILE"))
homedir = getenv("USERPROFILE");
else if (getenv("HOMEDRIVE") && getenv("HOMEPATH"))
homedir = std::string(getenv("HOMEDRIVE")) + getenv("HOMEPATH");
else {
std::cerr << "Cannot determine the user's home directory; config files in the home directory will not be discovered." << std::endl;
return filename;
}
return homedir + filename.substr(1);
}
return filename;
}
static std::vector<std::string> parse_set(const std::string &setstr) {
std::vector<std::string> result;
if ((setstr.size() > 2) && setstr[0] == '{' && setstr[setstr.size()-1] == '}') {
std::string sub = setstr.substr(1,setstr.size()-2);
lslboost::algorithm::split(result,sub,lslboost::algorithm::is_any_of(","));
for (std::vector<std::string>::iterator i=result.begin(); i!=result.end(); i++)
trim(*i);
}
return result;
}
bool file_is_readable(const std::string& filename) {
std::ifstream f(filename.c_str());
return f.good();
}
api_config::api_config() {
std::vector<std::string> filenames;
if(getenv("LSLAPICFG")) {
std::string envcfg(getenv("LSLAPICFG"));
if (!file_is_readable(envcfg))
std::cerr << "LSLAPICFG file " << envcfg << " not found" << std::endl;
else
filenames.insert(filenames.begin(), envcfg);
}
filenames.push_back("lsl_api.cfg");
filenames.push_back(expand_tilde("~/lsl_api/lsl_api.cfg"));
filenames.push_back("/etc/lsl_api/lsl_api.cfg");
for (std::size_t k=0; k < filenames.size(); k++) {
try {
if (file_is_readable(filenames[k])) {
load_from_file(filenames[k]);
return;
}
} catch(std::exception &e) {
std::cerr << "Error trying to load config file " << filenames[k] << ": " << e.what() << std::endl;
}
}
load_from_file();
}
void api_config::load_from_file(const std::string &filename) {
try {
lslboost::property_tree::ptree pt;
if (!filename.empty())
read_ini(filename, pt);
multicast_port_ = pt.get("ports.MulticastPort",16571);
base_port_ = pt.get("ports.BasePort",16572);
port_range_ = pt.get("ports.PortRange",32);
allow_random_ports_ = pt.get("ports.AllowRandomPorts",true);
std::string ipv6_str = pt.get("ports.IPv6",
#ifdef __APPLE__
"disable"); #else
"allow");
#endif
allow_ipv4_ = true;
allow_ipv6_ = true;
if (ipv6_str == "disabled" || ipv6_str == "disable")
allow_ipv6_ = false;
else if (ipv6_str == "allowed" || ipv6_str == "allow")
allow_ipv6_ = true;
else if (ipv6_str == "forced" || ipv6_str == "force")
allow_ipv4_ = false;
else
throw std::runtime_error("Unsupported setting for the IPv6 parameter.");
resolve_scope_ = pt.get("multicast.ResolveScope","site");
listen_address_ = pt.get("multicast.ListenAddress","");
std::string ipv6_multicast_group = pt.get("multicast.IPv6MulticastGroup", "113D:6FDD:2C17:A643:FFE2:1BD1:3CD2");
std::vector<std::string> machine_group = parse_set(pt.get("multicast.MachineAddresses","{127.0.0.1}"));
std::vector<std::string> link_group = parse_set(pt.get("multicast.LinkAddresses","{255.255.255.255, 224.0.0.1, 224.0.0.183}"));
std::vector<std::string> site_group = parse_set(pt.get("multicast.SiteAddresses","{239.255.172.215}"));
std::vector<std::string> organization_group = parse_set(pt.get("multicast.OrganizationAddresses","{}"));
std::vector<std::string> global_group = parse_set(pt.get("multicast.GlobalAddresses","{}"));
enum {
machine = 0,
link,
site,
organization,
global
} scope;
if (resolve_scope_ == "machine") scope = machine;
else if(resolve_scope_ == "link") scope = link;
else if(resolve_scope_ == "site") scope = site;
else if(resolve_scope_ == "organization") scope = organization;
else if(resolve_scope_ == "global") scope = global;
else throw std::runtime_error("This ResolveScope setting is unsupported.");
multicast_addresses_.insert(multicast_addresses_.end(), machine_group.begin(), machine_group.end());
multicast_ttl_ = 0;
if(scope >= link) {
multicast_addresses_.insert(multicast_addresses_.end(),link_group.begin(),link_group.end());
multicast_addresses_.push_back("FF02:" + ipv6_multicast_group);
multicast_ttl_ = 1;
}
if(scope >= site) {
multicast_addresses_.insert(multicast_addresses_.end(),site_group.begin(),site_group.end());
multicast_addresses_.push_back("FF05:" + ipv6_multicast_group);
multicast_ttl_ = 24;
}
if(scope >= organization) {
multicast_addresses_.insert(multicast_addresses_.end(),organization_group.begin(),organization_group.end());
multicast_addresses_.push_back("FF08:" + ipv6_multicast_group);
multicast_ttl_ = 32;
}
if(scope >= global) {
multicast_addresses_.insert(multicast_addresses_.end(),global_group.begin(),global_group.end());
multicast_addresses_.push_back("FF0E:" + ipv6_multicast_group);
multicast_ttl_ = 255;
}
int ttl_override = pt.get("multicast.TTLOverride",-1);
std::vector<std::string> address_override = parse_set(pt.get("multicast.AddressesOverride","{}"));
if (ttl_override >= 0)
multicast_ttl_ = ttl_override;
if (!address_override.empty())
multicast_addresses_ = address_override;
known_peers_ = parse_set(pt.get("lab.KnownPeers","{}"));
session_id_ = pt.get("lab.SessionID","default");
use_protocol_version_ = std::min(LSL_PROTOCOL_VERSION,pt.get("tuning.UseProtocolVersion",LSL_PROTOCOL_VERSION));
watchdog_check_interval_ = pt.get("tuning.WatchdogCheckInterval",15.0);
watchdog_time_threshold_ = pt.get("tuning.WatchdogTimeThreshold",15.0);
multicast_min_rtt_ = pt.get("tuning.MulticastMinRTT",0.5);
multicast_max_rtt_ = pt.get("tuning.MulticastMaxRTT",3.0);
unicast_min_rtt_ = pt.get("tuning.UnicastMinRTT",0.75);
unicast_max_rtt_ = pt.get("tuning.UnicastMaxRTT",5.0);
continuous_resolve_interval_ = pt.get("tuning.ContinuousResolveInterval",0.5);
timer_resolution_ = pt.get("tuning.TimerResolution",1);
max_cached_queries_ = pt.get("tuning.MaxCachedQueries",100);
time_update_interval_ = pt.get("tuning.TimeUpdateInterval",2.0);
time_update_minprobes_ = pt.get("tuning.TimeUpdateMinProbes",6);
time_probe_count_ = pt.get("tuning.TimeProbeCount",8);
time_probe_interval_ = pt.get("tuning.TimeProbeInterval",0.064);
time_probe_max_rtt_ = pt.get("tuning.TimeProbeMaxRTT",0.128);
outlet_buffer_reserve_ms_ = pt.get("tuning.OutletBufferReserveMs",5000);
outlet_buffer_reserve_samples_ = pt.get("tuning.OutletBufferReserveSamples",128);
inlet_buffer_reserve_ms_ = pt.get("tuning.InletBufferReserveMs",5000);
inlet_buffer_reserve_samples_ = pt.get("tuning.InletBufferReserveSamples",128);
smoothing_halftime_ = pt.get("tuning.SmoothingHalftime",90.0f);
force_default_timestamps_ = pt.get("tuning.ForceDefaultTimestamps", false);
} catch(std::exception &e) {
std::cerr << "Error parsing config file " << filename << " (" << e.what() << "). Rolling back to defaults." << std::endl;
load_from_file();
throw e;
}
}
const api_config *api_config::get_instance() {
lslboost::call_once(&called_once,once_flag);
return get_instance_internal();
}
api_config *api_config::get_instance_internal() {
static api_config cfg;
return &cfg;
}
void api_config::called_once() { get_instance_internal(); }
lslboost::once_flag api_config::once_flag = BOOST_ONCE_INIT;