def _create_simulator(self, params):
        board = params.get('board', 'telemetry')
        if board == 'telemetry':
            message = from_json("""
                {
                    "name":"telemetry_board",
                    "ver":"2017-09-23",
                    "power": {
                        "computer":1,
                        "fan":1,
                        "mount":1,
                        "cameras":1,
                        "weather":1,
                        "main":1
                    },
                    "current": {"main":387,"fan":28,"mount":34,"cameras":27},
                    "amps": {"main":1083.60,"fan":50.40,"mount":61.20,"cameras":27.00},
                    "humidity":42.60,
                    "temperature":[13.01,12.81,19.75],
                    "temp_00":15.50
                }
                """)
        elif board == 'camera':
            message = from_json("""
                {
                    "name":"camera_board",
                    "inputs":6,
                    "camera_00":1,
                    "camera_01":1,
                    "accelerometer": {"x":-7.02, "y":6.95, "z":1.70, "o": 6},
                    "humidity":59.60,
                    "temperature":[13.01,12.81,19.75],
                    "temp_00":12.50
                }
                """)
        elif board == 'json_object':
            # Produce an output that is json, but not what we expect
            message = {}
        else:
            raise Exception(
                'Unknown board: {}'.format(board))  # pragma: no cover

        # The elements of these queues are of type bytes. This means we aren't fully controlling
        # the baudrate unless the chunk_size is 1, but that should be OK.
        chunk_size = params.get('chunk_size', 20)
        self.json_queue = queue.Queue(
            maxsize=params.get('read_buffer_size', 10000))
        self.relay_queue = queue.Queue(
            maxsize=params.get('write_buffer_size', 100))

        self.device_simulator = ArduinoSimulator(message, self.relay_queue,
                                                 self.json_queue, chunk_size,
                                                 self.stop)
def test_quantity():
    json_str = serializers.to_json(dict(foo='42 deg', bar='foo deg'))
    assert json_str == '{"foo": "42 deg", "bar": "foo deg"}'
    json_obj = serializers.from_json(json_str)

    # Make sure we made a quantity when we could.
    assert json_obj['foo'] == 42 * u.deg
    # And not when we can't.
    assert json_obj['bar'] == 'foo deg'
示例#3
0
    def find(self, collection, obj_id):
        collection_fn = self._get_file(collection)
        obj = None
        with suppress(FileNotFoundError):
            with open(collection_fn, 'r') as f:
                for line in f:
                    if obj_id in line:
                        obj = from_json(line)
                        break

        return obj
示例#4
0
    def get_current(self, collection):
        current_fn = self._get_file(collection, permanent=False)

        try:
            with open(current_fn) as f:
                msg = from_json(f.read())

            return msg
        except FileNotFoundError:
            self._warn("No record found for {}".format(collection))
            return None
示例#5
0
def set_config(key, new_value, host='localhost', port='6563', parse=True):
    """Set config item in config server.

    Given a `key` entry, update the config to match. The `key` is a dot accessible
    string, as given by [scalpl](https://pypi.org/project/scalpl/). See Examples in `get_config`
    for details.

    Examples:
        >>> from astropy import units as u
        >>> set_config('location.horizon', 35 * u.degree)
        {'location.horizon': <Quantity 35. deg>}
        >>> get_config(key='location.horizon')
        <Quantity 35. deg>
        >>> set_config('location.horizon', 30 * u.degree)
        {'location.horizon': <Quantity 30. deg>}

    Args:
        key (str): The key to update, see Examples in `get_config` for details.
        new_value (scalar|object): The new value for the key, can be any serializable object.
        host (str, optional): The config server host, defaults to '127.0.0.1'.
        port (str, optional): The config server port, defaults to 6563.
        parse (bool, optional): If response should be parsed by
            `~panoptes.utils.serializers.from_json`, default True.

    Returns:
        dict: The updated config entry.

    Raises:
        Exception: Raised if the config server is not available.
    """
    url = f'http://{host}:{port}/set-config'

    json_str = serializers.to_json({key: new_value})

    config_entry = None
    try:
        # We use our own serializer so pass as `data` instead of `json`.
        response = requests.post(url,
                                 data=json_str,
                                 headers={'Content-Type': 'application/json'})
        if not response.ok:
            raise Exception(f'Cannot access config server: {response.text}')
    except Exception as e:
        get_root_logger().info(f'Problem with set_config: {e!r}')
    else:
        if parse:
            config_entry = serializers.from_json(
                response.content.decode('utf8'))
        else:
            config_entry = response.json()

    return config_entry
