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