Skip to content
Snippets Groups Projects
ini_client.py 5.82 KiB
Newer Older
from util.comms_client import CommClient
import configparser
import numpy

__all__ = ["ini_client"]


numpy_to_ini_dict = {
    numpy.int64: int,
    numpy.double: float,
    numpy.bool_: bool,
    str: str
}

numpy_to_ini_get_dict = {
    numpy.int64: configparser.ConfigParser.getint,
    numpy.double: configparser.ConfigParser.getfloat,
    numpy.float64: configparser.ConfigParser.getfloat,
    numpy.bool_: configparser.ConfigParser.getboolean,
    str: str
}

ini_to_numpy_dict = {
    int: numpy.int64,
    bool: numpy.bool_,
}

import os

class ini_client(CommClient):
    """
    this class provides an example implementation of a comms_client.
    Durirng initialisation it creates a correctly shaped zero filled value. on read that value is returned and on write its modified.
    """

    def start(self):
        super().start()

    def __init__(self, filename, fault_func, streams, try_interval=2):
        """
        initialises the class and tries to connect to the client.
        """
        self.config = configparser.ConfigParser()
        self.filename = filename

        super().__init__(fault_func, streams, try_interval)

        # Explicitly connect
        if not self.connect():
            # hardware or infra is down -- needs fixing first
            fault_func()
            return

    def connect(self):
        self.config_file = open(self.filename, "r")

        self.connected = True  # set connected to true
        return True  # if succesfull, return true. otherwise return false

    def disconnect(self):
        self.connected = False  # always force a reconnect, regardless of a successful disconnect
        self.streams.debug_stream("disconnected from the 'client' ")

    def _setup_annotation(self, annotation):
        """
        this function gives the client access to the comm client annotation data given to the attribute wrapper.
        The annotation data can be used to provide whatever extra data is necessary in order to find/access the monitor/control point.

        the annotation can be in whatever format may be required. it is up to the user to handle its content
        example annotation may include:
        - a file path and file line/location
        - COM object path
        """

        # as this is an example, just print the annotation
        self.streams.debug_stream("annotation: {}".format(annotation))
        name = annotation.get('name')
        if name is None:
            AssertionError("ini client requires a variable name to set/get")
        section = annotation.get('section')
        if section is None:
            AssertionError("requires a section to open")

        return section, name


    def _setup_value_conversion(self, attribute):
        """
        gives the client access to the attribute_wrapper object in order to access all
        necessary data such as dimensionality and data type
        """

        dim_y = attribute.dim_y
        dim_x = attribute.dim_x

        dtype = attribute.numpy_type

        return dim_y, dim_x, dtype
    def _setup_mapping(self, name, section, dtype, dim_y, dim_x):
        """
        takes all gathered data to configure and return the correct read and write functions
        """

        def read_function():
            self.config.read_file(self.config_file)
            value = self.config.get(section, name)
            value = data_handler(value, dtype)

            if dim_y > 1:
                # if data is an image, slice it according to the y dimensions
Taya Snijder's avatar
Taya Snijder committed
                value = numpy.array(numpy.split(value, indices_or_sections=dim_y))
        def write_function(value):

            if type(value) is list:
                write_value = ""

                for i in value:
                    write_value = write_value + str(value) + ", "

                # strip the extra ", " at the end
                write_value = write_value[:-2]
            else:
                write_value = str(value)

            self.config.read_file(self.config_file)
            self.config.set(section, name, write_value)
            fp = open(self.filename, 'w')
            self.config.write(fp)

        return read_function, write_function

    def setup_attribute(self, annotation=None, attribute=None):
        """
        MANDATORY function: is used by the attribute wrapper to get read/write functions.
        must return the read and write functions
        """

        # process the comms_annotation
        section, name = self._setup_annotation(annotation)

        # get all the necessary data to set up the read/write functions from the attribute_wrapper
        dim_y, dim_x, dtype = self._setup_value_conversion(attribute)

        # configure and return the read/write functions
        read_function, write_function = self._setup_mapping(name, section, dtype, dim_y, dim_x)

        # return the read/write functions
        return read_function, write_function

def data_handler(string, dtype):
    value = []

    if dtype is numpy.bool_:
        # Handle special case for Bools
        for i in string.split(","):
            i = i.strip(" ")
                value.append(True)
                value.append(False)
            else:
                raise ValueError("String to bool failed. String is not True/False, but is: '{}'".format(i))
Taya Snijder's avatar
Taya Snijder committed

        value = dtype(value)
Taya Snijder's avatar
Taya Snijder committed

    elif dtype is numpy.str_:
        for i in string.split(","):
            val = numpy.str_(i)
            value.append(val)
Taya Snijder's avatar
Taya Snijder committed

        value = numpy.array(value)
Taya Snijder's avatar
Taya Snijder committed

    else:
        # regular case, go through the separator
        for i in string.split(","):
            i = i.replace(" ", "")
            val = dtype(i)
            value.append(val)

Taya Snijder's avatar
Taya Snijder committed
        # convert values from buildin type to numpy type
        value = dtype(value)

    return value