示例#6
0
    def receive_message(self, blocking=True, flags=0, timeout_ms=0):
        """Receive a message

        Receives a message for the current subscriber. Blocks by default, pass
        `flags=zmq.NOBLOCK` for non-blocking.

        Args:
            blocking (bool, optional): If True, blocks until message
                received or timeout__ms elapsed (if timeout_ms > 0).
            flag (int, optional): Any valid recv flag, e.g. zmq.NOBLOCK
            timeout_ms (int, optional): Time in milliseconds to wait for
                a message to arrive. Only applies if blocking is True.

        Returns:
            tuple(str, dict): Tuple containing the topic and a dict
        """
        topic = None
        msg_obj = None
        if not blocking:
            flags = flags | zmq.NOBLOCK
        elif timeout_ms > 0:
            # Wait until a message is available or the timeout expires.
            # TODO(jamessynge): Remove flags=..., confirm that works with
            # the default flags value of zmq.POLLIN.
            self.socket.poll(timeout=timeout_ms, flags=(zmq.POLLIN | zmq.POLLOUT))
            # Don't block at this point, because we will have waited as long
            # as necessary.
            flags = flags | zmq.NOBLOCK

        try:
            # Ugh. So ugly with the strings.
            message = self.socket.recv_string(flags=flags)
        except Exception as e:
            self.logger.warning(f'error in receive_message: {e!r}')
        else:
            topic, msg = message.split(' ', maxsplit=1)
            try:
                msg_obj = from_json(msg)
            except Exception as e:
                self.logger.warning(f'Error parsing message: {msg} {e!r}')

        return topic, msg_obj
示例#7
0
    def get_and_parse_reading(self, retry_limit=5):
        """Reads a line of JSON text and returns the decoded value, along with the current time.

        Args:
            retry_limit: Number of lines to read in an attempt to get one that parses as JSON.

        Returns:
            A pair (tuple) of (timestamp, decoded JSON line). The timestamp is the time of
            completion of the readline operation.
        """
        for _ in range(max(1, retry_limit)):
            (ts, line) = self.get_reading()
            if not line:
                continue

            with suppress(error.InvalidDeserialization):
                data = from_json(line)
                if data:
                    return (ts, data)
        return None
示例#8
0
文件: bisque.py 项目: ASTROGBAE/POCS
    def read(self, timeout=5):
        while True:
            response = self.theskyx.read()
            if response is not None or timeout == 0:
                break
            else:
                time.sleep(1)
                timeout -= 1

        # Default object.
        response_obj = {
            "response": response,
            "success": False,
        }
        try:
            response_obj = from_json(response)
        except (TypeError, error.InvalidDeserialization) as e:
            self.logger.warning(f"Error: {e!r}: {response}")

        return response_obj
示例#9
0
 def find(self, collection, obj_id):
     with self.lock:
         obj = self.collections.get(collection, {}).get(obj_id)
     if obj:
         obj = from_json(obj)
     return obj
示例#10
0
 def get_current(self, collection):
     with self.lock:
         obj = self.current.get(collection, None)
     if obj:
         obj = from_json(obj)
     return obj
示例#11
0
def test_bad_deserialization():
    with pytest.raises(error.InvalidDeserialization):
        serializers.from_json('foobar')
示例#12
0
def test_roundtrip_json(obj):
    config_str = serializers.to_json(obj)
    config = serializers.from_json(config_str)
    assert config['name'] == obj['name']
    assert config['location']['latitude'] == obj['location']['latitude']
