def request_capture_stop(self, req, product_id):
        """Signals that an observation is has stopped

            Publishes a message to the 'alerts' channel of the form:
                capture-stop:product_id
            The product_id should match what what was sent in the ?configure request

            This alert should notify all backend processes (such as beamformer)
            that they should stop collecting data now
        """
        msg = "capture-stop:{}".format(product_id)
        success = publish_to_redis(self.redis_server, REDIS_CHANNELS.alerts,
                                   msg)
        if success:
            return ("ok", )
        else:
            return ("fail", "Failed to publish to our local redis server")
    def request_deconfigure(self, req, product_id):
        """Signals that the current data product is done.

            Deconfigure the BLUSE instance that was created by the call
            to ?configure with the corresponding product_id. Note:  CAM is
            expected to have sent a ?capture-done request before deconfiguring,
            in order to ensure that all data has been written. If BLUSE uses an
            instance of katportalclient to get information from CAM for this
            BLUSE instance, then it should disconnect at this time.

            Publishes a message to the 'alerts' channel of the form:
                deconfigure:product_id
            The product_id should match what what was sent in the ?configure request

            This alert should notify all backend processes (such as beamformer)
            that their data streams are ending
        """
        msg = "deconfigure:{}".format(product_id)
        success = publish_to_redis(self.redis_server, REDIS_CHANNELS.alerts,
                                   msg)
        if success:
            return ("ok", )
        else:
            return ("fail", "Failed to publish to our local redis server")
    def request_configure(self, req, product_id, antennas_csv, n_channels,
                          streams_json, proxy_name):
        """Receive metadata for upcoming observation.

        In order to allow BLUSE to make an estimate of its ability
        to process a particular data product, this command should
        be used to configure a BLUSE instance when a new subarray is activated.

        Args:
            product_id (str): This is a name for the data product,
                    which is a useful tag to include in the data,
                    but should not be analysed further.
                    For example "array_1_bc856M4k". This value will
                    be unique across all subarrays. However, it is
                    not a globally unique identifier for the lifetime
                    of the telescope.  The exact same value may be provided
                    at a later time when the same subarray is activated again.

            antennas_csv (str): A comma separated list of physical antenna names
                    used in particular sub-array to which the data products belongs.

            n_channels (int): The integer number of frequency channels provided by the CBF.

            streams_json (str) is a JSON struct containing config keys and
                    values describing the streams.  For example:
                    {'stream_type1': {
                    'stream_name1': 'stream_address1',
                    'stream_name2': 'stream_address2',
                    ...},
                 'stream_type2': {
                    'stream_name1': 'stream_address1',
                    'stream_name2': 'stream_address2',
                    ...},
                    ...}
                The steam type keys indicate the source of the data and the type, e.g. cam.http.
                stream_address will be a URI.  For SPEAD streams, the format will be
                spead://<ip>[+<count>]:<port>, representing SPEAD stream multicast groups.
                When a single logical stream requires too much bandwidth to accommodate
                as a single multicast group, the count parameter indicates the number of
                additional consecutively numbered multicast group ip addresses, and
                sharing the same UDP port number.
                stream_name is the name used to identify the stream in CAM.
                A Python example is shown below, for five streams:
                One CAM stream, with type cam.http.  The camdata stream provides the
                connection string for katportalclient (for the subarray that this
                BLUSE instance is being configured on).
                One F-engine stream, with type:  cbf.antenna_channelised_voltage.
                One X-engine stream, with type:  cbf.baseline_correlation_products.
                Two beam streams, with type: cbf.tied_array_channelised_voltage.
                The stream names ending in x are horizontally polarised, and those
                ending in y are vertically polarised.

            proxy_name (str): The CAM name for the instance of the BLUSE data
                proxy that is being configured.  For example, "BLUSE_3".  This
                can be used to query sensors on the correct proxy.  Note that for
                BLUSE there will only be a single instance of the proxy in a subarray.

        Returns:
            None... but replies with "ok" or "fail" and logs either info or error

        Writes:
            - subbarry1_abc65555:timestamp" -> "1534657577373.23423"  :: Redis String
            - subarray1_abc65555:antennas" -> [1,2,3,4] :: Redis List
            - subarray1_abc65555:n_channels" -> "4096" :: Redis String
            - subarray1_abc65555:proxy_name "-> "BLUSE_whatever" :: Redis String
            - subarray1_abc65555:streams" -> {....} :: Redis Hash !!!CURRENTLY A STRING!!!
            - current:obs:id -> "subbary1_abc65555"

        Publishes:
            redis-channel: 'alerts' <-- "configure"

        Examples:
            > ?configure array_1_bc856M4k a1,a2,a3,a4 128000 {"cam.http":{"camdata":"http://monctl.devnmk.camlab.kat.ac.za/api/client/2"},"stream_type2":{"stream_name1":"stream_address1","stream_name2":"stream_address2"}} BLUSE_3
        """
        try:
            antennas_list = antennas_csv.split(",")
            json_dict = unpack_dict(streams_json)
            cam_url = json_dict['cam.http']['camdata']
        except Exception as e:
            log.error(e)
            return ("fail", e)
        statuses = []
        statuses.append(
            write_pair_redis(self.redis_server,
                             "{}:timestamp".format(product_id), time.time()))
        statuses.append(
            write_list_redis(self.redis_server,
                             "{}:antennas".format(product_id), antennas_list))
        statuses.append(
            write_pair_redis(self.redis_server,
                             "{}:n_channels".format(product_id), n_channels))
        statuses.append(
            write_pair_redis(self.redis_server,
                             "{}:proxy_name".format(product_id), proxy_name))
        statuses.append(
            write_pair_redis(self.redis_server,
                             "{}:streams".format(product_id),
                             json.dumps(json_dict)))
        statuses.append(
            write_pair_redis(self.redis_server,
                             "{}:cam:url".format(product_id), cam_url))
        statuses.append(
            write_pair_redis(self.redis_server, "current:obs:id", product_id))
        msg = "configure:{}".format(product_id)
        statuses.append(
            publish_to_redis(self.redis_server, REDIS_CHANNELS.alerts, msg))
        if all(statuses):
            return ("ok", )
        else:
            return ("fail", "Failed to publish to our local redis server")