1 // ONELAB - Copyright (C) 2011-2021 Universite de Liege - Universite catholique 2 // de Louvain 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, and/or sell copies of the 8 // Software, and to permit persons to whom the Software is furnished to do so, 9 // provided that the above copyright notice(s) and this permission notice appear 10 // in all copies of the Software and that both the above copyright notice(s) and 11 // this permission notice appear in supporting documentation. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY 16 // RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS 17 // NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL 18 // DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 19 // PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 20 // ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 21 // SOFTWARE. 22 23 #ifndef ONELAB_H 24 #define ONELAB_H 25 26 #include <time.h> 27 #include <stdio.h> 28 #include <string> 29 #include <vector> 30 #include <set> 31 #include <map> 32 #include <iostream> 33 #include <algorithm> 34 #include <sstream> 35 #include <mutex> 36 #include <regex> 37 38 #include "GmshSocket.h" 39 40 //#define HAVE_PICOJSON 41 #if defined(HAVE_PICOJSON) 42 #include "picojson.h" 43 #endif 44 45 namespace onelab { 46 47 // The base parameter class. 48 class parameter { 49 private: 50 // the name of the parameter, including its '/'-separated path in the 51 // parameter hierarchy. Subpaths can start with white space, numbers or 52 // braces (in that order) to force their relative ordering; getShortName() 53 // can be used to remove these prefixes automatically. All strings in ONELAB 54 // are supposed to be UTF8-encoded. 55 std::string _name; 56 // the parameter label: if provided it serves as a better way to display the 57 // parameter in the interface 58 std::string _label; 59 // a help string 60 std::string _help; 61 // map of clients that use this parameter, associated with a "changed" flag 62 // (set to 0 if the client has already been run with the current value of 63 // the parameter; set to defaultChangedValue() when the value of a parameter 64 // has been changed; values between 1 and defaultChangedValue() can be used 65 // to "modulate" the degree of change, e.g. to change the action to be 66 // performed depending on the "severity" of the change) 67 std::map<std::string, int> _clients; 68 // flag indicating what the "changed" value should be set to when a 69 // parameter is updated to a different value (if set to 0, the parameter 70 // will appear as never being changed) 71 int _changedValue; 72 // should the parameter be visible in the interface? 73 bool _visible; 74 // sould the paramete be "read-only" (not settable by the user) 75 bool _readOnly; 76 77 protected: 78 // optional additional attributes 79 std::map<std::string, std::string> _attributes; 80 81 public: 82 parameter(const std::string &name = "", const std::string &label = "", 83 const std::string &help = "") _name(name)84 : _name(name), _label(label), _help(help), _visible(true), 85 _readOnly(false) 86 { 87 _changedValue = defaultChangedValue(); 88 } ~parameter()89 virtual ~parameter() {} setName(const std::string & name)90 void setName(const std::string &name) { _name = name; } setLabel(const std::string & label)91 void setLabel(const std::string &label) { _label = label; } setHelp(const std::string & help)92 void setHelp(const std::string &help) { _help = help; } 93 void setChanged(int changed, const std::string &client = "") 94 { 95 if(client.size()) { 96 auto it = _clients.find(client); 97 if(it != _clients.end()) it->second = changed; 98 } 99 else { 100 for(auto it = _clients.begin(); 101 it != _clients.end(); it++) 102 it->second = changed; 103 } 104 } setChangedValue(int value)105 void setChangedValue(int value) { _changedValue = value; } setNeverChanged(bool never)106 void setNeverChanged(bool never) 107 { 108 _changedValue = never ? 0 : defaultChangedValue(); 109 } setVisible(bool visible)110 void setVisible(bool visible) { _visible = visible; } setReadOnly(bool readOnly)111 void setReadOnly(bool readOnly) { _readOnly = readOnly; } setAttribute(const std::string & key,const std::string & value)112 void setAttribute(const std::string &key, const std::string &value) 113 { 114 _attributes[key] = value; 115 } setAttributes(const std::map<std::string,std::string> & attributes)116 void setAttributes(const std::map<std::string, std::string> &attributes) 117 { 118 _attributes = attributes; 119 } setClients(const std::map<std::string,int> & clients)120 void setClients(const std::map<std::string, int> &clients) 121 { 122 _clients = clients; 123 } addClient(const std::string & client,int changed)124 void addClient(const std::string &client, int changed) 125 { 126 if(_clients.find(client) == _clients.end()) _clients[client] = changed; 127 } addClients(const std::map<std::string,int> & clients)128 void addClients(const std::map<std::string, int> &clients) 129 { 130 _clients.insert(clients.begin(), clients.end()); 131 } hasClient(const std::string & client)132 bool hasClient(const std::string &client) 133 { 134 return (_clients.find(client) != _clients.end()); 135 } getNumClients()136 int getNumClients() { return (int)_clients.size(); }; 137 virtual std::string getType() const = 0; getName()138 const std::string &getName() const { return _name; } getLabel()139 const std::string &getLabel() const { return _label; } getHelp()140 const std::string &getHelp() const { return _help; } getPath()141 std::string getPath() const 142 { 143 std::string::size_type last = _name.find_last_of('/'); 144 return _name.substr(0, last); 145 } getShortName()146 std::string getShortName() const 147 { 148 std::string units = getAttribute("Units"); 149 if(_label.size()) { 150 if(units.empty()) 151 return _label; 152 else 153 return _label + " [" + units + "]"; 154 } 155 std::string s = _name; 156 // remove path 157 std::string::size_type last = _name.find_last_of('/'); 158 if(last != std::string::npos) s = _name.substr(last + 1); 159 // remove starting white space 160 while(s.size() && s[0] == ' ') s = s.substr(1); 161 // remove starting braces: can be used to order parameters 'from the end', 162 // as the ASCII code is after numbers and letters 163 while(s.size() && (s[0] == '}' || s[0] == '{')) s = s.substr(1); 164 // remove starting numbers: can be used to order parameters 'from the 165 // start' 166 while(s.size() && s[0] >= '0' && s[0] <= '9') s = s.substr(1); 167 if(units.empty()) 168 return s; 169 else 170 return s + " [" + units + "]"; 171 } 172 int getChanged(const std::string &client = "") const 173 { 174 if(client.size()) { 175 auto it = _clients.find(client); 176 if(it != _clients.end()) 177 return it->second; 178 else 179 return 0; 180 } 181 else { 182 int changed = 0; 183 for(auto it = _clients.begin(); 184 it != _clients.end(); it++) { 185 changed = std::max(changed, it->second); 186 } 187 return changed; 188 } 189 } getChangedValue()190 int getChangedValue() const { return _changedValue; } getNeverChanged()191 bool getNeverChanged() const { return _changedValue ? false : true; } getVisible()192 bool getVisible() const { return _visible; } getReadOnly()193 bool getReadOnly() const { return _readOnly; } getAttribute(const std::string & key)194 std::string getAttribute(const std::string &key) const 195 { 196 auto it = 197 _attributes.find(key); 198 if(it != _attributes.end()) return it->second; 199 return ""; 200 } getAttributes()201 const std::map<std::string, std::string> &getAttributes() const 202 { 203 return _attributes; 204 } getClients()205 const std::map<std::string, int> &getClients() const { return _clients; } charSep()206 static char charSep() { return '\0'; } maxNumber()207 static double maxNumber() { return 1e200; } version()208 static std::string version() { return "1.3"; } defaultChangedValue()209 static int defaultChangedValue() { return 31; } 210 static std::string getNextToken(const std::string &msg, 211 std::string::size_type &first, 212 char separator = charSep()) 213 { 214 if(first == std::string::npos) return ""; 215 std::string::size_type last = msg.find_first_of(separator, first); 216 std::string next(""); 217 if(last == std::string::npos) { 218 next = msg.substr(first); 219 first = last; 220 } 221 else if(first == last) { 222 next = ""; 223 first = last + 1; 224 } 225 else { 226 next = msg.substr(first, last - first); 227 first = last + 1; 228 } 229 return next; 230 } 231 static std::vector<std::string> split(const std::string &msg, 232 char separator = charSep()) 233 { 234 std::vector<std::string> out; 235 std::string::size_type first = 0; 236 while(first != std::string::npos) 237 out.push_back(getNextToken(msg, first, separator)); 238 return out; 239 } 240 static std::string trim(const std::string &str, 241 const std::string &whitespace = " \t\n") 242 { 243 std::string::size_type strBegin = str.find_first_not_of(whitespace); 244 if(strBegin == std::string::npos) return ""; // no content 245 std::string::size_type strEnd = str.find_last_not_of(whitespace); 246 std::string::size_type strRange = strEnd - strBegin + 1; 247 return str.substr(strBegin, strRange); 248 } sanitize(const std::string & in)249 std::string sanitize(const std::string &in) const 250 { 251 std::string out(in); 252 for(std::size_t i = 0; i < in.size(); i++) 253 if(out[i] == charSep()) out[i] = ' '; 254 return out; 255 } toChar()256 virtual std::string toChar() const 257 { 258 std::ostringstream sstream; 259 sstream << version() << charSep() << getType() << charSep() 260 << sanitize(getName()) << charSep() << sanitize(getLabel()) 261 << charSep() << sanitize(getHelp()) << charSep() 262 << getChangedValue() << charSep() << (getVisible() ? 1 : 0) 263 << charSep() << (getReadOnly() ? 1 : 0) << charSep() 264 << _attributes.size() << charSep(); 265 for(auto it = 266 _attributes.begin(); 267 it != _attributes.end(); it++) 268 sstream << sanitize(it->first) << charSep() << sanitize(it->second) 269 << charSep(); 270 sstream << getClients().size() << charSep(); 271 for(auto it = getClients().begin(); 272 it != getClients().end(); it++) 273 sstream << sanitize(it->first) << charSep() << (it->second ? 1 : 0) 274 << charSep(); 275 return sstream.str(); 276 } fromChar(const std::string & msg)277 virtual std::string::size_type fromChar(const std::string &msg) 278 { 279 std::string::size_type pos = 0; 280 if(getNextToken(msg, pos) != version()) return 0; 281 if(getNextToken(msg, pos) != getType()) return 0; 282 setName(getNextToken(msg, pos)); 283 setLabel(getNextToken(msg, pos)); 284 setHelp(getNextToken(msg, pos)); 285 setChangedValue(atoi(getNextToken(msg, pos).c_str())); 286 setVisible(atoi(getNextToken(msg, pos).c_str())); 287 setReadOnly(atoi(getNextToken(msg, pos).c_str())); 288 int numAttributes = atoi(getNextToken(msg, pos).c_str()); 289 for(int i = 0; i < numAttributes; i++) { 290 std::string key(getNextToken(msg, pos)); 291 setAttribute(key, getNextToken(msg, pos)); 292 } 293 int numClients = atoi(getNextToken(msg, pos).c_str()); 294 for(int i = 0; i < numClients; i++) { 295 std::string client(getNextToken(msg, pos)); 296 int changed = atoi(getNextToken(msg, pos).c_str()); 297 addClient(client, changed); 298 } 299 return pos; 300 } getInfoFromChar(const std::string & msg,std::string & version,std::string & type,std::string & name)301 static void getInfoFromChar(const std::string &msg, std::string &version, 302 std::string &type, std::string &name) 303 { 304 std::string::size_type first = 0; 305 version = getNextToken(msg, first); 306 type = getNextToken(msg, first); 307 name = getNextToken(msg, first); 308 } fromFile(std::vector<std::string> & msg,FILE * fp)309 static bool fromFile(std::vector<std::string> &msg, FILE *fp) 310 { 311 msg.clear(); 312 char tmp[1000]; 313 if(!fgets(tmp, sizeof(tmp), fp)) return false; // first line is comment 314 while(!feof(fp)) { 315 int numc = 0; 316 if(!fscanf(fp, "%d ", &numc)) break; // space is important 317 if(!numc) break; 318 msg.push_back(""); 319 for(int i = 0; i < numc; i++) msg.back() += fgetc(fp); 320 if(!fgets(tmp, sizeof(tmp), fp)) break; // end of line 321 } 322 return true; 323 } toFile(const std::vector<std::string> & msg,FILE * fp,const std::string & creator)324 static bool toFile(const std::vector<std::string> &msg, FILE *fp, 325 const std::string &creator) 326 { 327 time_t now; 328 time(&now); 329 fprintf(fp, "ONELAB database created by %s on %s", creator.c_str(), 330 ctime(&now)); 331 for(std::size_t i = 0; i < msg.size(); i++) { 332 fprintf(fp, "%d ", (int)msg[i].size()); 333 for(std::size_t j = 0; j < msg[i].size(); j++) fputc(msg[i][j], fp); 334 fputc('\n', fp); 335 } 336 return true; 337 } sanitizeJSON(const std::string & in)338 std::string sanitizeJSON(const std::string &in) const 339 { 340 // FIXME: replace \n with \\n, \t with \\t, etc. 341 return in; 342 } toJSON()343 virtual std::string toJSON() const 344 { 345 std::ostringstream sstream; 346 sstream << "\"type\":\"" << getType() << "\"" 347 << ", \"name\":\"" << sanitizeJSON(getName()) << "\""; 348 if(getLabel().size()) 349 sstream << ", \"label\":\"" << sanitizeJSON(getLabel()) << "\""; 350 if(getHelp().size()) 351 sstream << ", \"help\":\"" << sanitizeJSON(getHelp()) << "\""; 352 sstream << ", \"changedValue\":" << getChangedValue() 353 << ", \"visible\":" << (getVisible() ? "true" : "false") 354 << ", \"readOnly\":" << (getReadOnly() ? "true" : "false"); 355 if(_attributes.size()) { 356 sstream << ", \"attributes\":{ "; 357 for(auto it = 358 _attributes.begin(); 359 it != _attributes.end(); it++) { 360 if(it != _attributes.begin()) sstream << ", "; 361 sstream << "\"" << sanitizeJSON(it->first) << "\":\"" 362 << sanitizeJSON(it->second) << "\""; 363 } 364 sstream << " }"; 365 } 366 if(getClients().size()) { 367 sstream << ", \"clients\":{ "; 368 for(auto it = 369 getClients().begin(); 370 it != getClients().end(); it++) { 371 if(it != getClients().begin()) sstream << ", "; 372 sstream << "\"" << sanitizeJSON(it->first) << "\":" << it->second; 373 } 374 sstream << " }"; 375 } 376 return sstream.str(); 377 } 378 #if defined(HAVE_PICOJSON) fromJSON(const picojson::value::object & par)379 virtual bool fromJSON(const picojson::value::object &par) 380 { 381 for(auto it = par.begin(); 382 it != par.end(); ++it) { 383 if(it->first == "name") { 384 if(!it->second.is<std::string>()) return false; 385 setName(it->second.get<std::string>()); 386 } 387 else if(it->first == "label") { 388 if(!it->second.is<std::string>()) return false; 389 setLabel(it->second.get<std::string>()); 390 } 391 else if(it->first == "help") { 392 if(!it->second.is<std::string>()) return false; 393 setHelp(it->second.get<std::string>()); 394 } 395 else if(it->first == "changedValue") { 396 if(!it->second.is<double>()) return false; 397 setChangedValue((int)it->second.get<double>()); 398 } 399 else if(it->first == "visible") { 400 if(!it->second.is<bool>()) return false; 401 setVisible(it->second.get<bool>()); 402 } 403 else if(it->first == "readOnly") { 404 if(!it->second.is<bool>()) return false; 405 setReadOnly(it->second.get<bool>()); 406 } 407 else if(it->first == "attributes") { 408 if(!it->second.is<picojson::object>()) return false; 409 const picojson::value::object &obj = 410 it->second.get<picojson::object>(); 411 for(auto i = obj.begin(); 412 i != obj.end(); ++i) { 413 std::string key(i->first); 414 if(!i->second.is<std::string>()) return false; 415 setAttribute(key, i->second.get<std::string>()); 416 } 417 } 418 else if(it->first == "clients") { 419 if(!it->second.is<picojson::object>()) return false; 420 const picojson::value::object &obj = 421 it->second.get<picojson::object>(); 422 for(auto i = obj.begin(); 423 i != obj.end(); ++i) { 424 std::string client(i->first); 425 if(!i->second.is<double>()) return false; 426 addClient(client, (int)i->second.get<double>()); 427 } 428 } 429 } 430 return true; 431 } 432 #endif 433 }; 434 435 class parameterLessThan { 436 public: operator()437 bool operator()(const parameter *p1, const parameter *p2) const 438 { 439 return p1->getName() < p2->getName(); 440 } 441 }; 442 443 // The number class. Numbers are stored internally as double precision real 444 // numbers. 445 class number : public parameter { 446 private: 447 std::vector<double> _values, _choices; 448 double _min, _max, _step; 449 // when in a loop, indicates current index in the vector _choices; is -1 450 // when not in a loop 451 int _index; 452 std::map<double, std::string> _valueLabels; 453 454 public: 455 number(const std::string &name = "", double value = 0., 456 const std::string &label = "", const std::string &help = "") parameter(name,label,help)457 : parameter(name, label, help), _values(std::vector<double>(1, value)), 458 _min(-maxNumber()), _max(maxNumber()), _step(0.), _index(-1) 459 { 460 } 461 number(const std::string &name, const std::vector<double> &values, 462 const std::string &label = "", const std::string &help = "") parameter(name,label,help)463 : parameter(name, label, help), _values(values), _min(-maxNumber()), 464 _max(maxNumber()), _step(0.), _index(-1) 465 { 466 } setValue(double value)467 void setValue(double value) 468 { 469 _values.resize(1); 470 _values[0] = value; 471 } setValues(const std::vector<double> & values)472 void setValues(const std::vector<double> &values) { _values = values; } setMin(double min)473 void setMin(double min) { _min = min; } setMax(double max)474 void setMax(double max) { _max = max; } setStep(double step)475 void setStep(double step) { _step = step; } setIndex(int index)476 void setIndex(int index) { _index = index; } setChoices(const std::vector<double> & choices)477 void setChoices(const std::vector<double> &choices) { _choices = choices; } setChoiceLabels(const std::vector<std::string> & labels)478 void setChoiceLabels(const std::vector<std::string> &labels) 479 { 480 if(labels.size() != _choices.size()) return; 481 if(_valueLabels.size()) _valueLabels.clear(); 482 for(std::size_t i = 0; i < _choices.size(); i++) 483 _valueLabels[_choices[i]] = labels[i]; 484 } setValueLabels(const std::map<double,std::string> & valueLabels)485 void setValueLabels(const std::map<double, std::string> &valueLabels) 486 { 487 _valueLabels = valueLabels; 488 } setValueLabel(double value,const std::string & label)489 void setValueLabel(double value, const std::string &label) 490 { 491 _valueLabels[value] = label; 492 } getType()493 std::string getType() const { return "number"; } getValue()494 double getValue() const 495 { 496 if(_values.empty()) return 0.; 497 return _values[0]; 498 } getValueAsString()499 std::string getValueAsString() const 500 { 501 std::ostringstream sstream; 502 sstream << getValue(); 503 return sstream.str(); 504 } getValues()505 const std::vector<double> &getValues() const { return _values; } getNumValues()506 int getNumValues() const { return (int)_values.size(); } getMin()507 double getMin() const { return _min; } getMax()508 double getMax() const { return _max; } getStep()509 double getStep() const { return _step; } getIndex()510 int getIndex() const { return _index; } getChoices()511 const std::vector<double> &getChoices() const { return _choices; } getValueLabels()512 const std::map<double, std::string> &getValueLabels() const 513 { 514 return _valueLabels; 515 } getValueLabel(double value)516 std::string getValueLabel(double value) const 517 { 518 auto it = 519 _valueLabels.find(value); 520 if(it != _valueLabels.end()) return it->second; 521 return ""; 522 } update(const number & p)523 void update(const number &p) 524 { 525 addClients(p.getClients()); 526 setLabel(p.getLabel()); 527 setHelp(p.getHelp()); 528 setVisible(p.getVisible()); 529 setReadOnly(p.getReadOnly()); 530 setAttributes(p.getAttributes()); 531 bool changed = false; 532 for(std::size_t i = 0; i < p.getValues().size(); i++) { 533 if(p.getValues()[i] != getValues()[i]) { 534 changed = true; 535 break; 536 } 537 } 538 if(changed) { 539 setValues(p.getValues()); 540 setChanged(getChangedValue()); 541 } 542 setMin(p.getMin()); 543 setMax(p.getMax()); 544 setStep(p.getStep()); 545 setIndex(p.getIndex()); 546 setChoices(p.getChoices()); 547 setValueLabels(p.getValueLabels()); 548 if(getNeverChanged()) setChanged(0); 549 } toChar()550 std::string toChar() const 551 { 552 std::ostringstream sstream; 553 sstream.precision(16); 554 sstream << parameter::toChar() << _values.size() << charSep(); 555 for(std::size_t i = 0; i < _values.size(); i++) 556 sstream << _values[i] << charSep(); 557 sstream << _min << charSep() << _max << charSep() << _step << charSep() 558 << _index << charSep() << _choices.size() << charSep(); 559 for(std::size_t i = 0; i < _choices.size(); i++) 560 sstream << _choices[i] << charSep(); 561 sstream << _valueLabels.size() << charSep(); 562 for(auto it = 563 _valueLabels.begin(); 564 it != _valueLabels.end(); it++) { 565 sstream << it->first << charSep() << sanitize(it->second) << charSep(); 566 } 567 return sstream.str(); 568 } fromChar(const std::string & msg)569 std::string::size_type fromChar(const std::string &msg) 570 { 571 std::string::size_type pos = parameter::fromChar(msg); 572 if(!pos) return 0; 573 _values.resize(atoi(getNextToken(msg, pos).c_str())); 574 for(std::size_t i = 0; i < _values.size(); i++) 575 _values[i] = atof(getNextToken(msg, pos).c_str()); 576 setMin(atof(getNextToken(msg, pos).c_str())); 577 setMax(atof(getNextToken(msg, pos).c_str())); 578 setStep(atof(getNextToken(msg, pos).c_str())); 579 setIndex(atoi(getNextToken(msg, pos).c_str())); 580 _choices.resize(atoi(getNextToken(msg, pos).c_str())); 581 for(std::size_t i = 0; i < _choices.size(); i++) 582 _choices[i] = atof(getNextToken(msg, pos).c_str()); 583 int numValueLabels = atoi(getNextToken(msg, pos).c_str()); 584 for(int i = 0; i < numValueLabels; i++) { 585 double value = atof(getNextToken(msg, pos).c_str()); 586 _valueLabels[value] = getNextToken(msg, pos); 587 } 588 return pos; 589 } toJSON()590 std::string toJSON() const 591 { 592 std::ostringstream sstream; 593 sstream.precision(16); 594 sstream << "{ " << parameter::toJSON() << ", \"values\":[ "; 595 for(std::size_t i = 0; i < _values.size(); i++) { 596 if(i) sstream << ", "; 597 sstream << _values[i]; 598 } 599 sstream << " ]" 600 << ", \"min\":" << _min << ", \"max\":" << _max 601 << ", \"step\":" << _step << ", \"index\":" << _index; 602 if(_choices.size()) { 603 sstream << ", \"choices\":[ "; 604 for(std::size_t i = 0; i < _choices.size(); i++) { 605 if(i) sstream << ", "; 606 sstream << _choices[i]; 607 } 608 sstream << " ]"; 609 } 610 if(_valueLabels.size()) { 611 sstream << ", \"valueLabels\":{ "; 612 for(auto it = 613 _valueLabels.begin(); 614 it != _valueLabels.end(); it++) { 615 if(it != _valueLabels.begin()) sstream << ", "; 616 sstream << "\"" << sanitizeJSON(it->second) << "\":" << it->first; 617 } 618 sstream << " }"; 619 } 620 sstream << " }"; 621 return sstream.str(); 622 } fromJSON(const std::string & json)623 bool fromJSON(const std::string &json) 624 { 625 #if defined(HAVE_PICOJSON) 626 picojson::value v; 627 std::string err = picojson::parse(v, json); 628 if(err.size()) return false; 629 if(!v.is<picojson::object>()) return false; 630 const picojson::value::object &par = v.get<picojson::object>(); 631 auto it = par.find("type"); 632 if(it == par.end()) return false; 633 if(it->second.to_str() == "number") { 634 fromJSON(par); 635 return true; 636 } 637 #endif 638 return false; 639 } 640 #if defined(HAVE_PICOJSON) fromJSON(const picojson::value::object & par)641 bool fromJSON(const picojson::value::object &par) 642 { 643 if(!parameter::fromJSON(par)) return false; 644 for(auto it = par.begin(); 645 it != par.end(); ++it) { 646 if(it->first == "values") { 647 if(!it->second.is<picojson::array>()) return false; 648 const picojson::value::array &arr = it->second.get<picojson::array>(); 649 _values.resize(arr.size()); 650 for(std::size_t i = 0; i < arr.size(); i++) { 651 if(!arr[i].is<double>()) return false; 652 _values[i] = arr[i].get<double>(); 653 } 654 } 655 else if(it->first == "min") { 656 if(!it->second.is<double>()) return false; 657 setMin(it->second.get<double>()); 658 } 659 else if(it->first == "max") { 660 if(!it->second.is<double>()) return false; 661 setMax(it->second.get<double>()); 662 } 663 else if(it->first == "step") { 664 if(!it->second.is<double>()) return false; 665 setStep(it->second.get<double>()); 666 } 667 else if(it->first == "index") { 668 if(!it->second.is<double>()) return false; 669 setIndex((int)it->second.get<double>()); 670 } 671 else if(it->first == "choices") { 672 if(!it->second.is<picojson::array>()) return false; 673 const picojson::value::array &arr = it->second.get<picojson::array>(); 674 _choices.resize(arr.size()); 675 for(std::size_t i = 0; i < arr.size(); i++) { 676 if(!arr[i].is<double>()) return false; 677 _choices[i] = arr[i].get<double>(); 678 } 679 } 680 else if(it->first == "valueLabels") { 681 if(!it->second.is<picojson::object>()) return false; 682 const picojson::value::object &obj = 683 it->second.get<picojson::object>(); 684 for(auto i = obj.begin(); 685 i != obj.end(); ++i) { 686 if(!i->second.is<double>()) return false; 687 _valueLabels[i->second.get<double>()] = i->first; 688 } 689 } 690 } 691 return true; 692 } 693 #endif 694 }; 695 696 // The string class. A string has a mutable "kind", that can be changed at 697 // runtime. Kinds leading to specific behavior in Gmsh are: "file". 698 class string : public parameter { 699 private: 700 std::vector<std::string> _values, _choices; 701 std::string _kind; 702 703 public: 704 string(const std::string &name = "", const std::string &value = "", 705 const std::string &label = "", const std::string &help = "") parameter(name,label,help)706 : parameter(name, label, help), 707 _values(std::vector<std::string>(1, value)), _kind("generic") 708 { 709 } 710 string(const std::string &name, const std::vector<std::string> &values, 711 const std::string &label = "", const std::string &help = "") parameter(name,label,help)712 : parameter(name, label, help), _values(values), _kind("generic") 713 { 714 } setValue(const std::string & value)715 void setValue(const std::string &value) 716 { 717 _values.resize(1); 718 _values[0] = value; 719 } setValues(const std::vector<std::string> & values)720 void setValues(const std::vector<std::string> &values) { _values = values; } setKind(const std::string & kind)721 void setKind(const std::string &kind) { _kind = kind; } setChoices(const std::vector<std::string> & choices)722 void setChoices(const std::vector<std::string> &choices) 723 { 724 _choices = choices; 725 } getType()726 std::string getType() const { return "string"; } getValue()727 const std::string &getValue() const 728 { 729 static std::string n(""); 730 if(_values.empty()) return n; 731 return _values[0]; 732 } getValueAsString()733 std::string getValueAsString() const 734 { 735 return getValue(); 736 } getValues()737 const std::vector<std::string> &getValues() const { return _values; } getNumValues()738 int getNumValues() const { return (int)_values.size(); } getKind()739 const std::string &getKind() const { return _kind; } getChoices()740 const std::vector<std::string> &getChoices() const { return _choices; } update(const string & p)741 void update(const string &p) 742 { 743 addClients(p.getClients()); 744 setLabel(p.getLabel()); 745 setHelp(p.getHelp()); 746 setVisible(p.getVisible()); 747 setReadOnly(p.getReadOnly()); 748 setAttributes(p.getAttributes()); 749 bool changed = false; 750 for(std::size_t i = 0; i < p.getValues().size(); i++) { 751 if(p.getValues()[i] != getValues()[i]) { 752 changed = true; 753 break; 754 } 755 } 756 if(changed) { 757 setValues(p.getValues()); 758 setChanged(getChangedValue()); 759 } 760 if(p.getKind() != getKind()) { 761 setKind(p.getKind()); 762 setChanged(getChangedValue()); 763 } 764 setChoices(p.getChoices()); 765 if(getNeverChanged()) setChanged(0); 766 } toChar()767 std::string toChar() const 768 { 769 std::ostringstream sstream; 770 sstream << parameter::toChar() << _values.size() << charSep(); 771 for(std::size_t i = 0; i < _values.size(); i++) 772 sstream << sanitize(_values[i]) << charSep(); 773 sstream << sanitize(_kind) << charSep() << _choices.size() << charSep(); 774 for(std::size_t i = 0; i < _choices.size(); i++) 775 sstream << sanitize(_choices[i]) << charSep(); 776 return sstream.str(); 777 } fromChar(const std::string & msg)778 std::string::size_type fromChar(const std::string &msg) 779 { 780 std::string::size_type pos = parameter::fromChar(msg); 781 if(!pos) return 0; 782 _values.resize(atoi(getNextToken(msg, pos).c_str())); 783 for(std::size_t i = 0; i < _values.size(); i++) 784 _values[i] = getNextToken(msg, pos); 785 setKind(getNextToken(msg, pos)); 786 _choices.resize(atoi(getNextToken(msg, pos).c_str())); 787 for(std::size_t i = 0; i < _choices.size(); i++) 788 _choices[i] = getNextToken(msg, pos); 789 return pos; 790 } toJSON()791 std::string toJSON() const 792 { 793 std::ostringstream sstream; 794 sstream << "{ " << parameter::toJSON() << ", \"values\":[ "; 795 for(std::size_t i = 0; i < _values.size(); i++) { 796 if(i) sstream << ", "; 797 sstream << "\"" << sanitizeJSON(_values[i]) << "\""; 798 } 799 sstream << " ] "; 800 if(_kind.size()) 801 sstream << ", \"kind\":\"" << sanitizeJSON(_kind) << "\""; 802 if(_choices.size()) { 803 sstream << ", \"choices\":[ "; 804 for(std::size_t i = 0; i < _choices.size(); i++) { 805 if(i) sstream << ", "; 806 sstream << "\"" << sanitizeJSON(_choices[i]) << "\""; 807 } 808 sstream << " ]"; 809 } 810 sstream << " }"; 811 return sstream.str(); 812 } fromJSON(const std::string & json)813 bool fromJSON(const std::string &json) 814 { 815 #if defined(HAVE_PICOJSON) 816 picojson::value v; 817 std::string err = picojson::parse(v, json); 818 if(err.size()) return false; 819 if(!v.is<picojson::object>()) return false; 820 const picojson::value::object &par = v.get<picojson::object>(); 821 auto it = par.find("type"); 822 if(it == par.end()) return false; 823 if(it->second.to_str() == "string") { 824 fromJSON(par); 825 return true; 826 } 827 #endif 828 return false; 829 } 830 #if defined(HAVE_PICOJSON) fromJSON(const picojson::value::object & par)831 bool fromJSON(const picojson::value::object &par) 832 { 833 if(!parameter::fromJSON(par)) return false; 834 for(auto it = par.begin(); 835 it != par.end(); ++it) { 836 if(it->first == "values") { 837 if(!it->second.is<picojson::array>()) return false; 838 const picojson::value::array &arr = it->second.get<picojson::array>(); 839 _values.resize(arr.size()); 840 for(std::size_t i = 0; i < arr.size(); i++) { 841 if(!arr[i].is<std::string>()) return false; 842 _values[i] = arr[i].get<std::string>(); 843 } 844 } 845 else if(it->first == "kind") { 846 if(!it->second.is<std::string>()) return false; 847 setKind(it->second.get<std::string>()); 848 } 849 else if(it->first == "choices") { 850 if(!it->second.is<picojson::array>()) return false; 851 const picojson::value::array &arr = it->second.get<picojson::array>(); 852 _choices.resize(arr.size()); 853 for(std::size_t i = 0; i < arr.size(); i++) { 854 if(!arr[i].is<std::string>()) return false; 855 _choices[i] = arr[i].get<std::string>(); 856 } 857 } 858 } 859 return true; 860 } 861 #endif 862 }; 863 864 // The parameter space, i.e., the set of parameters stored and handled by the 865 // ONELAB server. 866 class parameterSpace { 867 private: 868 std::set<number *, parameterLessThan> _numbers; 869 std::set<string *, parameterLessThan> _strings; 870 std::mutex _mutex; 871 872 // delete a parameter from the parameter space 873 template <class T> _clear(const std::string & name,const std::string & client,std::set<T *,parameterLessThan> & ps)874 bool _clear(const std::string &name, const std::string &client, 875 std::set<T *, parameterLessThan> &ps) 876 { 877 if(name.empty() && client.size()) { 878 std::vector<T *> toDelete; 879 for(auto it = ps.begin(); 880 it != ps.end();) { 881 T *p = *it; 882 if(p->hasClient(client)) { 883 ps.erase(it++); // to avoid invalid iterator 884 delete p; 885 } 886 else { 887 it++; 888 } 889 } 890 } 891 else { 892 T tmp(name); 893 auto it = ps.find(&tmp); 894 if(it != ps.end()) { 895 T *p = *it; 896 if(client.empty() || p->hasClient(client)) { 897 ps.erase(it); 898 delete p; 899 return true; 900 } 901 } 902 } 903 return false; 904 } 905 // set a parameter in the parameter space; if it already exists, update it 906 // (adding new clients if necessary). This would need to be locked to avoid 907 // race conditions when several clients try to set a parameter at the same 908 // time. 909 template <class T> _set(const T & p,const std::string & client,std::set<T *,parameterLessThan> & ps)910 bool _set(const T &p, const std::string &client, 911 std::set<T *, parameterLessThan> &ps) 912 { 913 _mutex.lock(); 914 auto it = ps.find((T *)&p); 915 if(it != ps.end()) { 916 (*it)->update(p); 917 if(client.size()) 918 (*it)->addClient(client, parameter::defaultChangedValue()); 919 } 920 else { 921 T *newp = new T(p); 922 if(client.size()) 923 newp->addClient(client, parameter::defaultChangedValue()); 924 ps.insert(newp); 925 } 926 _mutex.unlock(); 927 return true; 928 } 929 // get the parameter matching the given name, or all the parameters in the 930 // category if no name is given. If we find a given parameter by name, we 931 // add the client requesting the parameter to the list of clients for this 932 // parameter. This would also need to be locked. 933 template <class T> _get(std::vector<T> & p,const std::string & name,const std::string & client,std::set<T *,parameterLessThan> & ps)934 bool _get(std::vector<T> &p, const std::string &name, 935 const std::string &client, std::set<T *, parameterLessThan> &ps) 936 { 937 p.clear(); 938 if(name.empty()) { 939 for(auto it = ps.begin(); 940 it != ps.end(); it++) 941 p.push_back(**it); 942 } 943 else { 944 T tmp(name); 945 auto it = ps.find(&tmp); 946 if(it != ps.end()) { 947 if(client.size()){ 948 _mutex.lock(); 949 (*it)->addClient(client, parameter::defaultChangedValue()); 950 _mutex.unlock(); 951 } 952 p.push_back(**it); 953 } 954 } 955 return true; 956 } 957 template <class T> _getPtr(std::string name,const std::string client,std::set<T *,parameterLessThan> ps)958 T *_getPtr(std::string name, const std::string client, 959 std::set<T *, parameterLessThan> ps) 960 { 961 T tmp(name); 962 auto it = ps.find(&tmp); 963 if(it != ps.end()) { 964 if(client.size()){ 965 _mutex.lock(); 966 (*it)->addClient(client, parameter::defaultChangedValue()); 967 _mutex.unlock(); 968 } 969 return *it; 970 } 971 return nullptr; 972 } 973 974 public: parameterSpace()975 parameterSpace() {} ~parameterSpace()976 ~parameterSpace() { clear(); } 977 void clear(const std::string &name = "", const std::string &client = "") 978 { 979 if(name.empty() && client.empty()) { 980 std::set<parameter *, parameterLessThan> ps; 981 getAllParameters(ps); 982 for(auto it = ps.begin(); 983 it != ps.end(); it++) 984 delete *it; 985 _numbers.clear(); 986 _strings.clear(); 987 } 988 else { 989 bool done = _clear(name, client, _numbers); 990 if(!done) done = _clear(name, client, _strings); 991 } 992 } 993 bool set(const number &p, const std::string &client = "") 994 { 995 return _set(p, client, _numbers); 996 } 997 bool set(const string &p, const std::string &client = "") 998 { 999 return _set(p, client, _strings); 1000 } 1001 bool get(std::vector<number> &ps, const std::string &name = "", 1002 const std::string &client = "") 1003 { 1004 return _get(ps, name, client, _numbers); 1005 } 1006 bool get(std::vector<string> &ps, const std::string &name = "", 1007 const std::string &client = "") 1008 { 1009 return _get(ps, name, client, _strings); 1010 } 1011 void getPtr(number **ptr, const std::string &name, 1012 const std::string &client = "") 1013 { 1014 *ptr = _getPtr(name, client, _numbers); 1015 } 1016 void getPtr(string **ptr, const std::string &name, 1017 const std::string &client = "") 1018 { 1019 *ptr = _getPtr(name, client, _strings); 1020 } getAllParameters(std::set<parameter *,parameterLessThan> & ps)1021 void getAllParameters(std::set<parameter *, parameterLessThan> &ps) const 1022 { 1023 ps.insert(_numbers.begin(), _numbers.end()); 1024 ps.insert(_strings.begin(), _strings.end()); 1025 } getNumParameters()1026 int getNumParameters() 1027 { 1028 return (int)(_numbers.size() + _strings.size()); 1029 } 1030 void getParameterNames(std::vector<std::string> &names, 1031 const std::string &search = "") const 1032 { 1033 names.clear(); 1034 if(search.empty()) { 1035 for(auto &p : _numbers) names.push_back(p->getName()); 1036 for(auto &p : _strings) names.push_back(p->getName()); 1037 } 1038 else{ 1039 try{ 1040 for(auto &p : _numbers) { 1041 if(std::regex_search(p->getName(), std::regex(search))) 1042 names.push_back(p->getName()); 1043 } 1044 for(auto &p : _strings) { 1045 if(std::regex_search(p->getName(), std::regex(search))) 1046 names.push_back(p->getName()); 1047 } 1048 } catch(...)1049 catch(...) { 1050 } 1051 } 1052 } 1053 // check if at least one parameter depends on the given client hasClient(const std::string & client)1054 bool hasClient(const std::string &client) const 1055 { 1056 std::set<parameter *, parameterLessThan> ps; 1057 getAllParameters(ps); 1058 for(auto it = ps.begin(); 1059 it != ps.end(); it++) 1060 if((*it)->hasClient(client)) return true; 1061 return false; 1062 } 1063 // check if some parameters have changed (optionnally only check the 1064 // parameters that depend on a given client) 1065 int getChanged(const std::string &client = "") const 1066 { 1067 std::set<parameter *, parameterLessThan> ps; 1068 getAllParameters(ps); 1069 int changed = 0; 1070 for(auto it = ps.begin(); 1071 it != ps.end(); it++) { 1072 changed = std::max(changed, (*it)->getChanged(client)); 1073 } 1074 return changed; 1075 } 1076 // set the changed flag for all the parameters that depend on the given 1077 // client (or for all parameters if no client name is provided) 1078 void setChanged(int changed, const std::string &client = "") 1079 { 1080 std::set<parameter *, parameterLessThan> ps; 1081 getAllParameters(ps); 1082 for(auto it = ps.begin(); 1083 it != ps.end(); it++) 1084 (*it)->setChanged(changed, client); 1085 } 1086 void thresholdChanged(int threshold, const std::string &client = "") 1087 { 1088 std::set<parameter *, parameterLessThan> ps; 1089 getAllParameters(ps); 1090 for(auto it = ps.begin(); 1091 it != ps.end(); it++) { 1092 int changed = (*it)->getChanged(client); 1093 if(changed > threshold) (*it)->setChanged(threshold, client); 1094 } 1095 } 1096 // serialize the parameter space (optionally only serialize those parameters 1097 // that depend on the given client) 1098 std::vector<std::string> toChar(const std::string &client = "") const 1099 { 1100 std::vector<std::string> s; 1101 std::set<parameter *, parameterLessThan> ps; 1102 getAllParameters(ps); 1103 for(auto it = 1104 ps.begin(); 1105 it != ps.end(); it++) 1106 if(client.empty() || (*it)->hasClient(client)) { 1107 if((*it)->getAttribute("NotInDb") != "True") 1108 s.push_back((*it)->toChar()); 1109 } 1110 return s; 1111 } 1112 // unserialize the parameter space 1113 bool fromChar(const std::vector<std::string> &msg, 1114 const std::string &client = "") 1115 { 1116 for(std::size_t i = 0; i < msg.size(); i++) { 1117 std::string version, type, name; 1118 onelab::parameter::getInfoFromChar(msg[i], version, type, name); 1119 if(onelab::parameter::version() != version) return false; 1120 if(type == "number") { 1121 number p; 1122 p.fromChar(msg[i]); 1123 set(p, client); 1124 } 1125 else if(type == "string") { 1126 string p; 1127 p.fromChar(msg[i]); 1128 set(p, client); 1129 } 1130 else 1131 return false; 1132 } 1133 return true; 1134 } 1135 bool toJSON(std::string &json, const std::string &creator = "", 1136 const std::string &client = "") const 1137 { 1138 time_t now; 1139 time(&now); 1140 std::string t(ctime(&now)); 1141 t.resize(t.size() - 1); 1142 json.clear(); 1143 json += "{ \"onelab\":{\n"; 1144 json += " \"creator\":\"" + creator + "\",\n"; 1145 json += " \"date\":\"" + t + "\",\n"; 1146 json += " \"version\":\"" + parameter::version() + "\",\n"; 1147 json += " \"parameters\":[\n"; 1148 std::set<parameter *, parameterLessThan> ps; 1149 getAllParameters(ps); 1150 for(auto it = 1151 ps.begin(); 1152 it != ps.end(); it++) { 1153 if(it != ps.begin()) json += ",\n"; 1154 if(client.empty() || (*it)->hasClient(client)) { 1155 if((*it)->getAttribute("NotInDb") != "True") { 1156 json += " " + (*it)->toJSON(); 1157 } 1158 } 1159 } 1160 json += "\n ] }\n}\n"; 1161 return true; 1162 } 1163 bool fromJSON(const std::string &json, const std::string &client = "") 1164 { 1165 #if defined(HAVE_PICOJSON) 1166 picojson::value v; 1167 std::string err = picojson::parse(v, json); 1168 if(err.size()) return false; 1169 if(v.is<picojson::object>()){ // full database or single parameter 1170 const picojson::value::object &obj = v.get<picojson::object>(); 1171 auto it = obj.find("onelab"); 1172 if(it != obj.end()){ // full database 1173 if(!it->second.is<picojson::object>()) return false; 1174 const picojson::value::object &db = it->second.get<picojson::object>(); 1175 for(auto j = db.begin(); j != db.end(); ++j) { 1176 if(j->first == "version") { 1177 if(!j->second.is<std::string>()) return false; 1178 if(j->second.get<std::string>() != parameter::version()) 1179 return false; 1180 } 1181 else if(j->first == "parameters") { 1182 if(!j->second.is<picojson::array>()) return false; 1183 const picojson::value::array &arr = j->second.get<picojson::array>(); 1184 for(std::size_t k = 0; k < arr.size(); k++) { 1185 if(!arr[k].is<picojson::object>()) return false; 1186 const picojson::value::object &par = arr[k].get<picojson::object>(); 1187 if(!fromJSON(par, client)) return false; 1188 } 1189 } 1190 } 1191 return true; 1192 } 1193 else{ // single parameter 1194 return fromJSON(obj, client); 1195 } 1196 } 1197 else if(v.is<picojson::array>()){ // array of parameters 1198 const picojson::value::array &arr = v.get<picojson::array>(); 1199 for(std::size_t k = 0; k < arr.size(); k++) { 1200 if(!arr[k].is<picojson::object>()) return false; 1201 const picojson::value::object &par = arr[k].get<picojson::object>(); 1202 if(!fromJSON(par, client)) return false; 1203 } 1204 return true; 1205 } 1206 else{ 1207 return false; 1208 } 1209 #else 1210 return false; 1211 #endif 1212 } 1213 #if defined(HAVE_PICOJSON) 1214 bool fromJSON(const picojson::value::object &par, const std::string &client = "") 1215 { 1216 auto it = par.find("type"); 1217 if(it == par.end()) return false; 1218 if(it->second.to_str() == "number") { 1219 number p; 1220 p.fromJSON(par); 1221 set(p, client); 1222 return true; 1223 } 1224 else if(it->second.to_str() == "string") { 1225 string p; 1226 p.fromJSON(par); 1227 set(p, client); 1228 return true; 1229 } 1230 return false; 1231 } 1232 #endif 1233 }; 1234 1235 // The ONELAB client: a class that communicates with the ONELAB server. Each 1236 // client should be derived from this one. A client can be understood as "one 1237 // simulation step in a complex computation". 1238 class client { 1239 protected: 1240 // the name of the client 1241 std::string _name; 1242 // the id of the client, used to create a unique socket for this client 1243 int _id; 1244 // the index of the client in an external client list (if any) 1245 int _index; 1246 1247 public: client(const std::string & name)1248 client(const std::string &name) : _name(name), _id(0), _index(-1) {} ~client()1249 virtual ~client() {} getName()1250 std::string getName() { return _name; } setId(int id)1251 void setId(int id) { _id = id; } getId()1252 int getId() { return _id; } setIndex(int index)1253 void setIndex(int index) { _index = index; } getIndex()1254 int getIndex() { return _index; } run()1255 virtual bool run() { return false; } isNetworkClient()1256 virtual bool isNetworkClient() { return false; } kill()1257 virtual bool kill() { return false; } sendInfo(const std::string & msg)1258 virtual void sendInfo(const std::string &msg) 1259 { 1260 std::cout << msg << std::endl; 1261 } sendWarning(const std::string & msg)1262 virtual void sendWarning(const std::string &msg) 1263 { 1264 std::cerr << msg << std::endl; 1265 } sendError(const std::string & msg)1266 virtual void sendError(const std::string &msg) 1267 { 1268 std::cerr << msg << std::endl; 1269 } sendProgress(const std::string & msg)1270 virtual void sendProgress(const std::string &msg) 1271 { 1272 std::cout << msg << std::endl; 1273 } sendMergeFileRequest(const std::string & msg)1274 virtual void sendMergeFileRequest(const std::string &msg) {} sendOpenProjectRequest(const std::string & msg)1275 virtual void sendOpenProjectRequest(const std::string &msg) {} sendParseStringRequest(const std::string & msg)1276 virtual void sendParseStringRequest(const std::string &msg) {} sendVertexArray(const std::string & msg)1277 virtual void sendVertexArray(const std::string &msg) {} 1278 virtual bool clear(const std::string &name) = 0; 1279 virtual bool set(const number &p) = 0; 1280 virtual bool set(const string &p) = 0; 1281 virtual bool get(std::vector<number> &ps, const std::string &name = "") = 0; 1282 virtual bool get(std::vector<string> &ps, const std::string &name = "") = 0; 1283 virtual bool setAndAppendChoices(const number &p) = 0; 1284 virtual bool setAndAppendChoices(const string &p) = 0; 1285 virtual bool getWithoutChoices(std::vector<number> &ps, 1286 const std::string &name = "") = 0; 1287 virtual bool getWithoutChoices(std::vector<string> &ps, 1288 const std::string &name = "") = 0; toChar()1289 std::vector<std::string> toChar() 1290 { 1291 std::vector<std::string> out; 1292 std::vector<number> n; 1293 get(n); 1294 for(std::size_t i = 0; i < n.size(); i++) out.push_back(n[i].toChar()); 1295 std::vector<string> s; 1296 get(s); 1297 for(std::size_t i = 0; i < s.size(); i++) out.push_back(s[i].toChar()); 1298 return out; 1299 } fromChar(const std::vector<std::string> & msg)1300 bool fromChar(const std::vector<std::string> &msg) 1301 { 1302 for(std::size_t i = 0; i < msg.size(); i++) { 1303 std::string version, type, name; 1304 onelab::parameter::getInfoFromChar(msg[i], version, type, name); 1305 if(onelab::parameter::version() != version) return false; 1306 if(type == "number") { 1307 number p; 1308 p.fromChar(msg[i]); 1309 set(p); 1310 } 1311 else if(type == "string") { 1312 string p; 1313 p.fromChar(msg[i]); 1314 set(p); 1315 } 1316 else 1317 return false; 1318 } 1319 return true; 1320 } toFile(FILE * fp)1321 bool toFile(FILE *fp) { return parameter::toFile(toChar(), fp, getName()); } fromFile(FILE * fp)1322 bool fromFile(FILE *fp) 1323 { 1324 std::vector<std::string> msg; 1325 if(parameter::fromFile(msg, fp)) return fromChar(msg); 1326 return false; 1327 } 1328 }; 1329 1330 // The ONELAB server: a singleton that stores the parameter space and 1331 // interacts with ONELAB clients. 1332 class server { 1333 private: 1334 // the unique server (singleton behaviour due to the "static" specifier) 1335 static server *_server; 1336 // the address of the server 1337 std::string _address; 1338 // the connected clients 1339 std::set<client *> _clients; 1340 // the parameter space 1341 parameterSpace _parameterSpace; 1342 1343 public: _address(address)1344 server(const std::string &address = "") : _address(address) {} ~server()1345 ~server() {} 1346 static server *instance(const std::string &address = "") 1347 { 1348 if(!_server) _server = new server(address); 1349 return _server; 1350 } setInstance(server * s)1351 static void setInstance(server *s) { _server = s; } 1352 void clear(const std::string &name = "", const std::string &client = "") 1353 { 1354 _parameterSpace.clear(name, client); 1355 } 1356 template <class T> bool set(const T &p, const std::string &client = "") 1357 { 1358 return _parameterSpace.set(p, client); 1359 } 1360 template <class T> 1361 bool get(std::vector<T> &ps, const std::string &name = "", 1362 const std::string &client = "") 1363 { 1364 return _parameterSpace.get(ps, name, client); 1365 } 1366 typedef std::set<client *>::iterator citer; firstClient()1367 citer firstClient() { return _clients.begin(); } lastClient()1368 citer lastClient() { return _clients.end(); } getNumClients()1369 int getNumClients() { return (int)_clients.size(); }; findClient(const std::string & name)1370 citer findClient(const std::string &name) 1371 { 1372 for(auto it = _clients.begin(); it != _clients.end(); it++) 1373 if((*it)->getName() == name) return it; 1374 return _clients.end(); 1375 } registerClient(client * c)1376 void registerClient(client *c) 1377 { 1378 _clients.insert(c); 1379 c->setId((int)_clients.size()); 1380 } unregisterClient(client * c)1381 void unregisterClient(client *c) { _clients.erase(c); } 1382 void setChanged(int changed, const std::string &client = "") 1383 { 1384 _parameterSpace.setChanged(changed, client); 1385 } 1386 int getChanged(const std::string &client = "") 1387 { 1388 return _parameterSpace.getChanged(client); 1389 } 1390 void thresholdChanged(int value, const std::string &client = "") 1391 { 1392 _parameterSpace.thresholdChanged(value, client); 1393 } getNumParameters()1394 int getNumParameters() 1395 { 1396 return _parameterSpace.getNumParameters(); 1397 } 1398 void getParameterNames(std::vector<std::string> &names, 1399 const std::string &search = "") const 1400 { 1401 _parameterSpace.getParameterNames(names, search); 1402 } 1403 std::vector<std::string> toChar(const std::string &client = "") 1404 { 1405 return _parameterSpace.toChar(client); 1406 } 1407 bool fromChar(const std::vector<std::string> &msg, 1408 const std::string &client = "") 1409 { 1410 return _parameterSpace.fromChar(msg, client); 1411 } 1412 bool toFile(FILE *fp, const std::string &client = "") 1413 { 1414 return parameter::toFile(toChar(client), fp, "onelab server"); 1415 } 1416 bool fromFile(FILE *fp, const std::string &client = "") 1417 { 1418 std::vector<std::string> msg; 1419 if(parameter::fromFile(msg, fp)) return fromChar(msg, client); 1420 return false; 1421 } 1422 bool toJSON(std::string &json, const std::string &client = "") 1423 { 1424 return _parameterSpace.toJSON(json, client); 1425 } 1426 bool fromJSON(const std::string &json, const std::string &client = "") 1427 { 1428 return _parameterSpace.fromJSON(json, client); 1429 } 1430 }; 1431 1432 // A local client, which lives in the same memory space as the server. 1433 class localClient : public client { 1434 private: _set(const T & p)1435 template <class T> bool _set(const T &p) 1436 { 1437 server::instance()->set(p, _name); 1438 return true; 1439 } 1440 template <class T> 1441 bool _get(std::vector<T> &ps, const std::string &name = "") 1442 { 1443 server::instance()->get(ps, name, _name); 1444 return true; 1445 } 1446 1447 public: localClient(const std::string & name)1448 localClient(const std::string &name) : client(name) 1449 { 1450 server::instance()->registerClient(this); 1451 } ~localClient()1452 virtual ~localClient() { server::instance()->unregisterClient(this); } 1453 virtual bool clear(const std::string &name = "") 1454 { 1455 server::instance()->clear(name); 1456 return true; 1457 } set(const number & p)1458 virtual bool set(const number &p) { return _set(p); } set(const string & p)1459 virtual bool set(const string &p) { return _set(p); } 1460 virtual bool get(std::vector<number> &ps, const std::string &name = "") 1461 { 1462 return _get(ps, name); 1463 } 1464 virtual bool get(std::vector<string> &ps, const std::string &name = "") 1465 { 1466 return _get(ps, name); 1467 } setAndAppendChoices(const number & p)1468 virtual bool setAndAppendChoices(const number &p) 1469 { 1470 std::vector<number> ps; 1471 _get(ps, _name); 1472 std::vector<double> choices; 1473 if(ps.size()) choices = ps[0].getChoices(); 1474 choices.insert(choices.end(), p.getChoices().begin(), 1475 p.getChoices().end()); 1476 number p2(p); 1477 p2.setChoices(choices); 1478 return _set(p2); 1479 } setAndAppendChoices(const string & p)1480 virtual bool setAndAppendChoices(const string &p) 1481 { 1482 std::vector<string> ps; 1483 _get(ps, _name); 1484 std::vector<std::string> choices; 1485 if(ps.size()) choices = ps[0].getChoices(); 1486 choices.insert(choices.end(), p.getChoices().begin(), 1487 p.getChoices().end()); 1488 string p2(p); 1489 p2.setChoices(choices); 1490 return _set(p2); 1491 } 1492 virtual bool getWithoutChoices(std::vector<number> &ps, 1493 const std::string &name = "") 1494 { 1495 bool ret = _get(ps, name); 1496 for(std::size_t i = 0; i < ps.size(); i++) 1497 ps[i].setChoices(std::vector<double>()); 1498 return ret; 1499 } 1500 virtual bool getWithoutChoices(std::vector<string> &ps, 1501 const std::string &name = "") 1502 { 1503 bool ret = _get(ps, name); 1504 for(std::size_t i = 0; i < ps.size(); i++) 1505 ps[i].setChoices(std::vector<std::string>()); 1506 return ret; 1507 } 1508 }; 1509 1510 // The local part of a network client. 1511 class localNetworkClient : public localClient { 1512 private: 1513 // executable of the client (including filesystem path, if necessary) 1514 std::string _executable; 1515 // treat the executable name as a full command line (will prevent the 1516 // escaping of the exe name, and will assume that the command line has been 1517 // correcly escaped) 1518 bool _treatExecutableAsFullCommandLine; 1519 // command to login to a remote host (if necessary) 1520 std::string _remoteLogin; 1521 // command line option to specify socket 1522 std::string _socketSwitch; 1523 // pid of the remote network client while it is running (-1 otherwise) 1524 int _pid; 1525 // underlying GmshServer 1526 GmshServer *_gmshServer; 1527 1528 public: 1529 localNetworkClient(const std::string &name, const std::string &executable, 1530 const std::string &remoteLogin = "", 1531 bool treatExecutableAsFullCommandLine = false) localClient(name)1532 : localClient(name), _executable(executable), 1533 _treatExecutableAsFullCommandLine(treatExecutableAsFullCommandLine), 1534 _remoteLogin(remoteLogin), _socketSwitch("-onelab"), _pid(-1), 1535 _gmshServer(nullptr) 1536 { 1537 } ~localNetworkClient()1538 virtual ~localNetworkClient() {} isNetworkClient()1539 virtual bool isNetworkClient() { return true; } getExecutable()1540 const std::string &getExecutable() { return _executable; } setExecutable(const std::string & s)1541 void setExecutable(const std::string &s) { _executable = s; } getRemoteLogin()1542 const std::string &getRemoteLogin() { return _remoteLogin; } treatExecutableAsFullCommandLine()1543 bool treatExecutableAsFullCommandLine() const 1544 { 1545 return _treatExecutableAsFullCommandLine; 1546 } setRemoteLogin(const std::string & s)1547 void setRemoteLogin(const std::string &s) { _remoteLogin = s; } getSocketSwitch()1548 const std::string &getSocketSwitch() { return _socketSwitch; } setSocketSwitch(const std::string & s)1549 void setSocketSwitch(const std::string &s) { _socketSwitch = s; } getPid()1550 int getPid() { return _pid; } setPid(int pid)1551 void setPid(int pid) { _pid = pid; } getGmshServer()1552 GmshServer *getGmshServer() { return _gmshServer; } setGmshServer(GmshServer * server)1553 void setGmshServer(GmshServer *server) { _gmshServer = server; } 1554 virtual bool run() = 0; 1555 virtual bool kill() = 0; 1556 }; 1557 1558 // The remote part of a network client. 1559 class remoteNetworkClient : public client { 1560 private: 1561 // address (inet:port or unix socket) of the server 1562 std::string _serverAddress; 1563 // underlying GmshClient 1564 GmshClient *_gmshClient; 1565 // number of subclients 1566 int _numSubClients; 1567 template <class T> bool _set(const T &p, bool withChoices = true) 1568 { 1569 if(!_gmshClient) return false; 1570 std::string msg = p.toChar(); 1571 _gmshClient->SendMessage(withChoices ? 1572 GmshSocket::GMSH_PARAMETER : 1573 GmshSocket::GMSH_PARAMETER_WITHOUT_CHOICES, 1574 (int)msg.size(), &msg[0]); 1575 return true; 1576 } 1577 template <class T> 1578 bool _get(std::vector<T> &ps, const std::string &name = "", 1579 bool withChoices = true) 1580 { 1581 ps.clear(); 1582 if(!_gmshClient) return false; 1583 T p(name); 1584 std::string msg = p.toChar(); 1585 if(name.size()) 1586 _gmshClient->SendMessage( 1587 withChoices ? GmshSocket::GMSH_PARAMETER_QUERY : 1588 GmshSocket::GMSH_PARAMETER_QUERY_WITHOUT_CHOICES, 1589 (int)msg.size(), &msg[0]); 1590 else // get all parameters 1591 _gmshClient->SendMessage(GmshSocket::GMSH_PARAMETER_QUERY_ALL, 1592 (int)msg.size(), &msg[0]); 1593 1594 while(1) { 1595 // stop if we have no communications for 5 minutes 1596 int ret = _gmshClient->Select(500, 0); 1597 if(!ret) { 1598 _gmshClient->Info("Timeout: aborting remote get"); 1599 return false; 1600 } 1601 else if(ret < 0) { 1602 _gmshClient->Error("Error on select: aborting remote get"); 1603 return false; 1604 } 1605 int type, length, swap; 1606 if(!_gmshClient->ReceiveHeader(&type, &length, &swap)) { 1607 _gmshClient->Error( 1608 "Did not receive message header: aborting remote get"); 1609 return false; 1610 } 1611 std::string msg(length, ' '); 1612 if(!_gmshClient->ReceiveMessage(length, &msg[0])) { 1613 _gmshClient->Error( 1614 "Did not receive message body: aborting remote get"); 1615 return false; 1616 } 1617 if(type == GmshSocket::GMSH_PARAMETER) { 1618 T p; 1619 p.fromChar(msg); 1620 ps.push_back(p); 1621 return true; 1622 } 1623 if(type == GmshSocket::GMSH_PARAMETER_QUERY_ALL) { 1624 T p; 1625 p.fromChar(msg); 1626 ps.push_back(p); 1627 // do NOT return until all parameters have been downloaded 1628 } 1629 else if(type == GmshSocket::GMSH_PARAMETER_QUERY_END) { 1630 // all parameters have been sent 1631 return true; 1632 } 1633 else if(type == GmshSocket::GMSH_PARAMETER_NOT_FOUND) { 1634 // parameter not found 1635 return true; 1636 } 1637 else if(type == GmshSocket::GMSH_INFO) { 1638 return true; 1639 } 1640 else { 1641 _gmshClient->Error("Unknown message type: aborting remote get"); 1642 return false; 1643 } 1644 } 1645 return true; 1646 } 1647 1648 public: waitOnSubClients()1649 void waitOnSubClients() 1650 { 1651 if(!_gmshClient) return; 1652 while(_numSubClients > 0) { 1653 int ret = _gmshClient->Select(500, 0); 1654 if(!ret) { 1655 _gmshClient->Info("Timeout: aborting wait on subclients"); 1656 return; 1657 } 1658 else if(ret < 0) { 1659 _gmshClient->Error("Error on select: aborting wait on subclients"); 1660 return; 1661 } 1662 int type, length, swap; 1663 if(!_gmshClient->ReceiveHeader(&type, &length, &swap)) { 1664 _gmshClient->Error( 1665 "Did not receive message header: aborting wait on subclients"); 1666 return; 1667 } 1668 std::string msg(length, ' '); 1669 if(!_gmshClient->ReceiveMessage(length, &msg[0])) { 1670 _gmshClient->Error( 1671 "Did not receive message body: aborting wait on subclients"); 1672 return; 1673 } 1674 if(type == GmshSocket::GMSH_STOP) _numSubClients -= 1; 1675 } 1676 } 1677 1678 public: remoteNetworkClient(const std::string & name,const std::string & serverAddress)1679 remoteNetworkClient(const std::string &name, 1680 const std::string &serverAddress) 1681 : client(name), _serverAddress(serverAddress), _numSubClients(0) 1682 { 1683 _gmshClient = new GmshClient(); 1684 if(_gmshClient->Connect(_serverAddress.c_str()) < 0) { 1685 delete _gmshClient; 1686 _gmshClient = nullptr; 1687 } 1688 else { 1689 _gmshClient->Start(); 1690 } 1691 } ~remoteNetworkClient()1692 virtual ~remoteNetworkClient() 1693 { 1694 if(_gmshClient) { 1695 waitOnSubClients(); 1696 _gmshClient->Stop(); 1697 _gmshClient->Disconnect(); 1698 delete _gmshClient; 1699 _gmshClient = nullptr; 1700 } 1701 } getGmshClient()1702 GmshClient *getGmshClient() { return _gmshClient; } isNetworkClient()1703 virtual bool isNetworkClient() { return true; } 1704 virtual bool clear(const std::string &name = "") 1705 { 1706 if(!_gmshClient) return false; 1707 std::string msg = name; 1708 if(msg.empty()) msg = "*"; 1709 _gmshClient->SendMessage(GmshSocket::GMSH_PARAMETER_CLEAR, (int)msg.size(), 1710 &msg[0]); 1711 return true; 1712 } set(const number & p)1713 virtual bool set(const number &p) { return _set(p); } set(const string & p)1714 virtual bool set(const string &p) { return _set(p); } 1715 virtual bool get(std::vector<number> &ps, const std::string &name = "") 1716 { 1717 return _get(ps, name); 1718 } 1719 virtual bool get(std::vector<string> &ps, const std::string &name = "") 1720 { 1721 return _get(ps, name); 1722 } setAndAppendChoices(const number & p)1723 virtual bool setAndAppendChoices(const number &p) 1724 { 1725 // this will send the parameter without choices, using 1726 // GMSH_PARAMETER_WITHOUT_CHOICES instead of GMSH_PARAMETER; the ONELAB 1727 // server will then append the value to the choices server-side. 1728 return _set(p, false); 1729 } setAndAppendChoices(const string & p)1730 virtual bool setAndAppendChoices(const string &p) 1731 { 1732 // idem 1733 return _set(p, false); 1734 } 1735 virtual bool getWithoutChoices(std::vector<number> &ps, 1736 const std::string &name = "") 1737 { 1738 return _get(ps, name, false); 1739 } 1740 virtual bool getWithoutChoices(std::vector<string> &ps, 1741 const std::string &name = "") 1742 { 1743 return _get(ps, name, false); 1744 } sendInfo(const std::string & msg)1745 void sendInfo(const std::string &msg) 1746 { 1747 if(_gmshClient) _gmshClient->Info(msg.c_str()); 1748 } sendWarning(const std::string & msg)1749 void sendWarning(const std::string &msg) 1750 { 1751 if(_gmshClient) _gmshClient->Warning(msg.c_str()); 1752 } sendError(const std::string & msg)1753 void sendError(const std::string &msg) 1754 { 1755 if(_gmshClient) _gmshClient->Error(msg.c_str()); 1756 } sendProgress(const std::string & msg)1757 void sendProgress(const std::string &msg) 1758 { 1759 if(_gmshClient) _gmshClient->Progress(msg.c_str()); 1760 } sendMergeFileRequest(const std::string & msg)1761 void sendMergeFileRequest(const std::string &msg) 1762 { 1763 if(_gmshClient) _gmshClient->MergeFile(msg.c_str()); 1764 } sendOpenProjectRequest(const std::string & msg)1765 void sendOpenProjectRequest(const std::string &msg) 1766 { 1767 if(_gmshClient) _gmshClient->OpenProject(msg.c_str()); 1768 } sendParseStringRequest(const std::string & msg)1769 void sendParseStringRequest(const std::string &msg) 1770 { 1771 if(_gmshClient) _gmshClient->ParseString(msg.c_str()); 1772 } runNonBlockingSubClient(const std::string & name,const std::string & command)1773 void runNonBlockingSubClient(const std::string &name, 1774 const std::string &command) 1775 { 1776 #if !defined(BUILD_IOS) 1777 if(!_gmshClient) { 1778 int res = system(command.c_str()); 1779 if(res) { 1780 // report error 1781 } 1782 return; 1783 } 1784 #endif 1785 std::string msg = name + parameter::charSep() + command; 1786 _gmshClient->SendMessage(GmshSocket::GMSH_CONNECT, (int)msg.size(), &msg[0]); 1787 _numSubClients += 1; 1788 } runSubClient(const std::string & name,const std::string & command)1789 void runSubClient(const std::string &name, const std::string &command) 1790 { 1791 runNonBlockingSubClient(name, command); 1792 waitOnSubClients(); 1793 } 1794 }; 1795 1796 } // namespace onelab 1797 1798 #endif 1799