示例#13
0
def get_config(key=None,
               host='localhost',
               port='6563',
               parse=True,
               default=None):
    """Get a config item from the config server.

    Return the config entry for the given `key`. If `key=None` (default), return
    the entire config.

    Nested keys can be specified as a string, as per [scalpl](https://pypi.org/project/scalpl/).

    Examples:
        >>> get_config(key='name')
        'Generic PANOPTES Unit'
        >>> get_config(key='location.horizon')
        <Quantity 30. deg>
        >>> get_config(key='location.horizon', parse=False)
        '30.0 deg'
        >>> get_config(key='cameras.devices[1].model')
        'canon_gphoto2'
        >>> # Returns `None` if key is not found.
        >>> foobar = get_config(key='foobar')
        >>> foobar is None
        True
        >>> # But you can supply a default.
        >>> get_config(key='foobar', default='baz')
        'baz'
        >>> # Can use Quantities as well
        >>> from astropy import units as u
        >>> get_config(key='foobar', default=42 * u.meter)
        <Quantity 42. m>

    Args:
        key (str): The key to update, see Examples in `get_config` for details.
        host (str, optional): The config server host, defaults to '127.0.0.1'.
        port (str, optional): The config server port, defaults to 6563.
        parse (bool, optional): If response should be parsed by
            `~panoptes.utils.serializers.from_json`, default True.
        default (str, optional): The config server port, defaults to 6563.

    Returns:
        dict: The corresponding config entry.

    Raises:
        Exception: Raised if the config server is not available.
    """
    url = f'http://{host}:{port}/get-config'

    config_entry = default

    try:
        response = requests.post(url, json={'key': key})
    except Exception as e:
        get_root_logger().info(f'Problem with get_config: {e!r}')
    else:
        if not response.ok:
            get_root_logger().info(
                f'Problem with get_config: {response.content!r}')
        else:
            if response.text != 'null\n':
                if parse:
                    config_entry = serializers.from_json(
                        response.content.decode('utf8'))
                else:
                    config_entry = response.json()

    if config_entry is None:
        config_entry = default

    return config_entry
示例#14
0
def get_config(key=None,
               host=None,
               port=None,
               endpoint='get-config',
               parse=True,
               default=None,
               verbose=True):
    """Get a config item from the config server.

    Return the config entry for the given ``key``. If ``key=None`` (default), return
    the entire config.

    Nested keys can be specified as a string, as per `scalpl <https://pypi.org/project/scalpl/>`_.

    Examples:

    .. doctest::

        >>> get_config(key='name')
        'Testing PANOPTES Unit'

        >>> get_config(key='location.horizon')
        <Quantity 30. deg>

        >>> # With no parsing, the raw string (including quotes) is returned.
        >>> get_config(key='location.horizon', parse=False)
        '"30.0 deg"'
        >>> get_config(key='cameras.devices[1].model')
        'canon_gphoto2'

        >>> # Returns `None` if key is not found.
        >>> foobar = get_config(key='foobar')
        >>> foobar is None
        True

        >>> # But you can supply a default.
        >>> get_config(key='foobar', default='baz')
        'baz'

        >>> # Can use Quantities as well
        >>> from astropy import units as u
        >>> get_config(key='foobar', default=42 * u.meter)
        <Quantity 42. m>

    Notes:
        By default all calls to this function will log at the `trace` level because
        there are some calls (e.g. during POCS operation) that will be quite noisy.

        Setting `verbose=True` changes those to `debug` log levels for an individual
        call.

    Args:
        key (str): The key to update, see Examples in :func:`get_config` for details.
        host (str, optional): The config server host. First checks for PANOPTES_CONFIG_HOST
            env var, defaults to 'localhost'.
        port (str or int, optional): The config server port. First checks for PANOPTES_CONFIG_HOST
            env var, defaults to 6563.
        endpoint (str, optional): The relative url endpoint to use for getting
            the config items, default 'get-config'. See `server_is_running()`
            for example of usage.
        parse (bool, optional): If response should be parsed by
            :func:`panoptes.utils.serializers.from_json`, default True.
        default (str, optional): The config server port, defaults to 6563.
        verbose (bool, optional): Determines the output log level, defaults to
            True (i.e. `debug` log level). See notes for details.
    Returns:
        dict: The corresponding config entry.

    Raises:
        Exception: Raised if the config server is not available.
    """
    log_level = 'DEBUG' if verbose else 'TRACE'

    host = host or os.getenv('PANOPTES_CONFIG_HOST', 'localhost')
    port = port or os.getenv('PANOPTES_CONFIG_PORT', 6563)

    url = f'http://{host}:{port}/{endpoint}'

    config_entry = default

    try:
        logger.log(log_level,
                   f'Calling get_config on url={url!r} with  key={key!r}')
        response = requests.post(url, json={'key': key, 'verbose': verbose})
        if not response.ok:  # pragma: no cover
            raise InvalidConfig(
                f'Config server returned invalid JSON:  response.content={response.content!r}'
            )
    except Exception as e:
        logger.warning(f'Problem with get_config: {e!r}')
    else:
        response_text = response.text.strip()
        logger.log(log_level, f'Decoded  response_text={response_text!r}')
        if response_text != 'null':
            logger.log(
                log_level,
                f'Received config key={key!r}  response_text={response_text!r}'
            )
            if parse:
                logger.log(
                    log_level,
                    f'Parsing config results:  response_text={response_text!r}'
                )
                config_entry = from_json(response_text)
            else:
                config_entry = response_text

    if config_entry is None:
        logger.log(log_level,
                   f'No config entry found, returning  default={default!r}')
        config_entry = default

    logger.log(log_level,
               f'Config key={key!r}:  config_entry={config_entry!r}')
    return config_entry
