From 2fdda004853ee258c726ff25f71e6a492c0502da Mon Sep 17 00:00:00 2001
From: Jonathan Lambrechts <jonathan.lambrechts@uclouvain.be>
Date: Mon, 24 Dec 2012 13:15:47 +0000
Subject: [PATCH] add onelab python clients

---
 Common/onelab.h                               |  10 +-
 contrib/onelab/onelab.i                       |  23 ----
 contrib/onelab/python/OnelabClient.py         | 130 ++++++++++++++++++
 contrib/onelab/python/test.ol                 |   2 +
 contrib/onelab/python/test.py                 |  20 +++
 contrib/onelab/python/wrappers/Makefile       |  11 ++
 .../onelab/python/wrappers/OnelabClient.py    |  58 ++++++++
 contrib/onelab/python/wrappers/onelab.i       |  15 ++
 contrib/onelab/python/wrappers/test.ol        |   1 +
 contrib/onelab/python/wrappers/test.py        |   1 +
 10 files changed, 244 insertions(+), 27 deletions(-)
 delete mode 100644 contrib/onelab/onelab.i
 create mode 100755 contrib/onelab/python/OnelabClient.py
 create mode 100755 contrib/onelab/python/test.ol
 create mode 100755 contrib/onelab/python/test.py
 create mode 100644 contrib/onelab/python/wrappers/Makefile
 create mode 100755 contrib/onelab/python/wrappers/OnelabClient.py
 create mode 100644 contrib/onelab/python/wrappers/onelab.i
 create mode 120000 contrib/onelab/python/wrappers/test.ol
 create mode 120000 contrib/onelab/python/wrappers/test.py

diff --git a/Common/onelab.h b/Common/onelab.h
index 6ad91635bc..969fb62960 100644
--- a/Common/onelab.h
+++ b/Common/onelab.h
@@ -702,7 +702,7 @@ namespace onelab{
              const std::string &client=""){ return _set(p, client, _functions); }
     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="",
+    bool get(std::vector<onelab::string> &ps, const std::string &name="",
              const std::string &client=""){ return _get(ps, name, client, _strings); }
     bool get(std::vector<region> &ps, const std::string &name="",
              const std::string &client=""){ return _get(ps, name, client, _regions); }
