diff options
Diffstat (limited to 'python/lib')
| -rw-r--r-- | python/lib/__init__.py | 1 | ||||
| -rw-r--r-- | python/lib/yamlrpc.py | 93 | ||||
| -rw-r--r-- | python/lib/zmqrc.py | 84 | 
3 files changed, 178 insertions, 0 deletions
| 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])) + | 
