forked from sccn/liblsl
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapi_config.cpp
More file actions
239 lines (216 loc) · 9.39 KB
/
api_config.cpp
File metadata and controls
239 lines (216 loc) · 9.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#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>
// === implementation of the api_config class ===
using namespace lsl;
using namespace lslboost::algorithm;
/// Helper function: Substitute the "~" character by the full home directory (according to environment variables).
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;
}
/// Helper function: Parse a set specifier (a string of the form {a, b, c, ...}) into a vector of strings.
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] == '}') {
// non-empty set: split by ","
std::string sub = setstr.substr(1,setstr.size()-2);
lslboost::algorithm::split(result,sub,lslboost::algorithm::is_any_of(","));
// remove leading and trailing whitespace from each element
for (std::vector<std::string>::iterator i=result.begin(); i!=result.end(); i++)
trim(*i);
}
return result;
}
// Returns true if the file exists and is openable for reading
bool file_is_readable(const std::string& filename) {
std::ifstream f(filename.c_str());
return f.good();
}
/**
* Constructor.
* Applies default settings and overrides them based on a config file (if present).
*/
api_config::api_config() {
// for each config file location under consideration...
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])) {
// try to load it if the file exists
load_from_file(filenames[k]);
// successful: finished
return;
}
} catch(std::exception &e) {
std::cerr << "Error trying to load config file " << filenames[k] << ": " << e.what() << std::endl;
}
}
// unsuccessful: load default settings
load_from_file();
}
/**
* Load a configuration file (or use defaults if a filename is empty).
* Expects a proper platform-native file name. Throws if there's an error.
*/
void api_config::load_from_file(const std::string &filename) {
try {
lslboost::property_tree::ptree pt;
if (!filename.empty())
read_ini(filename, pt);
// read out the [ports] parameters
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"); // on Mac OS (10.7) there's a bug in the IPv6 implementation that breaks LSL when it tries to use both v4 and v6
#else
"allow");
#endif
allow_ipv4_ = true;
allow_ipv6_ = true;
// fix some common mis-spellings
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.");
// read the [multicast] parameters
resolve_scope_ = pt.get("multicast.ResolveScope","site");
listen_address_ = pt.get("multicast.ListenAddress","");
// Note about multicast addresses: IPv6 multicast addresses should be
// FF0x::1 (see RFC2373, RFC1884) or a predefined multicast group
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}"));
// 224.0.0.1 is the group for all directly connected hosts (RFC1112)
std::vector<std::string> link_group = parse_set(pt.get("multicast.LinkAddresses","{255.255.255.255, 224.0.0.1, 224.0.0.183}"));
// Multicast groups defined by the organization (and therefore subject
// to filtering / forwarding are in the 239.192.0.0/14 subnet (RFC2365)
std::vector<std::string> site_group = parse_set(pt.get("multicast.SiteAddresses","{239.255.172.215}"));
// Organization groups use the same broadcast addresses (IPv4), but
// have a larger TTL. On the network site, it requires the routers
// to forward the broadcast packets (both IGMP and UDP)
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;
// construct list of addresses & TTL according to the ResolveScope.
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;
}
// apply overrides, if any
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;
// read the [lab] settings
known_peers_ = parse_set(pt.get("lab.KnownPeers","{}"));
session_id_ = pt.get("lab.SessionID","default");
// read the [tuning] settings
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;
// any error: assign defaults
load_from_file();
// and rethrow
throw e;
}
}
/**
* Instantiate / retrieve singleton.
*/
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;