From 5cf52c74e9eb6bf8a82af4509ff3eb5106f928f9 Mon Sep 17 00:00:00 2001
From: "Matthias P. Braendli" <matthias.braendli@mpb.li>
Date: Tue, 4 Dec 2018 16:45:58 +0100
Subject: Rework GUI and DPDCE

---
 python/lib/__init__.py |  1 +
 python/lib/yamlrpc.py  | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++
 python/lib/zmqrc.py    | 84 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 178 insertions(+)
 create mode 100644 python/lib/__init__.py
 create mode 100644 python/lib/yamlrpc.py
 create mode 100644 python/lib/zmqrc.py

(limited to 'python/lib')

diff --git a/python/lib/__init__.py b/python/lib/__init__.py
new file mode 100644
index 0000000..738b3a1
--- /dev/null
+++ b/python/lib/__init__.py
@@ -0,0 +1 @@
+# lib module
diff --git a/python/lib/yamlrpc.py b/python/lib/yamlrpc.py
new file mode 100644
index 0000000..bd61569
--- /dev/null
+++ b/python/lib/yamlrpc.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+#   Copyright (C) 2018
+#   Matthias P. Braendli, matthias.braendli@mpb.li
+#
+#    http://www.opendigitalradio.org
+#
+#   This file is part of ODR-DabMod.
+#
+#   ODR-DabMod is free software: you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as
+#   published by the Free Software Foundation, either version 3 of the
+#   License, or (at your option) any later version.
+#
+#   ODR-DabMod is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with ODR-DabMod.  If not, see <http://www.gnu.org/licenses/>.
+
+"""yamlrpc is json-rpc, except that it's yaml and not json."""
+
+# Same as jsonrpc version we're aiming to mirror in YAML
+YAMLRPC_VERSION = "2.0"
+
+import yaml
+import socket
+import struct
+
+def request(request_id: int, method: str, params) -> bytes:
+    r = {
+            'yamlrpc': YAMLRPC_VERSION,
+            'method': method,
+            'params': params,
+            'id': request_id}
+    return yaml.dump(r).encode()
+
+def response_success(request_id: int, result) -> bytes:
+    r = {
+            'yamlrpc': YAMLRPC_VERSION,
+            'result': result,
+            'error': None,
+            'id': request_id}
+    return yaml.dump(r).encode()
+
+def response_error(request_id: int, error) -> bytes:
+    r = {
+            'yamlrpc': YAMLRPC_VERSION,
+            'result': None,
+            'error': error,
+            'id': request_id}
+    return yaml.dump(r).encode()
+
+def notification(method: str, params) -> bytes:
+    r = {
+            'yamlrpc': YAMLRPC_VERSION,
+            'method': method,
+            'params': params}
+    return yaml.dump(r).encode()
+
+class Socket:
+    def __init__(self, bind_port: int):
+        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        if bind_port > 0:
+            self.socket.bind(('127.0.0.1', bind_port))
+
+    def receive_request(self):
+        data, addr = self.socket.recvfrom(512)
+        y = yaml.load(data.decode())
+
+        if 'yamlrpc' not in y:
+            raise ValueError("Message is not yamlrpc")
+        if y['yamlrpc'] != YAMLRPC_VERSION:
+            raise ValueError("Invalid yamlrpc version")
+
+        # expect a request
+        try:
+            method = y['method']
+            msg_id = y['id']
+            params = y['params']
+        except KeyError:
+            raise ValueError("Incomplete message")
+        return addr, msg_id, method, params
+
+    def send_success_response(self, addr, msg_id: int, result):
+        self.socket.sendto(response_success(msg_id, result), addr)
+
+    def send_error_response(self, addr, msg_id: int, error):
+        self.socket.sendto(response_error(msg_id, error), addr)
+
diff --git a/python/lib/zmqrc.py b/python/lib/zmqrc.py
new file mode 100644
index 0000000..3897d7a
--- /dev/null
+++ b/python/lib/zmqrc.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+#   Copyright (C) 2018
+#   Matthias P. Braendli, matthias.braendli@mpb.li
+#
+#    http://www.opendigitalradio.org
+#
+#   This file is part of ODR-DabMod.
+#
+#   ODR-DabMod is free software: you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as
+#   published by the Free Software Foundation, either version 3 of the
+#   License, or (at your option) any later version.
+#
+#   ODR-DabMod is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with ODR-DabMod.  If not, see <http://www.gnu.org/licenses/>.
+import zmq
+import json
+
+class ModRemoteControl(object):
+    """Interact with ODR-DabMod using the ZMQ RC"""
+    def __init__(self, mod_host, mod_port=9400):
+        self._host = mod_host
+        self._port = mod_port
+        self._ctx = zmq.Context()
+
+    def _read(self, message_parts):
+        sock = zmq.Socket(self._ctx, zmq.REQ)
+        sock.setsockopt(zmq.LINGER, 0)
+        sock.connect("tcp://{}:{}".format(self._host, self._port))
+
+        for i, part in enumerate(message_parts):
+            if i == len(message_parts) - 1:
+                f = 0
+            else:
+                f = zmq.SNDMORE
+            sock.send(part.encode(), flags=f)
+
+        # use poll for timeouts:
+        poller = zmq.Poller()
+        poller.register(sock, zmq.POLLIN)
+        if poller.poll(5*1000): # 5s timeout in milliseconds
+            recv = sock.recv_multipart()
+            sock.close()
+            return [r.decode() for r in recv]
+        else:
+            raise IOError("Timeout processing ZMQ request")
+
+    def get_modules(self):
+        modules = {}
+
+        for mod in [json.loads(j) for j in self._read(['list'])]:
+            params = {}
+            pv_list = self._read(['show', mod['name']])
+
+            for pv in pv_list:
+                p, _, v = pv.partition(": ")
+                params[p] = {"value": v.strip()}
+
+            for p in mod['params']:
+                if p in params:
+                    params[p]["help"] = mod['params'][p]
+            modules[mod['name']] = params
+
+        return modules
+
+    def get_param_value(self, module, param):
+        value = self._read(['get', module, param])
+        if value[0] == 'fail':
+            raise ValueError("Error getting param: {}".format(value[1]))
+        else:
+            return value[0]
+
+    def set_param_value(self, module, param, value):
+        ret = self._read(['set', module, param, value])
+        if ret[0] == 'fail':
+            raise ValueError("Error setting param: {}".format(ret[1]))
+
-- 
cgit v1.2.3