示例#15
0
def set_config(key, new_value, host=None, port=None, parse=True):
    """Set config item in config server.

    Given a `key` entry, update the config to match. The `key` is a dot accessible
    string, as given by `scalpl <https://pypi.org/project/scalpl/>`_. See Examples in
    :func:`get_config` for details.

    Examples:

    .. doctest::

        >>> from astropy import units as u

        >>> # Can use astropy units.
        >>> set_config('location.horizon', 35 * u.degree)
        {'location.horizon': <Quantity 35. deg>}

        >>> get_config(key='location.horizon')
        <Quantity 35. deg>

        >>> # String equivalent works for 'deg', 'm', 's'.
        >>> set_config('location.horizon', '30 deg')
        {'location.horizon': <Quantity 30. deg>}

    Args:
        key (str): The key to update, see Examples in :func:`get_config` for details.
        new_value (scalar|object): The new value for the key, can be any serializable object.
        host (str, optional): The config server host. First checks for PANOPTES_CONFIG_HOST
            env var, defaults to 'localhost'.
        port (str or int, optional): The config server port. First checks for PANOPTES_CONFIG_HOST
            env var, defaults to 6563.
        parse (bool, optional): If response should be parsed by
            :func:`panoptes.utils.serializers.from_json`, default True.

    Returns:
        dict: The updated config entry.

    Raises:
        Exception: Raised if the config server is not available.
    """
    host = host or os.getenv('PANOPTES_CONFIG_HOST', 'localhost')
    port = port or os.getenv('PANOPTES_CONFIG_PORT', 6563)
    url = f'http://{host}:{port}/set-config'

    json_str = to_json({key: new_value})

    config_entry = None
    try:
        # We use our own serializer so pass as `data` instead of `json`.
        logger.info(f'Calling set_config on  url={url!r}')
        response = requests.post(url,
                                 data=json_str,
                                 headers={'Content-Type': 'application/json'})
        if not response.ok:  # pragma: no cover
            raise Exception(f'Cannot access config server: {response.text}')
    except Exception as e:
        logger.warning(f'Problem with set_config: {e!r}')
    else:
        if parse:
            config_entry = from_json(response.content.decode('utf8'))
        else:
            config_entry = response.json()

    return config_entry