RTXI  2.4
The Real-Time eXperiment Interface Documentation
settings.cpp
Go to the documentation of this file.
1 /*
2  The Real-Time eXperiment Interface (RTXI)
3  Copyright (C) 2011 Georgia Institute of Technology, University of Utah, Weill Cornell Medical College
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  */
19 
20 #include <QDomElement>
21 #include <QSettings>
22 #include <QFile>
23 #include <QTextStream>
24 #include <cstdlib>
25 #include <debug.h>
26 #include <errno.h>
27 #include <event.h>
28 #include <io.h>
29 #include <plugin.h>
30 #include <rt.h>
31 #include <settings.h>
32 #include <sstream>
33 #include <main_window.h>
34 #include <rtxi_config.h>
35 
36 namespace
37 {
38 struct defer_t
39 {
40  Settings::Object *object;
42 };
43 };
44 
46 {
47  Manager::getInstance()->insertObject(this);
48 }
49 
51 {
52  Manager::getInstance()->removeObject(this);
53 }
54 
56 
58 
60 
61 double Settings::Object::State::loadDouble(const std::string &name) const
62 {
63  double value = 0.0;
64  std::istringstream s;
65  std::map<std::string,std::string>::const_iterator n;
66 
67  n = paramMap.find(name);
68  if (n != paramMap.end())
69  {
70  s.str(n->second);
71  s >> value;
72  }
73 
74  return value;
75 }
76 
77 int Settings::Object::State::loadInteger(const std::string &name) const
78 {
79  int value = 0;
80  std::istringstream s;
81  std::map<std::string,std::string>::const_iterator n;
82 
83  n = paramMap.find(name);
84  if (n != paramMap.end())
85  {
86  s.str(n->second);
87  s >> value;
88  }
89 
90  return value;
91 }
92 
93 std::string Settings::Object::State::loadString(const std::string &name) const
94 {
95  std::string value = "";
96  std::map<std::string,std::string>::const_iterator n;
97 
98  n = paramMap.find(name);
99  if (n != paramMap.end())
100  value = n->second;
101 
102  return value;
103 }
104 
105 void Settings::Object::State::saveDouble(const std::string &name,double value)
106 {
107  std::ostringstream s;
108  s << value;
109  paramMap[name] = s.str();
110 }
111 
112 void Settings::Object::State::saveInteger(const std::string &name,int value)
113 {
114  std::ostringstream s;
115  s << value;
116  paramMap[name] = s.str();
117 }
118 
119 void Settings::Object::State::saveString(const std::string &name,const std::string &value)
120 {
121  paramMap[name] = value;
122 }
123 
125 {
126  State value;
127  std::map<std::string,State>::const_iterator n;
128 
129  n = stateMap.find(const_cast<std::string &>(name));
130  if (n != stateMap.end())
131  value = n->second;
132 
133  return value;
134 }
135 
136 void Settings::Object::State::saveState(const std::string &name,const Settings::Object::State &value)
137 {
138  stateMap[name] = value;
139 }
140 
141 QDomElement Settings::Object::State::xml(QDomDocument &doc) const
142 {
143  QDomElement e = doc.createElement("OBJECT");
144  e.setAttribute("id",QString::number(id));
145 
146  for (std::map<std::string,std::string>::const_iterator i = paramMap.begin(); i != paramMap.end(); ++i)
147  {
148  QDomElement e1 = doc.createElement("PARAM");
149  e1.setAttribute("name",QString::fromStdString(i->first));
150  e.appendChild(e1);
151 
152  QDomText t = doc.createTextNode(QString::fromStdString(i->second));
153  e1.appendChild(t);
154  }
155 
156  for (std::map<std::string,State>::const_iterator i = stateMap.begin(); i!= stateMap.end(); ++i)
157  {
158  QDomElement e1 = i->second.xml(doc);
159  e1.setAttribute("name",QString::fromStdString(i->first));
160  e.appendChild(e1);
161  }
162 
163  return e;
164 }
165 
166 void Settings::Object::State::xml(const QDomElement &e1)
167 {
168  paramMap.clear();
169  stateMap.clear();
170 
171  if (e1.tagName().toUpper() != "OBJECT")
172  {
173  ERROR_MSG("Settings::Object::State::xml : invalid element\n");
174  return;
175  }
176  id = e1.attribute("id","0").toULong();
177 
178  // Load XML map into memory
179  for(QDomElement e2 = e1.firstChild().toElement(); !e2.isNull(); e2 = e2.nextSibling().toElement())
180  {
181  if(e2.tagName().toUpper() == "PARAM" && e2.attribute("name") != QString::null)
182  paramMap[e2.attribute("name").toStdString()] = e2.text().toStdString();
183  else if(e2.tagName().toUpper() == "OBJECT" && e2.attribute("name") != QString::null)
184  stateMap[e2.attribute("name").toStdString()].xml(e2);
185  }
186 }
187 
189 {
190  State s(id);
191  doSave(s);
192  return s;
193 }
194 
196 {
197  if (id != s.id)
198  {
199  Mutex::Locker lock(&Manager::getInstance()->mutex);
200  Manager::getInstance()->releaseID(this);
201  Manager::getInstance()->acquireID(this,s.id);
202  }
203 
204  doLoad(s);
205 }
206 
208 {
209  doDeferred(s);
210 }
211 
213 {
214  Mutex::Locker lock(&mutex);
215 
216  std::map<Object::ID,Object *>::const_iterator i = objectMap.find(id);
217  if (i != objectMap.end())
218  return i->second;
219  else
220  return 0;
221 }
222 
223 void Settings::Manager::foreachObject(void (*callback)(Object *,void *),void *param)
224 {
225  Mutex::Locker lock(&mutex);
226  for (std::list<Object *>::iterator i = objectList.begin(),end = objectList.end(); i != end; ++i)
227  callback(*i,param);
228 }
229 
230 int Settings::Manager::load(const std::string &filename)
231 {
232  QFile file(QString::fromStdString(filename));
233  if (!file.open(QIODevice::ReadOnly))
234  {
235  ERROR_MSG("Settings::Manager::load : failed to open %s for reading\n", filename.c_str());
236  return -EPERM;
237  }
238 
239  QDomDocument doc;
240  QString errorMsg;
241  int errorLine, errorColumn;
242 
243  if (!doc.setContent(&file,false,&errorMsg,&errorLine,&errorColumn))
244  {
245  ERROR_MSG("Settings::Manager::load : %s:%d:%d: %s\n", filename.c_str(), errorLine, errorColumn, errorMsg.toStdString().c_str());
246  return -EINVAL;
247  }
248 
249  QDomElement e1 = doc.documentElement();
250 
251  if (e1.tagName() != "RTXI" || e1.attribute("class") != "settings")
252  {
253  ERROR_MSG("Settings::Manager::load : invalid document element\n");
254  return -EINVAL;
255  }
256 
257  // Return RTXI to a startup like state.
261 
262  // Reading in the period for the system
263  long long period = RT::System::getInstance()->getPeriod();
264  RT::System::getInstance()->setPeriod(1000000); // ns equivalent to 1ms (1kHz)
265 
266  Object::State s;
267  Plugin::Object *plugin;
268  std::list<defer_t> deferList;
269  for (QDomElement e2 = e1.firstChild().toElement(); !e2.isNull(); e2 = e2.nextSibling().toElement())
270  {
271  if (e2.tagName() != "OBJECT" || e2.attribute("component") == QString::null) continue;
272 
273  s.xml(e2);
274  // Load plugin info
275  if (e2.attribute("component") == "plugin")
276  {
277  if ((plugin = Plugin::Manager::getInstance()->load(e2.attribute("library"))))
278  {
279  defer_t defer = { plugin, s };
280  deferList.push_back(defer);
281  plugin->load(s);
282  }
283  }
284  // Load RT info
285  else if (e2.attribute("component") == "rt")
286  {
287  period = strtoll(s.loadString("Period").c_str(),0,10);
288  // Legacy case, period is stored as a double in scientific notation
289  if (period < 1000)
290  period = s.loadDouble("Period");
291  } // Load IO info
292  else if (e2.attribute("component") == "io")
293  {
294  defer_t defer = { IO::Connector::getInstance(), s };
295  deferList.push_back(defer);
296  }
297  }
298  for (std::list<defer_t>::iterator i = deferList.begin(),end = deferList.end(); i != end; ++i)
299  i->object->deferred(i->s);
300 
301  if (period)
303 
304  // create QSettings
305  QSettings userprefs;
306  userprefs.setPath(QSettings::NativeFormat, QSettings::SystemScope, "/usr/local/share/rtxi/");
307 
308  int oldestsetting = userprefs.value("/recentSettingsList/start").toInt();
309  int num_settings = userprefs.value("/recentSettingsList/num").toInt();
310 
311  userprefs.beginGroup("/recentSettingsList");
312  QStringList entries = userprefs.childKeys();
313  userprefs.endGroup();
314  int numRecentFiles = entries.size();
315  QString listsetting;
316  bool doesnotexist = true;
317 
318  for (int i = 0; i < numRecentFiles; ++i)
319  {
320  listsetting = userprefs.value("/recentSettingsList/" + entries[i]).toString();
321  if (QString::fromStdString(filename) == listsetting)
322  doesnotexist = false;
323  }
324 
325  if (filename == "/usr/local/share/rtxi/rtxi.conf")
326  doesnotexist = false;
327 
328  if (doesnotexist)
329  {
330  if (num_settings == 10)
331  {
332  userprefs.setValue("/recentSettingsList/" + QString::number(oldestsetting), QVariant(QString::fromStdString(filename)));
333  oldestsetting++;
334  if (oldestsetting == 10)
335  oldestsetting = 0;
336  userprefs.setValue("/recentSettingsList/start", oldestsetting);
337  }
338  else
339  {
340  userprefs.setValue("/recentSettingsList/" + QString::number(num_settings++), QVariant(QString::fromStdString(filename)));
341  userprefs.setValue("/recentSettingsList/num", num_settings);
342  }
343  }
344 
345  return 0;
346 }
347 
348 static void saveState(Plugin::Object *plugin,void *param)
349 {
350  QDomDocument *doc = reinterpret_cast<QDomDocument *>(param);
351  QDomElement e = plugin->save().xml(*doc);
352 
353  e.setAttribute("component","plugin");
354  e.setAttribute("library",QString::fromStdString(plugin->getLibrary()));
355  doc->documentElement().appendChild(e);
356 }
357 
358 int Settings::Manager::save(const std::string &filename)
359 {
360  QDomDocument doc;
361  QDomElement e;
362 
363  doc.appendChild(doc.createElement("RTXI"));
364  doc.documentElement().setAttribute("class","settings");
365  doc.documentElement().setAttribute("version",QString(VERSION));
366 
367  /*
368  * Save RT System period
369  */
370  Object::State s;
371  char buffer[256];
372  snprintf(buffer,256,"%lld",RT::System::getInstance()->getPeriod());
373  s.saveString("Period",buffer);
374  e = s.xml(doc);
375  e.setAttribute("component","rt");
376  doc.documentElement().appendChild(e);
377 
378  /*
379  * Save IO Connection information
380  */
381  e = IO::Connector::getInstance()->save().xml(doc);
382  e.setAttribute("component","io");
383  doc.documentElement().appendChild(e);
384 
385  /*
386  * Save Plugin Settings information
387  */
388  Plugin::Manager::getInstance()->foreachPlugin(saveState,&doc);
389 
390  QFile file(QString::fromStdString(filename));
391  if (!file.open(QIODevice::WriteOnly))
392  {
393  ERROR_MSG("Settings::Manager::save : failed to open %s for writing\n",filename.c_str());
394  return -EPERM;
395  }
396 
397  QTextStream stream(&file);
398  stream << doc;
399 
400  // create QSettings
401  QSettings userprefs;
402  userprefs.setPath (QSettings::NativeFormat, QSettings::SystemScope, "/usr/local/share/rtxi/");
403 
404  int oldestsetting = userprefs.value("/recentSettingsList/start").toInt();
405  int num_settings = userprefs.value("/recentSettingsList/num").toInt();
406 
407  userprefs.beginGroup("/recentSettingsList");
408  QStringList entries = userprefs.childKeys();
409  userprefs.endGroup();
410  int numRecentFiles = entries.size();
411  QString listsetting;
412  bool doesnotexist = true;
413 
414  for (int i = 0; i < numRecentFiles; ++i)
415  {
416  listsetting = userprefs.value("/recentSettingsList/" + entries[i]).toString();
417  if (QString::fromStdString(filename) == listsetting)
418  doesnotexist = false;
419  }
420 
421  if (filename == "/usr/local/share/rtxi/rtxi.conf")
422  doesnotexist = false;
423 
424  if (doesnotexist)
425  {
426  if (num_settings == 10)
427  {
428  userprefs.setValue("/recentSettingsList/" + QString::number(oldestsetting), QVariant(QString::fromStdString(filename)));
429  oldestsetting++;
430  if (oldestsetting == 11)
431  oldestsetting = 1;
432  userprefs.setValue("/recentSettingsList/start", oldestsetting);
433  }
434  else
435  {
436  userprefs.setValue("/recentSettingsList/" + QString::number(num_settings++), QVariant(QString::fromStdString(filename)));
437  userprefs.setValue("/recentSettingsList/num", num_settings);
438  }
439  }
440 
441  return 0;
442 }
443 
444 void Settings::Manager::acquireID(Settings::Object *object,Settings::Object::ID id)
445 {
446  Mutex::Locker lock(&mutex);
447 
448  if (id != Object::INVALID)
449  {
450  if (objectMap.find(id) == objectMap.end())
451  {
452  objectMap[id] = object;
453  object->id = id;
454  return;
455  }
456  //else
457  //DEBUG_MSG("Settings::Manager::acquireID : requested ID in use\n");
458  }
459 
460  /***********************************************************
461  * Traverse the ID space to find an available ID. *
462  * It ain't pretty, O(n^2) if my math is correct... *
463  ***********************************************************/
464 
465  while (objectMap.find(currentID) != objectMap.end() && currentID != Object::INVALID) ++currentID;
466 
467  /*****************************************************************************
468  * It is possible that there are no more available IDs, and in that case *
469  * the insertion fails and the object is assigned an invalid ID. *
470  * In practice this shouldn't ever happen, because on the x86 we have 2^32 *
471  * IDs, so we would probably run out of memory before hitting this limit *
472  *****************************************************************************/
473 
474  if (currentID == Object::INVALID)
475  {
476  ERROR_MSG("Settings::Manager::acquireID : maximum number of settings objects loaded.\n");
477  object->id = Object::INVALID;
478  }
479  else
480  {
481  object->id = currentID;
482  objectMap[currentID++] = object;
483  }
484 }
485 
486 void Settings::Manager::releaseID(Settings::Object *object)
487 {
488  Mutex::Locker lock(&mutex);
489 
490  if (object->id != Object::INVALID)
491  {
492  objectMap.erase(object->id);
493  if (object->id < currentID) currentID = object->id;
494  object->id = Object::INVALID;
495  }
496 }
497 
498 void Settings::Manager::insertObject(Settings::Object *object)
499 {
500  if (!object)
501  {
502  ERROR_MSG("Settings::Manager::insertObject : invalid object\n");
503  return;
504  }
505 
506  Mutex::Locker lock(&mutex);
507 
508  acquireID(object);
509  std::list<Object *>::iterator i;
510  for (i = objectList.begin(); i != objectList.end() && (*i)->id < object->id; ++i);
511 
513  event.setParam("object",object);
515 
516  objectList.insert(i,object);
517 }
518 
519 void Settings::Manager::removeObject(Settings::Object *object)
520 {
521  Mutex::Locker lock(&mutex);
522 
524  event.setParam("object",object);
526 
527  objectList.remove(object);
528  releaseID(object);
529 }
530 
531 static Mutex mutex;
532 Settings::Manager *Settings::Manager::instance = 0;
533 
535 {
536  if (instance)
537  return instance;
538 
539  /*************************************************************************
540  * Seems like alot of hoops to jump through, but static allocation isn't *
541  * thread-safe. So effort must be taken to ensure mutual exclusion. *
542  *************************************************************************/
543 
544  Mutex::Locker lock(&::mutex);
545  if (!instance)
546  {
547  static Manager manager;
548  instance = &manager;
549  }
550  return instance;
551 }
Event::Manager::postEvent
void postEvent(const Object *event)
Definition: event.cpp:110
Settings::Object::State::xml
QDomElement xml(QDomDocument &) const
Definition: settings.cpp:141
Event::SETTINGS_OBJECT_REMOVE_EVENT
const char * SETTINGS_OBJECT_REMOVE_EVENT
Definition: event.cpp:39
VERSION
#define VERSION
Definition: rtxi_config.h:77
ERROR_MSG
void ERROR_MSG(const std::string &errmsg,...)
Definition: debug.cpp:27
Settings::Manager::getInstance
static Manager * getInstance(void)
Definition: settings.cpp:534
settings.h
IO::Connector::getInstance
static Connector * getInstance(void)
Definition: io.cpp:421
Event::Manager::getInstance
static Manager * getInstance(void)
Definition: event.cpp:149
Plugin::Object
Definition: plugin.h:145
Settings::Object::Object
Object(void)
Definition: settings.cpp:45
Settings::Object::deferred
void deferred(const State &)
Definition: settings.cpp:207
Settings::Object::State::loadState
State loadState(const std::string &name) const
Definition: settings.cpp:124
plugin.h
Settings::Manager::getObject
Object * getObject(Object::ID) const
Definition: settings.cpp:212
Settings::Object::State::loadDouble
double loadDouble(const std::string &name) const
Definition: settings.cpp:61
Settings::Manager
Definition: settings.h:173
Settings::Object::State::~State
~State(void)
Definition: settings.cpp:59
Settings::Object::State::saveDouble
void saveDouble(const std::string &name, double)
Definition: settings.cpp:105
RT::System::getPeriod
long long getPeriod(void) const
Definition: rt.h:404
MainWindow::clearFileMenu
void clearFileMenu(void)
Definition: main_window.cpp:80
Settings::Object::State::loadString
std::string loadString(const std::string &name) const
Definition: settings.cpp:93
Settings::Object::save
State save(void) const
Definition: settings.cpp:188
RT::System::getInstance
static System * getInstance(void)
Definition: rt.cpp:361
Settings::Object::ID
unsigned long ID
Definition: settings.h:53
Mutex
Definition: mutex.h:28
Settings::Object::State::saveString
void saveString(const std::string &name, const std::string &value)
Definition: settings.cpp:119
Settings::Manager::foreachObject
void foreachObject(void(*callback)(Object *, void *), void *param)
Definition: settings.cpp:223
MainWindow::clearModuleMenu
void clearModuleMenu(void)
Definition: main_window.cpp:107
Settings::Object::INVALID
const static ID INVALID
Definition: settings.h:57
Settings::Object::doLoad
virtual void doLoad(const State &)
Definition: settings.h:154
rtxi_config.h
event.h
Settings::Object::State::loadInteger
int loadInteger(const std::string &name) const
Definition: settings.cpp:77
Event::SETTINGS_OBJECT_INSERT_EVENT
const char * SETTINGS_OBJECT_INSERT_EVENT
Definition: event.cpp:38
Settings::Object::~Object
virtual ~Object(void)
Definition: settings.cpp:50
rt.h
Settings::Manager::load
int load(const std::string &)
Definition: settings.cpp:230
Settings::Object
Definition: settings.h:43
Settings::Object::State
Definition: settings.h:62
Plugin::Manager::getInstance
static Manager * getInstance(void)
Definition: plugin.cpp:188
Settings::Object::State::saveInteger
void saveInteger(const std::string &name, int)
Definition: settings.cpp:112
Mutex::Locker
Definition: mutex.h:36
Plugin::Manager::foreachPlugin
void foreachPlugin(void(*callback)(Plugin::Object *, void *), void *param)
Definition: plugin.cpp:132
Settings::Manager::save
int save(const std::string &)
Definition: settings.cpp:358
Settings::Object::doDeferred
virtual void doDeferred(const State &)
Definition: settings.h:158
io.h
Settings::Object::State::State
State(void)
Definition: settings.cpp:55
MainWindow::getInstance
static MainWindow * getInstance(void)
Definition: main_window.cpp:454
Settings::Object::State::saveState
void saveState(const std::string &name, const State &value)
Definition: settings.cpp:136
RT::System::setPeriod
int setPeriod(long long period)
Definition: rt.cpp:188
Plugin::Manager::unloadAll
void unloadAll(void)
Definition: plugin.cpp:117
Event::Object
Definition: event.h:128
Settings::Object::doSave
virtual void doSave(State &) const
Definition: settings.h:162
Plugin::Object::getLibrary
std::string getLibrary(void) const
Definition: plugin.cpp:36
debug.h
Settings::Object::load
void load(const State &)
Definition: settings.cpp:195
main_window.h