diff --git a/PendulumC++/GmshSocket.h b/PendulumC++/GmshSocket.h new file mode 100644 index 0000000000000000000000000000000000000000..4f6129ff8ceac515033b6df462ddb48665a4b8e2 --- /dev/null +++ b/PendulumC++/GmshSocket.h @@ -0,0 +1,473 @@ +// Gmsh - Copyright (C) 1997-2018 C. Geuzaine, J.-F. Remacle +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, and/or sell copies of the +// Software, and to permit persons to whom the Software is furnished +// to do so, provided that the above copyright notice(s) and this +// permission notice appear in all copies of the Software and that +// both the above copyright notice(s) and this permission notice +// appear in supporting documentation. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE +// COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR +// ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY +// DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +// OF THIS SOFTWARE. +// +// Please report all bugs and problems to the public mailing list +// <gmsh@onelab.info>. + +#ifndef _GMSH_SOCKET_H_ +#define _GMSH_SOCKET_H_ + +//#include "GmshConfig.h" + +#include <string> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined(_AIX) +#include <strings.h> +#endif + +#if !defined(WIN32) || defined(__CYGWIN__) +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#if defined(HAVE_NO_SOCKLEN_T) +typedef int socklen_t; +#endif +#else +#include <winsock.h> +#include <process.h> +typedef int socklen_t; +#endif + +class GmshSocket{ + public: + // types of messages that can be exchanged (never use values greater + // that 65535: if we receive a type > 65535 we assume that we + // receive data from a machine with a different byte ordering, and + // we swap the bytes in the payload) + enum MessageType{ + GMSH_START = 1, + GMSH_STOP = 2, + GMSH_INFO = 10, + GMSH_WARNING = 11, + GMSH_ERROR = 12, + GMSH_PROGRESS = 13, + GMSH_MERGE_FILE = 20, + GMSH_PARSE_STRING = 21, + GMSH_VERTEX_ARRAY = 22, + GMSH_PARAMETER = 23, + GMSH_PARAMETER_QUERY = 24, + GMSH_PARAMETER_QUERY_ALL = 25, + GMSH_PARAMETER_QUERY_END = 26, + GMSH_CONNECT = 27, + GMSH_OLPARSE = 28, + GMSH_PARAMETER_NOT_FOUND = 29, + GMSH_SPEED_TEST = 30, + GMSH_PARAMETER_CLEAR = 31, + GMSH_PARAMETER_UPDATE = 32, + GMSH_OPEN_PROJECT = 33, + GMSH_CLIENT_CHANGED = 34, + GMSH_PARAMETER_WITHOUT_CHOICES = 35, + GMSH_PARAMETER_QUERY_WITHOUT_CHOICES = 36, + GMSH_OPTION_1 = 100, + GMSH_OPTION_2 = 101, + GMSH_OPTION_3 = 102, + GMSH_OPTION_4 = 103, + GMSH_OPTION_5 = 104}; + protected: + // the socket descriptor + int _sock; + // the socket name + std::string _sockname; + // statistics + unsigned long int _sent, _received; + // send some data over the socket + int _SendData(const void *buffer, int bytes) + { + const char *buf = (const char *)buffer; + long int sofar = 0; + long int remaining = bytes; + do { + long int len = send(_sock, buf + sofar, remaining, 0); + if(len < 0) return -1; // error + sofar += len; + remaining -= len; + } while(remaining > 0); + _sent += bytes; + return bytes; + } + // receive some data over the socket + int _ReceiveData(void *buffer, int bytes) + { + char *buf = (char *)buffer; + long int sofar = 0; + long int remaining = bytes; + do { + long int len = recv(_sock, buf + sofar, remaining, 0); + if(len == 0) break; // we're done! + if(len < 0) return -1; // error + sofar += len; + remaining -= len; + } while(remaining > 0); + _received += bytes; + return bytes; + } + // utility function to swap bytes in an array + void _SwapBytes(char *array, int size, int n) + { + char *x = new char[size]; + for(int i = 0; i < n; i++) { + char *a = &array[i * size]; + memcpy(x, a, size); + for(int c = 0; c < size; c++) + a[size - 1 - c] = x[c]; + } + delete [] x; + } + // sleep for some milliseconds + void _Sleep(int ms) + { +#if !defined(WIN32) || defined(__CYGWIN__) + usleep(1000 * ms); +#else + Sleep(ms); +#endif + } + public: + GmshSocket() : _sock(0), _sent(0), _received(0) + { +#if defined(WIN32) && !defined(__CYGWIN__) + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); +#endif + } + ~GmshSocket() + { +#if defined(WIN32) && !defined(__CYGWIN__) + WSACleanup(); +#endif + } + // Wait for some data to read on the socket (if seconds and microseconds == 0 + // we check for available data and return immediately, i.e., we do + // polling). Returns 1 when data is available, 0 when nothing happened before + // the time delay, -1 on error. + int Select(int seconds, int microseconds, int socket=-1) + { + int s = (socket < 0) ? _sock : socket; + struct timeval tv; + tv.tv_sec = seconds; + tv.tv_usec = microseconds; + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(s, &rfds); + // select checks all IO descriptors between 0 and its first arg, minus 1; + // hence the +1 below + return select(s + 1, &rfds, NULL, NULL, &tv); + } + void SendMessage(int type, int length, const void *msg) + { + // send header (type + length) + _SendData(&type, sizeof(int)); + _SendData(&length, sizeof(int)); + // send body + _SendData(msg, length); + } + void SendString(int type, const char *str) + { + SendMessage(type, strlen(str), str); + } + void Info(const char *str){ SendString(GMSH_INFO, str); } + void Warning(const char *str){ SendString(GMSH_WARNING, str); } + void Error(const char *str){ SendString(GMSH_ERROR, str); } + void Progress(const char *str){ SendString(GMSH_PROGRESS, str); } + void MergeFile(const char *str){ SendString(GMSH_MERGE_FILE, str); } + void OpenProject(const char *str){ SendString(GMSH_OPEN_PROJECT, str); } + void ParseString(const char *str){ SendString(GMSH_PARSE_STRING, str); } + void SpeedTest(const char *str){ SendString(GMSH_SPEED_TEST, str); } + void Option(int num, const char *str) + { + if(num < 1) num = 1; + if(num > 5) num = 5; + SendString(GMSH_OPTION_1 + num - 1, str); + } + int ReceiveHeader(int *type, int *len, int *swap) + { + *swap = 0; + if(_ReceiveData(type, sizeof(int)) > 0){ + if(*type > 65535){ + // the data comes from a machine with different endianness and + // we must swap the bytes + *swap = 1; + _SwapBytes((char*)type, sizeof(int), 1); + } + if(_ReceiveData(len, sizeof(int)) > 0){ + if(*swap) _SwapBytes((char*)len, sizeof(int), 1); + return 1; + } + } + return 0; + } + int ReceiveMessage(int len, void *buffer) + { + if(_ReceiveData(buffer, len) == len) return 1; + return 0; + } + // str should be allocated with size (len+1) + int ReceiveString(int len, char *str) + { + if(_ReceiveData(str, len) == len) { + str[len] = '\0'; + return 1; + } + return 0; + } + void CloseSocket(int s) + { +#if !defined(WIN32) || defined(__CYGWIN__) + close(s); +#else + closesocket(s); +#endif + } + void ShutdownSocket(int s) + { +#if !defined(WIN32) || defined(__CYGWIN__) + shutdown(s, SHUT_RDWR); +#endif + } + unsigned long int SentBytes(){ return _sent; } + unsigned long int ReceivedBytes(){ return _received; } +}; + +class GmshClient : public GmshSocket { + public: + GmshClient() : GmshSocket() {} + ~GmshClient(){} + int Connect(const char *sockname) + { + if(strstr(sockname, "/") || strstr(sockname, "\\") || !strstr(sockname, ":")){ +#if !defined(WIN32) || defined(__CYGWIN__) + // UNIX socket (testing ":" is not enough with Windows paths) + _sock = socket(PF_UNIX, SOCK_STREAM, 0); + if(_sock < 0) return -1; + // try to connect socket to given name + struct sockaddr_un addr_un; + memset((char *) &addr_un, 0, sizeof(addr_un)); + addr_un.sun_family = AF_UNIX; + strcpy(addr_un.sun_path, sockname); + for(int tries = 0; tries < 5; tries++) { + if(connect(_sock, (struct sockaddr *)&addr_un, sizeof(addr_un)) >= 0) + return _sock; + _Sleep(100); + } +#else + return -1; // Unix sockets are not available on Windows +#endif + } + else{ + // TCP/IP socket + _sock = socket(AF_INET, SOCK_STREAM, 0); + if(_sock < 0) return -1; + char one = 1; + // disable Nagle's algorithm (very slow for many small messages) + setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); + // try to connect socket to host:port + const char *port = strstr(sockname, ":"); + int portno = atoi(port + 1); + int remotelen = strlen(sockname) - strlen(port); + char remote[256]; + if(remotelen > 0) + strncpy(remote, sockname, remotelen); + remote[remotelen] = '\0'; + struct hostent *server; + if(!(server = gethostbyname(remote))){ + CloseSocket(_sock); + return -3; // no such host + } + struct sockaddr_in addr_in; + memset((char *) &addr_in, 0, sizeof(addr_in)); + addr_in.sin_family = AF_INET; + memcpy((char *)&addr_in.sin_addr.s_addr, (char *)server->h_addr, server->h_length); + addr_in.sin_port = htons(portno); + for(int tries = 0; tries < 5; tries++) { + if(connect(_sock, (struct sockaddr *)&addr_in, sizeof(addr_in)) >= 0){ + return _sock; + } + _Sleep(100); + } + } + CloseSocket(_sock); + return -2; // couldn't connect + } + void Start() + { + char tmp[256]; +#if !defined(WIN32) || defined(__CYGWIN__) + sprintf(tmp, "%d", getpid()); +#else + sprintf(tmp, "%d", _getpid()); +#endif + SendString(GMSH_START, tmp); + } + void Stop(){ SendString(GMSH_STOP, "Goodbye!"); } + void Disconnect(){ CloseSocket(_sock); } +}; + +class GmshServer : public GmshSocket{ + private: + int _portno; + public: + GmshServer() : GmshSocket(), _portno(-1) {} + virtual ~GmshServer(){} + virtual int NonBlockingSystemCall(const std::string &exe, const std::string &args) = 0; + virtual int NonBlockingWait(double waitint, double timeout, int socket=-1) = 0; + // start the client by launching "exe args" (args is supposed to contain + // '%s' where the socket name should appear) + int Start(const std::string &exe, const std::string &args, const std::string &sockname, + double timeout) + { + _sockname = sockname; + int tmpsock; + if(strstr(_sockname.c_str(), "/") || strstr(_sockname.c_str(), "\\") || + !strstr(_sockname.c_str(), ":")){ + // UNIX socket (testing ":" is not enough with Windows paths) + _portno = -1; +#if !defined(WIN32) || defined(__CYGWIN__) + // delete the file if it already exists + unlink(_sockname.c_str()); + // create a socket + tmpsock = socket(PF_UNIX, SOCK_STREAM, 0); + if(tmpsock < 0) throw "Couldn't create socket"; + // bind the socket to its name + struct sockaddr_un addr_un; + memset((char *) &addr_un, 0, sizeof(addr_un)); + strcpy(addr_un.sun_path, _sockname.c_str()); + addr_un.sun_family = AF_UNIX; + if(bind(tmpsock, (struct sockaddr *)&addr_un, sizeof(addr_un)) < 0){ + CloseSocket(tmpsock); + throw "Couldn't bind socket to name"; + } + // change permissions on the socket name in case it has to be rm'd later + chmod(_sockname.c_str(), 0666); +#else + throw "Unix sockets not available on Windows"; +#endif + } + else{ + // TCP/IP socket: valid names are either explicit ("hostname:12345") + // or implicit ("hostname:", "hostname: ", "hostname:0") in which case + // the system attributes at random an available port + const char *port = strstr(_sockname.c_str(), ":"); + _portno = atoi(port + 1); + // create a socket + tmpsock = socket(AF_INET, SOCK_STREAM, 0); + char one = 1; + setsockopt(tmpsock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); + +#if !defined(WIN32) || defined(__CYGWIN__) + if(tmpsock < 0) +#else + if(tmpsock == (int)INVALID_SOCKET) +#endif + throw "Couldn't create socket"; + // bind the socket to its name + struct sockaddr_in addr_in; + memset((char *) &addr_in, 0, sizeof(addr_in)); + addr_in.sin_family = AF_INET; + addr_in.sin_addr.s_addr = INADDR_ANY; + addr_in.sin_port = htons(_portno); // random assign if _portno == 0 + if(bind(tmpsock, (struct sockaddr *)&addr_in, sizeof(addr_in)) < 0){ + CloseSocket(tmpsock); + throw "Couldn't bind socket to name"; + } + if(!_portno){ // retrieve name if randomly assigned port + socklen_t addrlen = sizeof(addr_in); + getsockname(tmpsock, (struct sockaddr *)&addr_in, &addrlen); + _portno = ntohs(addr_in.sin_port); + int pos = _sockname.find(':'); // remove trailing ' ' or '0' + char tmp[256]; + sprintf(tmp, "%s:%d", _sockname.substr(0, pos).c_str(), _portno); + _sockname.assign(tmp); + } + } + + if(exe.size() || args.size()){ + char s[1024]; + sprintf(s, args.c_str(), _sockname.c_str()); + NonBlockingSystemCall(exe, s); // starts the solver + } + else{ + timeout = 0.; // no command launched: don't set a timeout + } + + // listen on socket (queue up to 20 connections before having + // them automatically rejected) + if(listen(tmpsock, 20)){ + CloseSocket(tmpsock); + throw "Socket listen failed"; + } + + // wait until we get data + int ret = NonBlockingWait(0.001, timeout, tmpsock); + if(ret){ + CloseSocket(tmpsock); + if(ret == 2){ + throw "Socket listening timeout"; + } + else{ + return -1; // stopped listening + } + } + + // accept connection request + if(_portno < 0){ +#if !defined(WIN32) || defined(__CYGWIN__) + struct sockaddr_un from_un; + socklen_t len = sizeof(from_un); + _sock = accept(tmpsock, (struct sockaddr *)&from_un, &len); +#endif + } + else{ + struct sockaddr_in from_in; + socklen_t len = sizeof(from_in); + _sock = accept(tmpsock, (struct sockaddr *)&from_in, &len); + char one = 1; + setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); + } + CloseSocket(tmpsock); + if(_sock < 0) + throw "Socket accept failed"; + return _sock; + } + int Shutdown() + { +#if !defined(WIN32) || defined(__CYGWIN__) + if(_portno < 0) + unlink(_sockname.c_str()); +#endif + ShutdownSocket(_sock); + CloseSocket(_sock); + return 0; + } +}; + +#endif diff --git a/PendulumC++/onelab.h b/PendulumC++/onelab.h new file mode 100644 index 0000000000000000000000000000000000000000..1bf5c359882eb58674393bdc6fd7841a4649c6b6 --- /dev/null +++ b/PendulumC++/onelab.h @@ -0,0 +1,1578 @@ +// ONELAB - Copyright (C) 2011-2016 ULg-UCL +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, and/or sell copies of the +// Software, and to permit persons to whom the Software is furnished +// to do so, provided that the above copyright notice(s) and this +// permission notice appear in all copies of the Software and that +// both the above copyright notice(s) and this permission notice +// appear in supporting documentation. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE +// COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR +// ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY +// DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +// ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +// OF THIS SOFTWARE. +// +// Please report all bugs and problems to the public mailing list +// <gmsh@onelab.info>. + +#ifndef _ONELAB_H_ +#define _ONELAB_H_ + +#include <time.h> +#include <stdio.h> +#include <string> +#include <vector> +#include <set> +#include <map> +#include <iostream> +#include <algorithm> +#include <sstream> +#include "GmshSocket.h" + +//#define HAVE_PICOJSON +#if defined(HAVE_PICOJSON) +#include "picojson.h" +#endif + +namespace onelab{ + + // The base parameter class. + class parameter { + private: + // the name of the parameter, including its '/'-separated path in the + // parameter hierarchy. Parameters or subpaths can start with numbers to + // force their relative ordering (such numbers are automatically hidden in + // the interface). All strings in onelab are supposed to be UTF8-encoded. + std::string _name; + // the parameter label: if provided it serves as a better way to display the + // parameter in the interface + std::string _label; + // a help string + std::string _help; + // map of clients that use this parameter, associated with a "changed" flag + // (set to 0 if the client has already been run with the current value of + // the parameter; set to defaultChangedValue() when the value of a parameter + // has been changed; values between 1 and defaultChangedValue() can be used + // to "modulate" the degree of change, e.g. to change the action to be + // performed depending on the "severity" of the change) + std::map<std::string, int> _clients; + // flag indicating what the "changed" value should be set to when a + // parameter is updated to a different value (if set to 0, the parameter + // will appear as never being changed) + int _changedValue; + // should the parameter be visible in the interface? + bool _visible; + // sould the paramete be "read-only" (not settable by the user) + bool _readOnly; + protected: + // optional additional attributes + std::map<std::string, std::string> _attributes; + public: + parameter(const std::string &name="", const std::string &label="", + const std::string &help="") + : _name(name), _label(label), _help(help), _visible(true), + _readOnly(false) + { + _changedValue = defaultChangedValue(); + } + virtual ~parameter(){} + void setName(const std::string &name){ _name = name; } + void setLabel(const std::string &label){ _label = label; } + void setHelp(const std::string &help){ _help = help; } + void setChanged(int changed, const std::string &client="") + { + if(client.size()){ + std::map<std::string, int>::iterator it = _clients.find(client); + if(it != _clients.end()) it->second = changed; + } + else{ + for(std::map<std::string, int>::iterator it = _clients.begin(); + it != _clients.end(); it++) + it->second = changed; + } + } + void setChangedValue(int value){ _changedValue = value; } + void setNeverChanged(bool never) + { + _changedValue = never ? 0 : defaultChangedValue(); + } + void setVisible(bool visible){ _visible = visible; } + void setReadOnly(bool readOnly){ _readOnly = readOnly; } + void setAttribute(const std::string &key, const std::string &value) + { + _attributes[key] = value; + } + void setAttributes(const std::map<std::string, std::string> &attributes) + { + _attributes = attributes; + } + void setClients(const std::map<std::string, int> &clients){ _clients = clients; } + void addClient(const std::string &client, int changed) + { + if(_clients.find(client) == _clients.end()) + _clients[client] = changed; + } + void addClients(const std::map<std::string, int> &clients) + { + _clients.insert(clients.begin(), clients.end()); + } + bool hasClient(const std::string &client) + { + return (_clients.find(client) != _clients.end()); + } + int getNumClients() { return (int)_clients.size(); }; + virtual std::string getType() const = 0; + const std::string &getName() const { return _name; } + const std::string &getLabel() const { return _label; } + const std::string &getHelp() const { return _help; } + std::string getPath() const + { + std::string::size_type last = _name.find_last_of('/'); + return _name.substr(0, last); + } + std::string getShortName() const + { + std::string units = getAttribute("Units"); + if(_label.size()){ + if(units.empty()) + return _label; + else + return _label + " [" + units + "]"; + } + std::string s = _name; + // remove path + std::string::size_type last = _name.find_last_of('/'); + if(last != std::string::npos) + s = _name.substr(last + 1); + // remove starting white space + while(s.size() && s[0] == ' ') + s = s.substr(1); + // remove starting braces: can be used to order parameters 'from the end', + // as the ASCII code is after numbers and letters + while(s.size() && (s[0] == '}' || s[0] == '{')) + s = s.substr(1); + // remove starting numbers: can be used to order parameters 'from the + // start' + while(s.size() && s[0] >= '0' && s[0] <= '9') + s = s.substr(1); + if(units.empty()) + return s; + else + return s + " [" + units + "]"; + } + int getChanged(const std::string &client="") const + { + if(client.size()){ + std::map<std::string, int>::const_iterator it = _clients.find(client); + if(it != _clients.end()) return it->second; + else return 0; + } + else{ + int changed = 0; + for(std::map<std::string, int>::const_iterator it = _clients.begin(); + it != _clients.end(); it++){ + changed = std::max(changed, it->second); + } + return changed; + } + } + int getChangedValue() const { return _changedValue; } + bool getNeverChanged() const { return _changedValue ? false : true; } + bool getVisible() const { return _visible; } + bool getReadOnly() const { return _readOnly; } + std::string getAttribute(const std::string &key) const + { + std::map<std::string, std::string>::const_iterator it = _attributes.find(key); + if(it != _attributes.end()) return it->second; + return ""; + } + const std::map<std::string, std::string> &getAttributes() const + { + return _attributes; + } + const std::map<std::string, int> &getClients() const { return _clients; } + static char charSep() { return '\0'; } + static double maxNumber() { return 1e200; } + static std::string version() { return "1.3"; } + static int defaultChangedValue() { return 31; } + static std::string getNextToken(const std::string &msg, + std::string::size_type &first, + char separator=charSep()) + { + if(first == std::string::npos) return ""; + std::string::size_type last = msg.find_first_of(separator, first); + std::string next(""); + if(last == std::string::npos){ + next = msg.substr(first); + first = last; + } + else if(first == last){ + next = ""; + first = last + 1; + } + else{ + next = msg.substr(first, last - first); + first = last + 1; + } + return next; + } + static std::vector<std::string> split(const std::string &msg, + char separator=charSep()) + { + std::vector<std::string> out; + std::string::size_type first = 0; + while(first != std::string::npos) + out.push_back(getNextToken(msg, first, separator)); + return out; + } + static std::string trim(const std::string &str, + const std::string &whitespace = " \t\n") + { + std::string::size_type strBegin = str.find_first_not_of(whitespace); + if(strBegin == std::string::npos) + return ""; // no content + std::string::size_type strEnd = str.find_last_not_of(whitespace); + std::string::size_type strRange = strEnd - strBegin + 1; + return str.substr(strBegin, strRange); + } + std::string sanitize(const std::string &in) const + { + std::string out(in); + for(unsigned int i = 0; i < in.size(); i++) + if(out[i] == charSep()) out[i] = ' '; + return out; + } + virtual std::string toChar() const + { + std::ostringstream sstream; + sstream << version() << charSep() << getType() << charSep() + << sanitize(getName()) << charSep() + << sanitize(getLabel()) << charSep() + << sanitize(getHelp()) << charSep() + << getChangedValue() << charSep() + << (getVisible() ? 1 : 0) << charSep() + << (getReadOnly() ? 1 : 0) << charSep() + << _attributes.size() << charSep(); + for(std::map<std::string, std::string>::const_iterator it = _attributes.begin(); + it != _attributes.end(); it++) + sstream << sanitize(it->first) << charSep() + << sanitize(it->second) << charSep(); + sstream << getClients().size() << charSep(); + for(std::map<std::string, int>::const_iterator it = getClients().begin(); + it != getClients().end(); it++) + sstream << sanitize(it->first) << charSep() + << (it->second ? 1 : 0) << charSep(); + return sstream.str(); + } + virtual std::string::size_type fromChar(const std::string &msg) + { + std::string::size_type pos = 0; + if(getNextToken(msg, pos) != version()) return 0; + if(getNextToken(msg, pos) != getType()) return 0; + setName(getNextToken(msg, pos)); + setLabel(getNextToken(msg, pos)); + setHelp(getNextToken(msg, pos)); + setChangedValue(atoi(getNextToken(msg, pos).c_str())); + setVisible(atoi(getNextToken(msg, pos).c_str())); + setReadOnly(atoi(getNextToken(msg, pos).c_str())); + int numAttributes = atoi(getNextToken(msg, pos).c_str()); + for(int i = 0; i < numAttributes; i++){ + std::string key(getNextToken(msg, pos)); + setAttribute(key, getNextToken(msg, pos)); + } + int numClients = atoi(getNextToken(msg, pos).c_str()); + for(int i = 0; i < numClients; i++){ + std::string client(getNextToken(msg, pos)); + int changed = atoi(getNextToken(msg, pos).c_str()); + addClient(client, changed); + } + return pos; + } + static void getInfoFromChar(const std::string &msg, std::string &version, + std::string &type, std::string &name) + { + std::string::size_type first = 0; + version = getNextToken(msg, first); + type = getNextToken(msg, first); + name = getNextToken(msg, first); + } + static bool fromFile(std::vector<std::string> &msg, FILE *fp) + { + msg.clear(); + char tmp[1000]; + if(!fgets(tmp, sizeof(tmp), fp)) return false; // first line is comment + while(!feof(fp)){ + int numc = 0; + if(!fscanf(fp, "%d ", &numc)) break; // space is important + if(!numc) break; + msg.push_back(""); + for(int i = 0; i < numc; i++) + msg.back() += fgetc(fp); + if(!fgets(tmp, sizeof(tmp), fp)) break; // end of line + } + return true; + } + static bool toFile(const std::vector<std::string> &msg, FILE *fp, + const std::string &creator) + { + time_t now; + time(&now); + fprintf(fp, "ONELAB database created by %s on %s", + creator.c_str(), ctime(&now)); + for(unsigned int i = 0; i < msg.size(); i++){ + fprintf(fp, "%d ", (int)msg[i].size()); + for(unsigned int j = 0; j < msg[i].size(); j++) + fputc(msg[i][j], fp); + fputc('\n', fp); + } + return true; + } + std::string sanitizeJSON(const std::string &in) const + { + // FIXME: replace \n with \\n, \t with \\t, etc. + return in; + } + virtual std::string toJSON() const + { + std::ostringstream sstream; + sstream << "\"type\":\"" << getType() << "\"" + << ", \"name\":\"" << sanitizeJSON(getName()) << "\""; + if(getLabel().size()) + sstream << ", \"label\":\"" << sanitizeJSON(getLabel()) << "\""; + if(getHelp().size()) + sstream << ", \"help\":\"" << sanitizeJSON(getHelp()) << "\""; + sstream << ", \"changedValue\":" << getChangedValue() + << ", \"visible\":" << (getVisible() ? "true" : "false") + << ", \"readOnly\":" << (getReadOnly() ? "true" : "false"); + if(_attributes.size()){ + sstream << ", \"attributes\":{ "; + for(std::map<std::string, std::string>::const_iterator it = _attributes.begin(); + it != _attributes.end(); it++){ + if(it != _attributes.begin()) sstream << ", "; + sstream << "\"" << sanitizeJSON(it->first) << "\":\"" + << sanitizeJSON(it->second) << "\""; + } + sstream << " }"; + } + if(getClients().size()){ + sstream << ", \"clients\":{ "; + for(std::map<std::string, int>::const_iterator it = getClients().begin(); + it != getClients().end(); it++){ + if(it != getClients().begin()) sstream << ", "; + sstream << "\"" << sanitizeJSON(it->first) << "\":" + << it->second; + } + sstream << " }"; + } + return sstream.str(); + } +#if defined(HAVE_PICOJSON) + virtual bool fromJSON(const picojson::value::object& par) + { + for(picojson::value::object::const_iterator it = par.begin(); it != par.end(); ++it){ + if(it->first == "name"){ + if(!it->second.is<std::string>()) return false; + setName(it->second.get<std::string>()); + } + else if(it->first == "label"){ + if(!it->second.is<std::string>()) return false; + setLabel(it->second.get<std::string>()); + } + else if(it->first == "help"){ + if(!it->second.is<std::string>()) return false; + setHelp(it->second.get<std::string>()); + } + else if(it->first == "changedValue"){ + if(!it->second.is<double>()) return false; + setChangedValue((int)it->second.get<double>()); + } + else if(it->first == "visible"){ + if(!it->second.is<bool>()) return false; + setVisible(it->second.get<bool>()); + } + else if(it->first == "readOnly"){ + if(!it->second.is<bool>()) return false; + setReadOnly(it->second.get<bool>()); + } + else if(it->first == "attributes"){ + if(!it->second.is<picojson::object>()) return false; + const picojson::value::object &obj = it->second.get<picojson::object>(); + for (picojson::value::object::const_iterator i = obj.begin(); i != obj.end(); ++i) { + std::string key(i->first); + if(!i->second.is<std::string>()) return false; + setAttribute(key, i->second.get<std::string>()); + } + } + else if(it->first == "clients"){ + if(!it->second.is<picojson::object>()) return false; + const picojson::value::object &obj = it->second.get<picojson::object>(); + for (picojson::value::object::const_iterator i = obj.begin(); i != obj.end(); ++i) { + std::string client(i->first); + if(!i->second.is<double>()) return false; + addClient(client, (int)i->second.get<double>()); + } + } + } + return true; + } +#endif + }; + + class parameterLessThan{ + public: + bool operator()(const parameter *p1, const parameter *p2) const + { + return p1->getName() < p2->getName(); + } + }; + + // The number class. Numbers are stored internally as double precision real + // numbers. + class number : public parameter{ + private: + std::vector<double> _values, _choices; + double _min, _max, _step; + // when in a loop, indicates current index in the vector _choices; is -1 + // when not in a loop + int _index; + std::map<double, std::string> _valueLabels; + public: + number(const std::string &name="", double value=0., + const std::string &label="", const std::string &help="") + : parameter(name, label, help), _values(std::vector<double>(1, value)), + _min(-maxNumber()), _max(maxNumber()), _step(0.), _index(-1){} + number(const std::string &name, const std::vector<double> &values, + const std::string &label="", const std::string &help="") + : parameter(name, label, help), _values(values), + _min(-maxNumber()), _max(maxNumber()), _step(0.), _index(-1){} + void setValue(double value){ _values.resize(1); _values[0] = value; } + void setValues(const std::vector<double> &values){ _values = values; } + void setMin(double min){ _min = min; } + void setMax(double max){ _max = max; } + void setStep(double step){ _step = step; } + void setIndex(int index){ _index = index; } + void setChoices(const std::vector<double> &choices){ _choices = choices; } + void setChoiceLabels(const std::vector<std::string> &labels) + { + if(labels.size() != _choices.size()) return; + if(_valueLabels.size()) _valueLabels.clear(); + for(unsigned int i = 0; i < _choices.size(); i++) + _valueLabels[_choices[i]] = labels[i]; + } + void setValueLabels(const std::map<double, std::string> &valueLabels) + { + _valueLabels = valueLabels; + } + void setValueLabel(double value, const std::string &label) + { + _valueLabels[value] = label; + } + std::string getType() const { return "number"; } + double getValue() const { if(_values.empty()) return 0.; return _values[0]; } + const std::vector<double> &getValues() const { return _values; } + unsigned int getNumValues() const { return _values.size(); } + double getMin() const { return _min; } + double getMax() const { return _max; } + double getStep() const { return _step; } + int getIndex() const { return _index; } + const std::vector<double> &getChoices() const { return _choices; } + const std::map<double, std::string> &getValueLabels() const + { + return _valueLabels; + } + std::string getValueLabel(double value) const + { + std::map<double, std::string>::const_iterator it = _valueLabels.find(value); + if(it != _valueLabels.end()) return it->second; + return ""; + } + void update(const number &p) + { + addClients(p.getClients()); + setLabel(p.getLabel()); + setHelp(p.getHelp()); + setVisible(p.getVisible()); + setReadOnly(p.getReadOnly()); + setAttributes(p.getAttributes()); + if(p.getValue() != getValue()){ // FIXME change this + setValues(p.getValues()); + setChanged(getChangedValue()); + } + setMin(p.getMin()); + setMax(p.getMax()); + setStep(p.getStep()); + setIndex(p.getIndex()); + setChoices(p.getChoices()); + setValueLabels(p.getValueLabels()); + if(getNeverChanged()) setChanged(0); + } + std::string toChar() const + { + std::ostringstream sstream; + sstream.precision(16); + sstream << parameter::toChar() + << _values.size() << charSep(); + for(unsigned int i = 0; i < _values.size(); i++) + sstream << _values[i] << charSep(); + sstream << _min << charSep() + << _max << charSep() + << _step << charSep() + << _index << charSep() + << _choices.size() << charSep(); + for(unsigned int i = 0; i < _choices.size(); i++) + sstream << _choices[i] << charSep(); + sstream << _valueLabels.size() << charSep(); + for(std::map<double, std::string>::const_iterator it = _valueLabels.begin(); + it != _valueLabels.end(); it++){ + sstream << it->first << charSep() + << sanitize(it->second) << charSep(); + } + return sstream.str(); + } + std::string::size_type fromChar(const std::string &msg) + { + std::string::size_type pos = parameter::fromChar(msg); + if(!pos) return 0; + _values.resize(atoi(getNextToken(msg, pos).c_str())); + for(unsigned int i = 0; i < _values.size(); i++) + _values[i] = atof(getNextToken(msg, pos).c_str()); + setMin(atof(getNextToken(msg, pos).c_str())); + setMax(atof(getNextToken(msg, pos).c_str())); + setStep(atof(getNextToken(msg, pos).c_str())); + setIndex(atoi(getNextToken(msg, pos).c_str())); + _choices.resize(atoi(getNextToken(msg, pos).c_str())); + for(unsigned int i = 0; i < _choices.size(); i++) + _choices[i] = atof(getNextToken(msg, pos).c_str()); + int numValueLabels = atoi(getNextToken(msg, pos).c_str()); + for(int i = 0; i < numValueLabels; i++){ + double value = atof(getNextToken(msg, pos).c_str()); + _valueLabels[value] = getNextToken(msg, pos); + } + return pos; + } + std::string toJSON() const + { + std::ostringstream sstream; + sstream.precision(16); + sstream << "{ " << parameter::toJSON() + << ", \"values\":[ "; + for(unsigned int i = 0; i < _values.size(); i++){ + if(i) sstream << ", "; + sstream << _values[i]; + } + sstream << " ]" + << ", \"min\":" << _min + << ", \"max\":" << _max + << ", \"step\":" << _step + << ", \"index\":" << _index; + if(_choices.size()){ + sstream << ", \"choices\":[ "; + for(unsigned int i = 0; i < _choices.size(); i++){ + if(i) sstream << ", "; + sstream << _choices[i]; + } + sstream << " ]"; + } + if(_valueLabels.size()){ + sstream << ", \"valueLabels\":{ "; + for(std::map<double, std::string>::const_iterator it = _valueLabels.begin(); + it != _valueLabels.end(); it++){ + if(it != _valueLabels.begin()) sstream << ", "; + sstream << "\"" << sanitizeJSON(it->second) << "\":" << it->first; + } + sstream << " }"; + } + sstream << " }"; + return sstream.str(); + } +#if defined(HAVE_PICOJSON) + bool fromJSON(const picojson::value::object& par) + { + if(!parameter::fromJSON(par)) return false; + for(picojson::value::object::const_iterator it = par.begin(); it != par.end(); ++it){ + if(it->first == "values"){ + if(!it->second.is<picojson::array>()) return false; + const picojson::value::array &arr = it->second.get<picojson::array>(); + _values.resize(arr.size()); + for(unsigned int i = 0; i < arr.size(); i++){ + if(!arr[i].is<double>()) return false; + _values[i] = arr[i].get<double>(); + } + } + else if(it->first == "min"){ + if(!it->second.is<double>()) return false; + setMin(it->second.get<double>()); + } + else if(it->first == "max"){ + if(!it->second.is<double>()) return false; + setMax(it->second.get<double>()); + } + else if(it->first == "step"){ + if(!it->second.is<double>()) return false; + setStep(it->second.get<double>()); + } + else if(it->first == "index"){ + if(!it->second.is<double>()) return false; + setIndex((int)it->second.get<double>()); + } + else if(it->first == "choices"){ + if(!it->second.is<picojson::array>()) return false; + const picojson::value::array &arr = it->second.get<picojson::array>(); + _choices.resize(arr.size()); + for(unsigned int i = 0; i < arr.size(); i++){ + if(!arr[i].is<double>()) return false; + _choices[i] = arr[i].get<double>(); + } + } + else if(it->first == "valueLabels"){ + if(!it->second.is<picojson::object>()) return false; + const picojson::value::object &obj = it->second.get<picojson::object>(); + for (picojson::value::object::const_iterator i = obj.begin(); i != obj.end(); ++i) { + if(!i->second.is<double>()) return false; + _valueLabels[i->second.get<double>()] = i->first; + } + } + } + return true; + } +#endif + }; + + // The string class. A string has a mutable "kind", that can be changed at + // runtime. Kinds leading to specific behavior in Gmsh are: "file". + class string : public parameter{ + private: + std::vector<std::string> _values, _choices; + std::string _kind; + public: + string(const std::string &name="", const std::string &value="", + const std::string &label="", const std::string &help="") + : parameter(name, label, help), _values(std::vector<std::string>(1, value)), + _kind("generic") {} + string(const std::string &name, const std::vector<std::string> &values, + const std::string &label="", const std::string &help="") + : parameter(name, label, help), _values(values), + _kind("generic") {} + void setValue(const std::string &value){ _values.resize(1); _values[0] = value; } + void setValues(const std::vector<std::string> &values){ _values = values; } + void setKind(const std::string &kind){ _kind = kind; } + void setChoices(const std::vector<std::string> &choices){ _choices = choices; } + std::string getType() const { return "string"; } + const std::string &getValue() const + { + static std::string n(""); + if(_values.empty()) return n; return _values[0]; + } + const std::vector<std::string> &getValues() const { return _values; } + unsigned int getNumValues() const { return _values.size(); } + const std::string &getKind() const { return _kind; } + const std::vector<std::string> &getChoices() const { return _choices; } + void update(const string &p) + { + addClients(p.getClients()); + setLabel(p.getLabel()); + setHelp(p.getHelp()); + setVisible(p.getVisible()); + setReadOnly(p.getReadOnly()); + setAttributes(p.getAttributes()); + if(p.getValue() != getValue()){ // FIXME: change this + setValues(p.getValues()); + setChanged(getChangedValue()); + } + if(p.getKind() != getKind()){ + setKind(p.getKind()); + setChanged(getChangedValue()); + } + setChoices(p.getChoices()); + if(getNeverChanged()) setChanged(0); + } + std::string toChar() const + { + std::ostringstream sstream; + sstream << parameter::toChar() + << _values.size() << charSep(); + for(unsigned int i = 0; i < _values.size(); i++) + sstream << sanitize(_values[i]) << charSep(); + sstream << sanitize(_kind) << charSep() + << _choices.size() << charSep(); + for(unsigned int i = 0; i < _choices.size(); i++) + sstream << sanitize(_choices[i]) << charSep(); + return sstream.str(); + } + std::string::size_type fromChar(const std::string &msg) + { + std::string::size_type pos = parameter::fromChar(msg); + if(!pos) return 0; + _values.resize(atoi(getNextToken(msg, pos).c_str())); + for(unsigned int i = 0; i < _values.size(); i++) + _values[i] = getNextToken(msg, pos); + setKind(getNextToken(msg, pos)); + _choices.resize(atoi(getNextToken(msg, pos).c_str())); + for(unsigned int i = 0; i < _choices.size(); i++) + _choices[i] = getNextToken(msg, pos); + return pos; + } + std::string toJSON() const + { + std::ostringstream sstream; + sstream << "{ " << parameter::toJSON() + << ", \"values\":[ " ; + for(unsigned int i = 0; i < _values.size(); i++){ + if(i) sstream << ", "; + sstream << "\"" << sanitizeJSON(_values[i]) << "\""; + } + sstream << " ] "; + if(_kind.size()) + sstream << ", \"kind\":\"" << sanitizeJSON(_kind) << "\""; + if(_choices.size()){ + sstream << ", \"choices\":[ "; + for(unsigned int i = 0; i < _choices.size(); i++){ + if(i) sstream << ", "; + sstream << "\"" << sanitizeJSON(_choices[i]) << "\""; + } + sstream << " ]"; + } + sstream << " }"; + return sstream.str(); + } +#if defined(HAVE_PICOJSON) + bool fromJSON(const picojson::value::object& par) + { + if(!parameter::fromJSON(par)) return false; + for(picojson::value::object::const_iterator it = par.begin(); it != par.end(); ++it){ + if(it->first == "values"){ + if(!it->second.is<picojson::array>()) return false; + const picojson::value::array &arr = it->second.get<picojson::array>(); + _values.resize(arr.size()); + for(unsigned int i = 0; i < arr.size(); i++){ + if(!arr[i].is<std::string>()) return false; + _values[i] = arr[i].get<std::string>(); + } + } + else if(it->first == "kind"){ + if(!it->second.is<std::string>()) return false; + setKind(it->second.get<std::string>()); + } + else if(it->first == "choices"){ + if(!it->second.is<picojson::array>()) return false; + const picojson::value::array &arr = it->second.get<picojson::array>(); + _choices.resize(arr.size()); + for(unsigned int i = 0; i < arr.size(); i++){ + if(!arr[i].is<std::string>()) return false; + _choices[i] = arr[i].get<std::string>(); + } + } + } + return true; + } +#endif + }; + + // The parameter space, i.e., the set of parameters stored and handled by the + // onelab server. + class parameterSpace{ + private: + std::set<number*, parameterLessThan> _numbers; + std::set<string*, parameterLessThan> _strings; + // delete a parameter from the parameter space + template <class T> bool _clear(const std::string &name, + const std::string &client, + std::set<T*, parameterLessThan> &ps) + { + if(name.empty() && client.size()){ + std::vector<T*> toDelete; + for(typename std::set<T*, parameterLessThan>::iterator it = ps.begin(); + it != ps.end(); ){ + T *p = *it; + if(p->hasClient(client)){ + ps.erase(it++); // to avoid invalid iterator + delete p; + } + else{ + it++; + } + } + } + else{ + T tmp(name); + typename std::set<T*, parameterLessThan>::iterator it = ps.find(&tmp); + if(it != ps.end()){ + T *p = *it; + if(client.empty() || p->hasClient(client)){ + ps.erase(it); + delete p; + return true; + } + } + } + return false; + } + // set a parameter in the parameter space; if it already exists, update it + // (adding new clients if necessary). This would need to be locked to avoid + // race conditions when several clients try to set a parameter at the same + // time. + template <class T> bool _set(const T &p, const std::string &client, + std::set<T*, parameterLessThan> &ps) + { + typename std::set<T*, parameterLessThan>::iterator it = ps.find((T*)&p); + if(it != ps.end()){ + (*it)->update(p); + if(client.size()) (*it)->addClient(client, parameter::defaultChangedValue()); + } + else{ + T* newp = new T(p); + if(client.size()) newp->addClient(client, parameter::defaultChangedValue()); + ps.insert(newp); + } + return true; + } + // get the parameter matching the given name, or all the parameters in the + // category if no name is given. If we find a given parameter by name, we + // add the client requesting the parameter to the list of clients for this + // parameter. This would also need to be locked. + template <class T> bool _get(std::vector<T> &p, const std::string &name, + const std::string &client, + std::set<T*, parameterLessThan> &ps) + { + p.clear(); + if(name.empty()){ + for(typename std::set<T*, parameterLessThan>::iterator it = ps.begin(); + it != ps.end(); it++) + p.push_back(**it); + } + else{ + T tmp(name); + typename std::set<T*, parameterLessThan>::iterator it = ps.find(&tmp); + if(it != ps.end()){ + if(client.size()) (*it)->addClient(client, parameter::defaultChangedValue()); + p.push_back(**it); + } + } + return true; + } + template <class T> T* _getPtr(std::string name, const std::string client, + std::set<T*, parameterLessThan> ps) + { + T tmp(name); + typename std::set<T*, parameterLessThan>::iterator it = ps.find(&tmp); + if(it != ps.end()){ + if(client.size()) (*it)->addClient(client, parameter::defaultChangedValue()); + return *it; + } + return NULL; + } + public: + parameterSpace(){} + ~parameterSpace(){ clear(); } + void clear(const std::string &name="", const std::string &client="") + { + if(name.empty() && client.empty()){ + std::set<parameter*, parameterLessThan> ps; + getAllParameters(ps); + for(std::set<parameter*, parameterLessThan>::iterator it = ps.begin(); + it != ps.end(); it++) + delete *it; + _numbers.clear(); + _strings.clear(); + } + else{ + bool done = _clear(name, client, _numbers); + if(!done) done = _clear(name, client, _strings); + } + } + bool set(const number &p, const std::string &client="") + { + return _set(p, client, _numbers); + } + bool set(const string &p, const std::string &client="") + { + return _set(p, client, _strings); + } + bool get(std::vector<number> &ps, const std::string &name="", + const std::string &client="") + { + return _get(ps, name, client, _numbers); + } + bool get(std::vector<string> &ps, const std::string &name="", + const std::string &client="") + { + return _get(ps, name, client, _strings); + } + void getPtr(number **ptr, const std::string name, const std::string client="") + { + *ptr = _getPtr(name, client, _numbers); + } + void getPtr(string **ptr, const std::string name, const std::string client="") + { + *ptr = _getPtr(name, client, _strings); + } + void getAllParameters(std::set<parameter*, parameterLessThan> &ps) const + { + ps.insert(_numbers.begin(), _numbers.end()); + ps.insert(_strings.begin(), _strings.end()); + } + unsigned int getNumParameters() + { + return (int)(_numbers.size() + _strings.size()); + } + // check if at least one parameter depends on the given client + bool hasClient(const std::string &client) const + { + std::set<parameter*, parameterLessThan> ps; + getAllParameters(ps); + for(std::set<parameter*, parameterLessThan>::iterator it = ps.begin(); + it != ps.end(); it++) + if((*it)->hasClient(client)) return true; + return false; + } + // check if some parameters have changed (optionnally only check the + // parameters that depend on a given client) + int getChanged(const std::string &client="") const + { + std::set<parameter*, parameterLessThan> ps; + getAllParameters(ps); + int changed = 0; + for(std::set<parameter*, parameterLessThan>::iterator it = ps.begin(); + it != ps.end(); it++){ + changed = std::max(changed, (*it)->getChanged(client)); + } + return changed; + } + // set the changed flag for all the parameters that depend on the given + // client (or for all parameters if no client name is provided) + void setChanged(int changed, const std::string &client="") + { + std::set<parameter*, parameterLessThan> ps; + getAllParameters(ps); + for(std::set<parameter*, parameterLessThan>::iterator it = ps.begin(); + it != ps.end(); it++) + (*it)->setChanged(changed, client); + } + void thresholdChanged(int threshold, const std::string &client="") + { + std::set<parameter*, parameterLessThan> ps; + getAllParameters(ps); + for(std::set<parameter*, parameterLessThan>::iterator it = ps.begin(); + it != ps.end(); it++){ + int changed = (*it)->getChanged(client); + if(changed > threshold) + (*it)->setChanged(threshold, client); + } + } + // serialize the parameter space (optionally only serialize those parameters + // that depend on the given client) + std::vector<std::string> toChar(const std::string &client="") const + { + std::vector<std::string> s; + std::set<parameter*, parameterLessThan> ps; + getAllParameters(ps); + for(std::set<parameter*, parameterLessThan>::const_iterator it = ps.begin(); + it != ps.end(); it++) + if(client.empty() || (*it)->hasClient(client)){ + if((*it)->getAttribute("NotInDb") != "True") + s.push_back((*it)->toChar()); + } + return s; + } + // unserialize the parameter space + bool fromChar(const std::vector<std::string> &msg, const std::string &client="") + { + for(unsigned int i = 0; i < msg.size(); i++){ + std::string version, type, name; + onelab::parameter::getInfoFromChar(msg[i], version, type, name); + if(onelab::parameter::version() != version) return false; + if(type == "number"){ + number p; p.fromChar(msg[i]); set(p, client); + } + else if(type == "string"){ + string p; p.fromChar(msg[i]); set(p, client); + } + else + return false; + } + return true; + } + bool toJSON(std::string &json, const std::string &creator="", + const std::string &client="") const + { + time_t now; + time(&now); + std::string t(ctime(&now)); + t.resize(t.size() - 1); + json.clear(); + json += "{ \"onelab\":{\n"; + json += " \"creator\":\"" + creator + "\",\n"; + json += " \"date\":\"" + t + "\",\n"; + json += " \"version\":\"" + parameter::version() + "\",\n"; + json += " \"parameters\":[\n"; + std::set<parameter*, parameterLessThan> ps; + getAllParameters(ps); + for(std::set<parameter*, parameterLessThan>::const_iterator it = ps.begin(); + it != ps.end(); it++){ + if(it != ps.begin()) json += ",\n"; + if(client.empty() || (*it)->hasClient(client)){ + if((*it)->getAttribute("NotInDb") != "True"){ + json += " " + (*it)->toJSON(); + } + } + } + json += "\n ] }\n}\n"; + return true; + } + bool fromJSON(const std::string &json, const std::string &client="") + { +#if defined(HAVE_PICOJSON) + picojson::value v; + std::string err = picojson::parse(v, json); + if(err.size()) return false; + if(!v.is<picojson::object>()) return false; + const picojson::value::object &obj = v.get<picojson::object>(); + for (picojson::value::object::const_iterator i = obj.begin(); i != obj.end(); ++i) { + if(i->first == "onelab"){ // onelab database + if(!i->second.is<picojson::object>()) return false; + const picojson::value::object &db = i->second.get<picojson::object>(); + for (picojson::value::object::const_iterator j = db.begin(); j != db.end(); ++j) { + if(j->first == "version"){ + if(!j->second.is<std::string>()) return false; + if(j->second.get<std::string>() != parameter::version()) return false; + } + else if(j->first == "parameters"){ + if(!j->second.is<picojson::array>()) return false; + const picojson::value::array &arr = j->second.get<picojson::array>(); + for(unsigned int k = 0; k < arr.size(); k++){ + if(!arr[k].is<picojson::object>()) return false; + const picojson::value::object &par = arr[k].get<picojson::object>(); + picojson::value::object::const_iterator it = par.find("type"); + if(it == par.end()) return false; + if(it->second.to_str() == "number"){ + number p; p.fromJSON(par); set(p, client); + } + else if(it->second.to_str() == "string"){ + string p; p.fromJSON(par); set(p, client); + } + } + } + } + } + } + return true; +#else + return false; +#endif + } + }; + + // The onelab client: a class that communicates with the onelab server. Each + // client should be derived from this one. A client can be understood as "one + // simulation step in a complex computation". + class client{ + protected: + // the name of the client + std::string _name; + // the id of the client, used to create a unique socket for this client + int _id; + // the index of the client in an external client list (if any) + int _index; + public: + client(const std::string &name) : _name(name), _id(0), _index(-1){} + virtual ~client(){} + std::string getName(){ return _name; } + void setId(int id){ _id = id; } + int getId(){ return _id; } + void setIndex(int index){ _index = index; } + int getIndex(){ return _index; } + virtual bool run(){ return false; } + virtual bool isNetworkClient(){ return false; } + virtual bool kill(){ return false; } + virtual void sendInfo(const std::string &msg){ std::cout << msg << std::endl; } + virtual void sendWarning(const std::string &msg){ std::cerr << msg << std::endl; } + virtual void sendError(const std::string &msg){ std::cerr << msg << std::endl; } + virtual void sendProgress(const std::string &msg){ std::cout << msg << std::endl; } + virtual void sendMergeFileRequest(const std::string &msg){} + virtual void sendOpenProjectRequest(const std::string &msg){} + virtual void sendParseStringRequest(const std::string &msg){} + virtual void sendVertexArray(const std::string &msg){} + virtual bool clear(const std::string &name) = 0; + virtual bool set(const number &p) = 0; + virtual bool set(const string &p) = 0; + virtual bool get(std::vector<number> &ps, const std::string &name="") = 0; + virtual bool get(std::vector<string> &ps, const std::string &name="") = 0; + virtual bool setAndAppendChoices(const number &p) = 0; + virtual bool setAndAppendChoices(const string &p) = 0; + virtual bool getWithoutChoices(std::vector<number> &ps, const std::string &name="") = 0; + virtual bool getWithoutChoices(std::vector<string> &ps, const std::string &name="") = 0; + std::vector<std::string> toChar() + { + std::vector<std::string> out; + std::vector<number> n; get(n); + for(unsigned int i = 0; i < n.size(); i++) out.push_back(n[i].toChar()); + std::vector<string> s; get(s); + for(unsigned int i = 0; i < s.size(); i++) out.push_back(s[i].toChar()); + return out; + } + bool fromChar(const std::vector<std::string> &msg) + { + for(unsigned int i = 0; i < msg.size(); i++){ + std::string version, type, name; + onelab::parameter::getInfoFromChar(msg[i], version, type, name); + if(onelab::parameter::version() != version) return false; + if(type == "number"){ + number p; p.fromChar(msg[i]); set(p); + } + else if(type == "string"){ + string p; p.fromChar(msg[i]); set(p); + } + else + return false; + } + return true; + } + bool toFile(FILE *fp) + { + return parameter::toFile(toChar(), fp, getName()); + } + bool fromFile(FILE *fp) + { + std::vector<std::string> msg; + if(parameter::fromFile(msg, fp)) return fromChar(msg); + return false; + } + }; + + // The onelab server: a singleton that stores the parameter space and + // interacts with onelab clients. + class server{ + private: + // the unique server (singleton behaviour due to the "static" specifier) + static server *_server; + // the address of the server + std::string _address; + // the connected clients + std::set<client*> _clients; + // the parameter space + parameterSpace _parameterSpace; + public: + server(const std::string &address="") : _address(address) {} + ~server(){} + static server *instance(const std::string &address="") + { + if(!_server) _server = new server(address); + return _server; + } + static void setInstance(server *s) { _server = s; } + void clear(const std::string &name="", const std::string &client="") + { + _parameterSpace.clear(name, client); + } + template <class T> bool set(const T &p, const std::string &client="") + { + return _parameterSpace.set(p, client); + } + template <class T> bool get(std::vector<T> &ps, const std::string &name="", + const std::string &client="") + { + return _parameterSpace.get(ps, name, client); + } + typedef std::set<client*>::iterator citer; + citer firstClient(){ return _clients.begin(); } + citer lastClient(){ return _clients.end(); } + int getNumClients() { return (int)_clients.size(); }; + citer findClient(const std::string &name) + { + for(citer it = _clients.begin(); it != _clients.end(); it++) + if((*it)->getName() == name) return it; + return _clients.end(); + } + void registerClient(client *c) + { + _clients.insert(c); + c->setId(_clients.size()); + } + void unregisterClient(client *c){ _clients.erase(c); } + void setChanged(int changed, const std::string &client="") + { + _parameterSpace.setChanged(changed, client); + } + int getChanged(const std::string &client="") + { + return _parameterSpace.getChanged(client); + } + void thresholdChanged(int value, const std::string &client="") + { + _parameterSpace.thresholdChanged(value, client); + } + unsigned int getNumParameters(){ return _parameterSpace.getNumParameters(); } + std::vector<std::string> toChar(const std::string &client="") + { + return _parameterSpace.toChar(client); + } + bool fromChar(const std::vector<std::string> &msg, const std::string &client="") + { + return _parameterSpace.fromChar(msg, client); + } + bool toFile(FILE *fp, const std::string &client="") + { + return parameter::toFile(toChar(client), fp, "onelab server"); + } + bool fromFile(FILE *fp, const std::string &client="") + { + std::vector<std::string> msg; + if(parameter::fromFile(msg, fp)) return fromChar(msg, client); + return false; + } + bool toJSON(std::string &json, const std::string &client="") + { + return _parameterSpace.toJSON(json, client); + } + bool fromJSON(const std::string &json, const std::string &client="") + { + return _parameterSpace.fromJSON(json, client); + } + }; + + // A local client, which lives in the same memory space as the server. + class localClient : public client{ + private: + template <class T> bool _set(const T &p) + { + server::instance()->set(p, _name); + return true; + } + template <class T> bool _get(std::vector<T> &ps, + const std::string &name="") + { + server::instance()->get(ps, name, _name); + return true; + } + public: + localClient(const std::string &name) : client(name) + { + server::instance()->registerClient(this); + } + virtual ~localClient() + { + server::instance()->unregisterClient(this); + } + virtual bool clear(const std::string &name="") + { + server::instance()->clear(name); + return true; + } + virtual bool set(const number &p){ return _set(p); } + virtual bool set(const string &p){ return _set(p); } + virtual bool get(std::vector<number> &ps, + const std::string &name=""){ return _get(ps, name); } + virtual bool get(std::vector<string> &ps, + const std::string &name=""){ return _get(ps, name); } + virtual bool setAndAppendChoices(const number &p) + { + std::vector<number> ps; + _get(ps, _name); + std::vector<double> choices; + if(ps.size()) choices = ps[0].getChoices(); + choices.insert(choices.end(), p.getChoices().begin(), p.getChoices().end()); + number p2(p); + p2.setChoices(choices); + return _set(p2); + } + virtual bool setAndAppendChoices(const string &p) + { + std::vector<string> ps; + _get(ps, _name); + std::vector<std::string> choices; + if(ps.size()) choices = ps[0].getChoices(); + choices.insert(choices.end(), p.getChoices().begin(), p.getChoices().end()); + string p2(p); + p2.setChoices(choices); + return _set(p2); + } + virtual bool getWithoutChoices(std::vector<number> &ps, + const std::string &name="") + { + bool ret = _get(ps, name); + for(unsigned int i = 0; i < ps.size(); i++) + ps[i].setChoices(std::vector<double>()); + return ret; + } + virtual bool getWithoutChoices(std::vector<string> &ps, + const std::string &name="") + { + bool ret = _get(ps, name); + for(unsigned int i = 0; i < ps.size(); i++) + ps[i].setChoices(std::vector<std::string>()); + return ret; + } + }; + + // The local part of a network client. + class localNetworkClient : public localClient{ + private: + // executable of the client (including filesystem path, if necessary) + std::string _executable; + // treat the executable name as a full command line (will prevent the + // escaping of the exe name, and will assume that the command line has been + // correcly escaped) + bool _treatExecutableAsFullCommandLine; + // command to login to a remote host (if necessary) + std::string _remoteLogin; + // command line option to specify socket + std::string _socketSwitch; + // pid of the remote network client while it is running (-1 otherwise) + int _pid; + // underlying GmshServer + GmshServer *_gmshServer; + public: + localNetworkClient(const std::string &name, const std::string &executable, + const std::string &remoteLogin="", + bool treatExecutableAsFullCommandLine=false) + : localClient(name), _executable(executable), + _treatExecutableAsFullCommandLine(treatExecutableAsFullCommandLine), + _remoteLogin(remoteLogin), _socketSwitch("-onelab"), _pid(-1), + _gmshServer(0) {} + virtual ~localNetworkClient(){} + virtual bool isNetworkClient(){ return true; } + const std::string &getExecutable(){ return _executable; } + void setExecutable(const std::string &s){ _executable = s; } + const std::string &getRemoteLogin(){ return _remoteLogin; } + bool treatExecutableAsFullCommandLine() const + { + return _treatExecutableAsFullCommandLine; + } + void setRemoteLogin(const std::string &s){ _remoteLogin = s; } + const std::string &getSocketSwitch(){ return _socketSwitch; } + void setSocketSwitch(const std::string &s){ _socketSwitch = s; } + int getPid(){ return _pid; } + void setPid(int pid){ _pid = pid; } + GmshServer *getGmshServer(){ return _gmshServer; } + void setGmshServer(GmshServer *server){ _gmshServer = server; } + virtual bool run() = 0; + virtual bool kill() = 0; + }; + + // The remote part of a network client. + class remoteNetworkClient : public client{ + private: + // address (inet:port or unix socket) of the server + std::string _serverAddress; + // underlying GmshClient + GmshClient *_gmshClient; + // number of subclients + int _numSubClients; + template <class T> bool _set(const T &p, bool withChoices=true) + { + if(!_gmshClient) return false; + std::string msg = p.toChar(); + _gmshClient->SendMessage(withChoices ? GmshSocket::GMSH_PARAMETER : + GmshSocket::GMSH_PARAMETER_WITHOUT_CHOICES, + msg.size(), &msg[0]); + return true; + } + template <class T> bool _get(std::vector<T> &ps, const std::string &name="", + bool withChoices=true) + { + ps.clear(); + if(!_gmshClient) return false; + T p(name); + std::string msg = p.toChar(); + if(name.size()) + _gmshClient->SendMessage(withChoices ? GmshSocket::GMSH_PARAMETER_QUERY : + GmshSocket::GMSH_PARAMETER_QUERY_WITHOUT_CHOICES, + msg.size(), &msg[0]); + else // get all parameters + _gmshClient->SendMessage(GmshSocket::GMSH_PARAMETER_QUERY_ALL, msg.size(), &msg[0]); + + while(1){ + // stop if we have no communications for 5 minutes + int ret = _gmshClient->Select(500, 0); + if(!ret){ + _gmshClient->Info("Timeout: aborting remote get"); + return false; + } + else if(ret < 0){ + _gmshClient->Error("Error on select: aborting remote get"); + return false; + } + int type, length, swap; + if(!_gmshClient->ReceiveHeader(&type, &length, &swap)){ + _gmshClient->Error("Did not receive message header: aborting remote get"); + return false; + } + std::string msg(length, ' '); + if(!_gmshClient->ReceiveMessage(length, &msg[0])){ + _gmshClient->Error("Did not receive message body: aborting remote get"); + return false; + } + if(type == GmshSocket::GMSH_PARAMETER){ + T p; + p.fromChar(msg); + ps.push_back(p); + return true; + } + if(type == GmshSocket::GMSH_PARAMETER_QUERY_ALL){ + T p; + p.fromChar(msg); + ps.push_back(p); + // do NOT return until all parameters have been downloaded + } + else if(type == GmshSocket::GMSH_PARAMETER_QUERY_END){ + // all parameters have been sent + return true; + } + else if(type == GmshSocket::GMSH_PARAMETER_NOT_FOUND){ + // parameter not found + return true; + } + else if(type == GmshSocket::GMSH_INFO){ + return true; + } + else{ + _gmshClient->Error("Unknown message type: aborting remote get"); + return false; + } + } + return true; + } + public: + void waitOnSubClients() + { + if(!_gmshClient) return; + while(_numSubClients > 0){ + int ret = _gmshClient->Select(500, 0); + if(!ret){ + _gmshClient->Info("Timeout: aborting wait on subclients"); + return; + } + else if(ret < 0){ + _gmshClient->Error("Error on select: aborting wait on subclients"); + return; + } + int type, length, swap; + if(!_gmshClient->ReceiveHeader(&type, &length, &swap)){ + _gmshClient->Error("Did not receive message header: aborting wait on subclients"); + return; + } + std::string msg(length, ' '); + if(!_gmshClient->ReceiveMessage(length, &msg[0])){ + _gmshClient->Error("Did not receive message body: aborting wait on subclients"); + return; + } + if(type == GmshSocket::GMSH_STOP) + _numSubClients -= 1; + } + } + public: + remoteNetworkClient(const std::string &name, const std::string &serverAddress) + : client(name), _serverAddress(serverAddress), _numSubClients(0) + { + _gmshClient = new GmshClient(); + if(_gmshClient->Connect(_serverAddress.c_str()) < 0){ + delete _gmshClient; + _gmshClient = 0; + } + else{ + _gmshClient->Start(); + } + } + virtual ~remoteNetworkClient() + { + if(_gmshClient){ + waitOnSubClients(); + _gmshClient->Stop(); + _gmshClient->Disconnect(); + delete _gmshClient; + _gmshClient = 0; + } + } + GmshClient *getGmshClient(){ return _gmshClient; } + virtual bool isNetworkClient(){ return true; } + virtual bool clear(const std::string &name="") + { + if(!_gmshClient) return false; + std::string msg = name; + if(msg.empty()) msg = "*"; + _gmshClient->SendMessage(GmshSocket::GMSH_PARAMETER_CLEAR, msg.size(), &msg[0]); + return true; + } + virtual bool set(const number &p){ return _set(p); } + virtual bool set(const string &p){ return _set(p); } + virtual bool get(std::vector<number> &ps, const std::string &name="") + { + return _get(ps, name); + } + virtual bool get(std::vector<string> &ps, const std::string &name="") + { + return _get(ps, name); + } + virtual bool setAndAppendChoices(const number &p){ return _set(p, false); } + virtual bool setAndAppendChoices(const string &p){ return _set(p, false); } + virtual bool getWithoutChoices(std::vector<number> &ps, const std::string &name="") + { + return _get(ps, name, false); + } + virtual bool getWithoutChoices(std::vector<string> &ps, const std::string &name="") + { + return _get(ps, name, false); + } + void sendInfo(const std::string &msg) + { + if(_gmshClient) _gmshClient->Info(msg.c_str()); + } + void sendWarning(const std::string &msg) + { + if(_gmshClient) _gmshClient->Warning(msg.c_str()); + } + void sendError(const std::string &msg) + { + if(_gmshClient) _gmshClient->Error(msg.c_str()); + } + void sendProgress(const std::string &msg) + { + if(_gmshClient) _gmshClient->Progress(msg.c_str()); + } + void sendMergeFileRequest(const std::string &msg) + { + if(_gmshClient) _gmshClient->MergeFile(msg.c_str()); + } + void sendOpenProjectRequest(const std::string &msg) + { + if(_gmshClient) _gmshClient->OpenProject(msg.c_str()); + } + void sendParseStringRequest(const std::string &msg) + { + if(_gmshClient) _gmshClient->ParseString(msg.c_str()); + } + void runNonBlockingSubClient(const std::string &name, const std::string &command) + { +#if !defined(BUILD_IOS) + if(!_gmshClient){ + int res = system(command.c_str()); + if(res){ + // report error + } + return; + } +#endif + std::string msg = name + parameter::charSep() + command; + _gmshClient->SendMessage(GmshSocket::GMSH_CONNECT, msg.size(), &msg[0]); + _numSubClients += 1; + } + void runSubClient(const std::string &name, const std::string &command) + { + runNonBlockingSubClient(name, command); + waitOnSubClients(); + } + }; + +} + +#endif diff --git a/PendulumC++/pend.cpp b/PendulumC++/pend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c83ab795963c93001f9669460ae119c869f989d6 --- /dev/null +++ b/PendulumC++/pend.cpp @@ -0,0 +1,202 @@ +// 1) compile with "c++ pend.cpp -o pend.exe" +// 2) launch "gmsh pend.exe" + +#include <math.h> +#include <stdio.h> +#include "onelab.h" + +void exportMsh(const std::string &path, double le1, double le2) +{ + FILE *mshFile = fopen((path + "pend.msh").c_str(),"w"); + if(!mshFile) return; + fprintf(mshFile, "$MeshFormat\n2.2 0 8\n$EndMeshFormat\n"); + fprintf(mshFile, "$Nodes\n3\n1 0 0 0\n2 0 %f 0\n3 0 %f 0\n$EndNodes\n", + -le1, -le1-le2); + fprintf(mshFile, "$Elements\n3\n1 1 2 0 1 1 2\n2 1 2 0 1 2 3\n3 15 2 0 2 3\n" + "$EndElements\n"); + fclose(mshFile); +} + +void exportMshOpt(const std::string &path) +{ + FILE *optFile = fopen((path + "pend.msh.opt").c_str(), "w"); + if(!optFile) return; + fprintf(optFile, "n = PostProcessing.NbViews - 1;\n"); + fprintf(optFile, "If(n >= 0)\nView[n].ShowScale = 0;\nView[n].VectorType = 5;\n"); + fprintf(optFile, "View[n].ExternalView = 0;\nView[n].DisplacementFactor = 1 ;\n"); + fprintf(optFile, "View[n].PointType = 1;\nView[n].PointSize = 5;\n"); + fprintf(optFile, "View[n].LineWidth = 2;\nEndIf\n"); + fclose(optFile); +} + +void exportIter(const std::string &path, int iter, double t, double x1, double y1, + double x2, double y2) +{ + FILE *mshFile = fopen((path + "pend.msh").c_str(), "a"); + if(!mshFile) return; + fprintf(mshFile, "$NodeData\n1\n\"motion\"\n1\n\t%f\n3\n\t%d\n3\n", t, iter); + fprintf(mshFile, "\t3\n\t1 0 0 0\n\t2 %f %f 0\n\t3 %f %f 0\n$EndNodeData\n", + x1, y1, x2, y2); + fclose(mshFile); +} + +double defineNumber(onelab::client *c, const std::string &name, double value, + const std::map<std::string, std::string> &attributes) +{ + std::vector<onelab::number> ns; + c->get(ns, name); + if(ns.empty()){ // define new parameter + onelab::number n(name, value); + if(attributes.size()) n.setAttributes(attributes); + c->set(n); + return value; + } + // return value from server + return ns[0].getValue(); +} + +void setNumber(onelab::client *c, const std::string &name, double value, + double min=0, double max=0, bool visible=true) +{ + onelab::number n(name, value); + n.setMin(min); + n.setMax(max); + n.setVisible(visible); + c->set(n); +} + +void addNumberChoice(onelab::client *c, const std::string &name, double choice) +{ + std::vector<onelab::number> ns; + c->get(ns, name); + if(ns.size()){ + std::vector<double> choices = ns[0].getChoices(); + choices.push_back(choice); + ns[0].setChoices(choices); + c->set(ns[0]); + } +} + +int main(int argc, char **argv) +{ + std::string name, address; + for(int i = 0; i < argc; i++){ + if(std::string(argv[i]) == "-onelab" && i + 2 < argc){ + name = std::string(argv[i + 1]); + address = std::string(argv[i + 2]); + } + } + + if(name.empty() || address.empty()) return 1; + + onelab::remoteNetworkClient *c = new onelab::remoteNetworkClient(name, address); + + std::string action; + std::vector<onelab::string> ns; + c->get(ns, name + "/Action"); + if(ns.size()) action = ns[0].getValue(); + + std::string path(argv[0]); + int islash = (int)path.find_last_of("/\\"); + if(islash > 0) + path = path.substr(0, islash + 1); + else + path = ""; + + double g = 9.8; // acceleration of gravity + double m = 0.3; // mass of pendulum balls + + std::map<std::string, std::string> attr; + + double l = defineNumber(c, "Geom/arm length [m]", 1.0, attr); + double time = defineNumber(c, "Dyna/time [s]", 0., attr); + double dt = defineNumber(c, "Dyna/time step [s]", 0.001, attr); + double tmax = defineNumber(c, "Dyna/max time [s]", 20, attr); + double refresh = defineNumber(c, "Dyna/refresh interval [s]", 0.05, attr); + attr["Highlight"] = "Pink"; + double theta0 = defineNumber(c, "Init/initial theta angle [deg]", 10, attr); + double phi0 = defineNumber(c, "Init/initial phi angle [deg]", 180, attr); + + // we're done if we are not in the compute phase + if(action != "compute"){ + delete c; + return 0; + } + + double l1 = l; + double l2 = l; + double m1 = m; + double m2 = m; + double theta = theta0 / 180.*M_PI; + double phi = phi0 / 180.*M_PI; + double theta_dot = 0.0; + double phi_dot = 0.0; + double refr = 0.0; + int iter = 0; + time = 0.0; + + while (time < tmax){ + double delta = phi - theta; + double sdelta = sin(delta); + double cdelta = cos(delta); + double theta_dot_dot = ( m2*l1*(theta_dot*theta_dot)*sdelta*cdelta + + m2*g*sin(phi)*cdelta + + m2*l2*(phi_dot*phi_dot)*sdelta + - (m1+m2)*g*sin(theta) ); + theta_dot_dot /= ( (m1+m2)*l1 - m2*l1*(cdelta*cdelta) ); + + double phi_dot_dot = ( -m2*l2*(phi_dot*phi_dot)*sdelta*cdelta + + (m1+m2)*(g*sin(theta)*cdelta + - l1*(theta_dot*theta_dot)*sdelta + - g*sin(phi)) ); + phi_dot_dot /= ( (m1+m2)*l2 - m2*l2*(cdelta*cdelta) ); + + theta_dot += theta_dot_dot*dt; + phi_dot += phi_dot_dot*dt; + theta += theta_dot*dt; + phi += phi_dot*dt; + + double x1 = l1*sin(theta); + double y1 = -l1*cos(theta); + double x2 = l1*sin(theta) + l2*sin(phi); + double y2 = -l1*cos(theta) - l2*cos(phi); + + time += dt; + refr += dt; + + exportMshOpt(path); + + if(refr >= refresh){ + refr = 0; + setNumber(c, name + "/Progress", time, 0, tmax, false); + setNumber(c, "Dyna/time [s]", time); + setNumber(c, "Solu/phi", phi); + addNumberChoice(c, "Solu/phi", phi); + setNumber(c, "Solu/theta", theta); + addNumberChoice(c, "Solu/theta", theta); + setNumber(c, "Solu/phi dot", phi_dot); + addNumberChoice(c, "Solu/phi dot", phi_dot); + setNumber(c, "Solu/theta dot", theta_dot); + addNumberChoice(c, "Solu/theta dot", theta_dot); + + // ask Gmsh to refresh + onelab::string s("Gmsh/Action", "refresh"); + c->set(s); + + // stop if we are asked to (by Gmsh) + c->get(ns, name + "/Action"); + if(ns.size() && ns[0].getValue() == "stop") break; + + exportMsh(path, l1, l2); + exportIter(path, iter, time, x1, y1+l1, x2, y2+l1+l2); + c->sendMergeFileRequest(path + "pend.msh"); + iter += 1; + } + } + + setNumber(c, name + "/Progress", 0, 0, tmax, false); + + delete c; + + return 0; +} diff --git a/PendulumPython/pend.py b/PendulumPython/pend.py new file mode 100644 index 0000000000000000000000000000000000000000..58878103beba235febfe4470c383a74333862ba8 --- /dev/null +++ b/PendulumPython/pend.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +#coding=utf-8 + +# 1) Launch "gmsh pend.py", or open "pend.py" with Gmsh's File->Open menu +# 2) Click on "Run" in the left Gmsh panel +# 3) there is no number 3... :-) + +# To interact with ONELAB, the script must first import the onelab.py module: +import onelab +import math, os + +# The script then creates a ONELAB client with: +c = onelab.client(__file__) + +# Creating the client connects the script to the onelab server, through a +# socket. The __file__ argument is a python variable. It tells ONELAB in which +# directory the script being executed is located. + +def exportMsh(le1,le2): + mshFile = open(c.getPath("pend.msh"), 'w') + mshFile.write('$MeshFormat\n2.2 0 8\n$EndMeshFormat\n') + mshFile.write('$Nodes\n3\n1 0 0 0\n2 0 %s 0\n3 0 %s 0\n$EndNodes\n' %(-le1, -le1-le2)) + mshFile.write('$Elements\n3\n1 1 2 0 1 1 2\n2 1 2 0 1 2 3\n3 15 2 0 2 3\n$EndElements\n') + mshFile.close() + +def exportMshOpt(): + optFile = open(c.getPath("pend.msh.opt"),'w') + optFile.write('n = PostProcessing.NbViews - 1;\n') + optFile.write('If(n >= 0)\nView[n].ShowScale = 0;\nView[n].VectorType = 5;\n') + optFile.write('View[n].ExternalView = 0;\nView[n].DisplacementFactor = 1 ;\n') + optFile.write('View[n].PointType = 1;\nView[n].PointSize = 5;\n') + optFile.write('View[n].LineWidth = 2;\nEndIf\n') + optFile.close() + +def exportIter(iter,t,x1,y1,x2,y2): + mshFile = open(c.getPath("pend.msh"),'a') + mshFile.write('$NodeData\n1\n"motion"\n1\n\t%f\n3\n\t%d\n3\n' % (t, iter)) + mshFile.write('\t3\n\t1 0 0 0\n\t2 %f %f 0\n\t3 %f %f 0\n$EndNodeData\n' %(x1,y1,x2,y2)) + mshFile.close() + +g = 9.8 # acceleration of gravity +m = 0.3 # mass of pendulum balls + +# New ONELAB variables can then be defined using defineNumber, e.g.: +l = c.defineNumber('Geom/arm length [m]', value=1.0) +time = c.defineNumber('Dyna/time [s]', value=0.0) +dt = c.defineNumber('Dyna/time step [s]', value=0.001) +tmax = c.defineNumber('Dyna/max time [s]', value=20) +refresh = c.defineNumber('Dyna/refresh interval [s]', value=0.1) +theta0 = c.defineNumber('Init/initial theta angle [deg]', value=10, + attributes={'Highlight':'Pink'}) +phi0 = c.defineNumber('Init/initial phi angle [deg]', value=180, + attributes={'Highlight':'Pink'}) + +# When the script is run, if the parameter Geom/arm length [m] has not been +# previously defined, it takes the value (1.0) provided in defineNumber and is +# sent to the ONELAB server. The "/" character in the variable name is +# interpreted as a path separator, and results in the creation of a sub-tree in +# the graphical user interface. If the script is re-run later, the value will be +# updated using the value from the server (unless it is labeled as readOnly: see +# below). When Gmsh runs a ONELAB client, the client can be run in two modes: +# c.action=='check' to check the coherence of the ONELAB database and make +# adjustments if necessary, and c.action=='compute' to perform the actual +# computation. For instance, in 'check' mode, the double pendulum client simply +# defines the ONELAB variables it wants to share with the server, then exits: +if c.action == 'check' : + exit(0) + +# In 'compute' mode, the code enters a loop and performs the actual computation. + +l1 = l; +l2 = l; +m1 = m; +m2 = m; +theta = theta0 / 180.*math.pi; +phi = phi0 / 180.*math.pi; +theta_dot = 0.0 +phi_dot = 0.0 +refr = 0.0 +iter = 0 +time = 0.0 + +while (time < tmax): + delta = phi - theta + sdelta = math.sin(delta) + cdelta = math.cos(delta) + theta_dot_dot = ( m2*l1*(theta_dot**2.0)*sdelta*cdelta + + m2*g*math.sin(phi)*cdelta + + m2*l2*(phi_dot**2.0)*sdelta + - (m1+m2)*g*math.sin(theta) ) + theta_dot_dot /= ( (m1+m2)*l1 - m2*l1*(cdelta)**2.0 ) + + phi_dot_dot = ( -m2*l2*(phi_dot**2.0)*sdelta*cdelta + + (m1+m2)*(g*math.sin(theta)*cdelta + - l1*(theta_dot**2.0)*sdelta + - g*math.sin(phi)) ) + phi_dot_dot /= ( (m1+m2)*l2 - m2*l2*(cdelta)**2.0 ) + + theta_dot = theta_dot + theta_dot_dot*dt + phi_dot = phi_dot + phi_dot_dot*dt + + theta = theta + theta_dot*dt + phi = phi + phi_dot*dt + + x1 = l1*math.sin(theta) + y1 = -l1*math.cos(theta) + x2 = l1*math.sin(theta) + l2*math.sin(phi) + y2 = -l1*math.cos(theta) - l2*math.cos(phi) + + time += dt + refr += dt + + exportMshOpt() + + if refr >= refresh: + refr = 0 + # During the computation the script can directly set a value in the ONELAB + # database with setNumber: + c.setNumber(c.name + '/Progress', value=time, min=0, max=tmax, visible=0) + c.setNumber('Dyna/time [s]', value=time) + c.setNumber('Solu/phi', value=phi) + c.addNumberChoice('Solu/phi', phi) + c.setNumber('Solu/theta', value=theta) + c.addNumberChoice('Solu/theta', theta) + c.setNumber('Solu/phi dot', value=phi_dot) + c.addNumberChoice('Solu/phi dot', phi_dot) + c.setNumber('Solu/theta dot', value=theta_dot) + c.addNumberChoice('Solu/theta dot', theta_dot) + + # It can also ask the server to refresh... + c.setString('Gmsh/Action', value='refresh') + + # or to stop... + if(c.getString(c.name + '/Action') == 'stop'): + break; + + exportMsh(l1, l2) + exportIter(iter, time, x1, y1+l1, x2, y2+l1+l2) + + # or to read a file: + c.mergeFile(c.checkPath('pend.msh')) + iter += 1 + + # The check path function (c.checkPath) builds the pathname of a file + # named pend.msh located in the same directory as the script under + # execution, and then checks on whether the pathname exists on disk. If + # not, an error message is issued. Use the regular path function + # (c.getPath) to build a pathname without checking on the presence on disk + # of the file. + +c.setNumber(c.name + '/Progress', value=0)