@@ -817,7 +817,7 @@ namespace onelab{
     virtual bool set(const region &p) = 0;
     virtual bool set(const function &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 get(std::vector<onelab::string> &ps, const std::string &name="") = 0;
     virtual bool get(std::vector<region> &ps, const std::string &name="") = 0;
     virtual bool get(std::vector<function> &ps, const std::string &name="") = 0;
     std::vector<std::string> toChar()
@@ -967,7 +967,7 @@ namespace onelab{
     virtual bool set(const region &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,
+    virtual bool get(std::vector<onelab::string> &ps,
                      const std::string &name=""){ return _get(ps, name); }
     virtual bool get(std::vector<function> &ps,
                      const std::string &name=""){ return _get(ps, name); }
@@ -1005,8 +1005,10 @@ namespace onelab{
     void setPid(int pid){ _pid = pid; }
     GmshServer *getGmshServer(){ return _gmshServer; }
     void setGmshServer(GmshServer *server){ _gmshServer = server; }
+    #ifndef SWIG
     virtual bool run();
     virtual bool kill();
+    #endif
   };
 
   // The remote part of a network client.
@@ -1112,7 +1114,7 @@ namespace onelab{
     virtual bool set(const region &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,
+    virtual bool get(std::vector<onelab::string> &ps,
                      const std::string &name=""){ return _get(ps, name); }
     virtual bool get(std::vector<function> &ps,
                      const std::string &name=""){ return _get(ps, name); }
diff --git a/contrib/onelab/onelab.i b/contrib/onelab/onelab.i
deleted file mode 100644
index ae601f09c8..0000000000
--- a/contrib/onelab/onelab.i
+++ /dev/null
@@ -1,23 +0,0 @@
-%module onelab
-%{
-#include "onelab.h"
-onelab::server *onelab::server::_server = 0;
-void modelName(const std::string &name, const std::string &wdir="");
-int metamodel(const std::string &todo);
-void setNumber(const std::string &name, const double value);
-void setString(const std::string &name, const std::string &value);
-double getNumber(const std::string &name);
-std::string getString(const std::string &name);
-
-%}
- 
-%include std_string.i
-
-extern void modelName(const std::string &name, const std::string &wdir="");
-extern int metamodel(const std::string &todo);
-extern void setNumber(const std::string &name, const double value);
-extern void setString(const std::string &name, const std::string &value);
-extern double getNumber(const std::string &name);
-extern std::string getString(const std::string &name);
-
-
diff --git a/contrib/onelab/python/OnelabClient.py b/contrib/onelab/python/OnelabClient.py
new file mode 100755
index 0000000000..584c5f6dc4
--- /dev/null
+++ b/contrib/onelab/python/OnelabClient.py
@@ -0,0 +1,130 @@
+import socket, struct, os, sys
+_VERSION = '1.05'
+
+class _parameter() :
+  _membersbase = [
+    ('name', 'string'), ('label', 'string', ''), ('help', 'string', ''),
+    ('neverChanged', 'int', 0), ('changed', 'int', 1), ('visible', 'int', 1),
+    ('read_only', 'int', 0), ('attributes', ('dict', 'string', 'string'), {}),
+    ('clients', ('list', 'string'), [])
+  ]
+  _members = {
+    'string' : _membersbase + [
+      ('value', 'string'), ('kind', 'string', 'generic'), ('choices', ('list', 'string'), [])
+    ],
+    'number' : _membersbase + [
+      ('value', 'float'), ('min', 'float', -sys.float_info.max), ('max', 'float', sys.float_info.max),
+      ('step', 'float', 0.), ('index', 'int', -1), ('choices', ('list', 'float'), []),
+      ('labels', ('dict', 'float', 'string'), {})
+    ]
+  }
+
+  def __init__(self, type, **values) :
+    self.type = type
+    for i in _parameter._members[self.type] :
+      setattr(self, i[0], values[i[0]] if i[0] in values else i[2])
+
+  def tochar(self) :
+    def tocharitem(l, t, v) :
+      if t=='string' : l.append(v)
+      elif t =='int': l.append(str(v))
+      elif t=='float' : l.append('%.16g' % v)
+      elif t[0]=='list' : 
+        l.append(str(len(v)))
+        for i in v : tocharitem(l, t[1], i)
+      elif t[0]=='dict' :
+        l.append(str(len(v)))
+        for i, j in v.items() :
+          tocharitem(l, t[1], i)
+          tocharitem(l, t[2], j)
+    msg = [_VERSION, self.type]
+    for i in _parameter._members[self.type] :
+      tocharitem(msg, i[1], getattr(self, i[0]))
+    return '\0'.join(msg)
+
+  def fromchar(self, msg) :
+    def fromcharitem(l, t) :
+      if t=='string' : return l.pop()
+      elif t =='int': return int(l.pop())
+      elif t=='float' : return float(l.pop())
+      elif t[0]=='list' : return [fromcharitem(l, t[1]) for i in range(int(l.pop()))]
+      elif t[0]=='dict' : return dict([(fromcharitem(l, t[1]),fromcharitem(l, t[2])) for i in range(int(l.pop()))])
+    l = msg.split('\0')
+    l.reverse()
+    if l.pop() != _VERSION :
+      print('onelab version mismatch')
+    if l.pop() != self.type :
+      print('onelab parameter type mismatch')
+    for p in  _parameter._members[self.type]:
+      setattr(self, p[0], fromcharitem(l, p[1]))
+    
+class client :
+  _GMSH_START = 1
+  _GMSH_STOP = 2
+  _GMSH_INFO = 10
+  _GMSH_PARAMETER = 23
+  _GMSH_PARAMETER_QUERY = 24
+
+  def _receive(self) :
+    def buffered_receive(l) :
+      msg = b''
+      while len(msg) < l:
+        chunk = self.socket.recv(l - len(msg))
+        if not chunk :
+          RuntimeError('onelab socket closed')
+        msg += chunk
+      return msg
+    msg = buffered_receive(struct.calcsize('ii'))
+    t, l = struct.unpack('ii', msg)
+    msg = buffered_receive(l).decode('utf-8')
+    if t == self._GMSH_INFO :
+      print('onelab info : %s' % msg)
+    return t, msg
+
+  def _send(self, t, msg) :
+    m = msg.encode('utf-8')
+    if self.socket.send(struct.pack('ii%is' %len(m), t, len(m), m)) == 0 :
+      RuntimeError('onelab socket closed')
+
+  def _get_parameter(self, param) :
+    if not self.socket :
+      return
+    self._send(self._GMSH_PARAMETER_QUERY, param.tochar())
+    (t, msg) = self._receive() 
+    if t == self._GMSH_PARAMETER :
+      param.fromchar(msg)
+    elif t == self._GMSH_INFO :
+      self._send(self._GMSH_PARAMETER, param.tochar())
+
+  def get_string(self, name, value, **param):
+    param = _parameter('string', name=name, value=value, **param)
+    self._get_parameter(param)
+    return param.value
+
+  def get_number(self, name, value, **param):
+    if "labels" in param :
+      param["choices"] = param["labels"].keys()
+    p = _parameter('number', name=name, value=value, **param)
+    self._get_parameter(p)
+    return p.value
+
+  def __init__(self):
+    self.socket = None
+    self.name = ""
+    for i, v in enumerate(sys.argv) :
+      if v == '-onelab':
+        self.name = sys.argv[i + 1]
+        addr = sys.argv[2]
+        if '/' in addr or '\\' in addr or ':' not in addr :
+          self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        else :
+          self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.socket.connect(sys.argv[i + 2])
+        self._send(self._GMSH_START, str(os.getpid))
+    self.action = self.get_string(self.name + '/Action', 'compute')
+    if self.action == "initialize": exit(0)
+  
+  def __del__(self) :
+    if self.socket :
+      self._send(self._GMSH_STOP, 'Goodbye!')
+      self.socket.close()
diff --git a/contrib/onelab/python/test.ol b/contrib/onelab/python/test.ol
new file mode 100755
index 0000000000..79463f51e7
--- /dev/null
+++ b/contrib/onelab/python/test.ol
@@ -0,0 +1,2 @@
+Native.register(native, ./test.py);
+Native.run();
diff --git a/contrib/onelab/python/test.py b/contrib/onelab/python/test.py
new file mode 100755
index 0000000000..631bb7070f
--- /dev/null
+++ b/contrib/onelab/python/test.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+#coding=utf-8
+import OnelabClient
+
+oc = OnelabClient.client()
+
+#name and default value are required
+A = oc.get_number('A', 10)
+#other attributes are optionals
+B = oc.get_number('Group/B', 0, min = -10, max = 10, step = 1)
+C = oc.get_number('Group/C', 2, choices = [0, 1, 2, 3], attributes={'Highlight':'Pink'})
+D = oc.get_number('Group/D', 2, labels = {0:'zero', 1:'un', 2:'deux', 3:'trois'}, attributes={'Highlight':'Blue'})
+#utf-8 are allowed everywhere (should be prefixed by 'u' in python 2, not required in python 3)
+Omega = oc.get_string(u'Ω', u'∫(∂φ/∂α)³dx', help=u'ask someone@universe.org', choices = ['oui', 'non', u'peut-être'])
+
+if oc.action != 'compute' :
+  exit(0)
+
+#this is the solver code
+print (A, B, C, str(Omega))
diff --git a/contrib/onelab/python/wrappers/Makefile b/contrib/onelab/python/wrappers/Makefile
new file mode 100644
index 0000000000..e3978894fe
--- /dev/null
+++ b/contrib/onelab/python/wrappers/Makefile
@@ -0,0 +1,11 @@
+all: _onelab.so
+GMSH_DIR=../../../../
+COMMON_DIR=$(GMSH_DIR)/Common
+CPPFLAGS+= $(shell python-config --includes) -I$(COMMON_DIR) -DONELAB_LOADER
+
+onelab_wrap.cpp : onelab.i
+	swig -python -c++ ${CPPFLAGS} -o $@ $<
+
+_onelab.so : onelab_wrap.cpp
+	$(CXX) -shared onelab_wrap.cpp -o $@ $(CPPFLAGS) -fPIC -DSWIG
+.PRECIOUS: $(OBJDIR)/%.o
diff --git a/contrib/onelab/python/wrappers/OnelabClient.py b/contrib/onelab/python/wrappers/OnelabClient.py
new file mode 100755
index 0000000000..6ec8641976
--- /dev/null
+++ b/contrib/onelab/python/wrappers/OnelabClient.py
@@ -0,0 +1,58 @@
+import onelab
+import sys
+
+class client :
+
+  def get_number(self, name, default, label = '', min = -sys.float_info.max, max = sys.float_info.max, step = 0, choices = [], labels = [], attributes = {}, help = "", visible = 1, read_only = 0) :
+    if not self.client :
+      return default
+    n = onelab.number(name, default)
+    n.setLabel(label)
+    n.setVisible(visible)
+    n.setReadOnly(read_only)
+    n.setMin(min)
+    n.setMax(max)
+    n.setStep(step)
+    n.setHelp(help)
+    n.setChoices(choices)
+    if labels :
+      n.setChoices([k for k in labels.keys()])
+      n.setChoiceLabels([v for v in labels.values()])
+    for k,v in attributes.items() :
+      n.setAttribute(k, v)
+    r = onelab.OnelabNumberVector()
+    self.client.get(r, n.getName())
+    if r :
+      return r[0].getValue()
+    else :
+      self.client.set(n)
+      return default
+
+  def get_string(self, name, default, choices = [], help ="", label = "", visible = 1, read_only = 0, kind = "generic", attributes = {}) :
+    if not self.client :
+      return default
+    n = onelab.string(name, default)
+    n.setChoices(choices)
+    n.setReadOnly(read_only)
+    n.setVisible(visible)
+    n.setLabel(label)
+    n.setHelp(help)
+    n.setKind(kind)
+    r = onelab.OnelabStringVector()
+    for k,v in attributes.items() :
+      n.setAttribute(k, v)
+    self.client.get(r, n.getName())
+    if r :
+      return r[0].getValue()
+    else :
+      self.client.set(n)
+      return default
+
+  def __init__(self):
+    self.client = None
+    for i, v in enumerate(sys.argv) :
+      if v == '-onelab':
+        self.name = sys.argv[i + 1]
+        self.client = onelab.remoteNetworkClient(self.name, sys.argv[i+2])
+    self.action = self.get_string(self.name + '/Action', 'compute')
+    if self.action == "initialize": exit(0)
diff --git a/contrib/onelab/python/wrappers/onelab.i b/contrib/onelab/python/wrappers/onelab.i
new file mode 100644
index 0000000000..21c18c2875
--- /dev/null
+++ b/contrib/onelab/python/wrappers/onelab.i
@@ -0,0 +1,15 @@
+%module onelab
+%{
+#include "onelab.h"
+onelab::server *onelab::server::_server = 0;
+%}
+ 
+%include std_string.i
+%include std_vector.i
+%include std_map.i
+%template(OnelabNumberVector) std::vector<onelab::number>;
+%template(OnelabStringVector) std::vector<onelab::string>;
+%template(DoubleVector) std::vector<double>;
+%template(StringVector) std::vector<std::string>;
+%template(DoubleStringMap) std::map<double, std::string>;
+%include "onelab.h"
diff --git a/contrib/onelab/python/wrappers/test.ol b/contrib/onelab/python/wrappers/test.ol
new file mode 120000
index 0000000000..200dd10d85
--- /dev/null
+++ b/contrib/onelab/python/wrappers/test.ol
@@ -0,0 +1 @@
+../test.ol
\ No newline at end of file
diff --git a/contrib/onelab/python/wrappers/test.py b/contrib/onelab/python/wrappers/test.py
new file mode 120000
index 0000000000..40a41e7dea
--- /dev/null
+++ b/contrib/onelab/python/wrappers/test.py
@@ -0,0 +1 @@
+../test.py
\ No newline at end of file
-- 
GitLab