class Danfysik8800StreamInterface(CommonStreamInterface, StreamInterface):
    """
    Stream interface for a Danfysik model 8800.
    """

    protocol = 'model8800'

    commands = CommonStreamInterface.commands + [
        CmdBuilder("set_current").escape("WA ").int().eos().build(),
        CmdBuilder("get_current").escape("ADCV").eos().build(),
        CmdBuilder("init_comms").escape("ADR 000").build(),
    ]

    @conditional_reply("comms_initialized")
    def get_status(self):
        """
        Respond to the get_status command (S1)
        """

        def bit(condition):
            return "!" if condition else "."

        def ilk(name):
            return bit(name in self.device.active_interlocks)

        response = "{spare}{user1}{user2}{user3}{user4}{user5}{user6}{fw_diode_overtemp}{low_water_flow}{door_open}" \
                   "{pol_normal}{pol_reversed}{spare}{spare}{spare}{spare}{diode_heatsink}{chassis_overtemp}" \
                   "{igbt_heatsink_overtemp}{hf_diode_overtemp}{switch_reg_ddct_fail}{switch_reg_supply_fail}" \
                   "{igbt_driver_fail}{spare}{spare}{ac_undervolt}{spare}{ground_ripple}{ground_leak}"\
                   "{overcurrent}{power_on}{ready}".format(
                        spare=bit(False),
                        user1=ilk("user1"),
                        user2=ilk("user2"),
                        user3=ilk("user3"),
                        user4=ilk("user4"),
                        user5=ilk("user5"),
                        user6=ilk("user6"),
                        pol_normal=bit(not self.device.negative_polarity),
                        pol_reversed=bit(self.device.negative_polarity),
                        fw_diode_overtemp=ilk("fw_diode_overtemp"),
                        low_water_flow=ilk("low_water_flow"),
                        door_open=ilk("door_open"),
                        diode_heatsink=ilk("diode_heatsink"),
                        chassis_overtemp=ilk("chassis_overtemp"),
                        igbt_heatsink_overtemp=ilk("igbt_heatsink_overtemp"),
                        hf_diode_overtemp=ilk("hf_diode_overtemp"),
                        switch_reg_ddct_fail=ilk("switch_reg_ddct_fail"),
                        switch_reg_supply_fail=ilk("switch_reg_supply_fail"),
                        igbt_driver_fail=ilk("igbt_driver_fail"),
                        ac_undervolt=ilk("ac_undervolt"),
                        ground_ripple=ilk("ground_ripple"),
                        ground_leak=ilk("ground_leak"),
                        overcurrent=ilk("overcurrent"),
                        power_on=bit(not self.device.power),
                        ready=bit(self.device.power),
                    )

        assert len(response) == 32
        return response
Example #2
0
    def __init__(self):

        super(WbvalveStreamInterface, self).__init__()
        self.commands = {
            CmdBuilder(self.get_position).escape("st").eos().build(),
            CmdBuilder(self.set_position).escape("wb").int().escape(
                "on").eos().build()
        }
    def __init__(self):

        super(Knrk6StreamInterface, self).__init__()
        # Commands that we expect via serial during normal operation
        self.commands = {
            CmdBuilder(self.get_position).escape("p").eos().build(),
            CmdBuilder(self.set_position).int().eos().build(),
            CmdBuilder(self.get_valve_type).escape("t").eos().build(),
        }
class Danfysik8000StreamInterface(CommonStreamInterface, StreamInterface):
    """
    Stream interface for a Danfysik model 8000.
    """

    protocol = 'model8000'

    commands = CommonStreamInterface.commands + [
        CmdBuilder("set_current").escape("DA 0 ").int().eos().build(),
        CmdBuilder("get_current").escape("AD 8").eos().build(),
        CmdBuilder("init_comms").escape("UNLOCK").build(),
    ]

    @conditional_reply("comms_initialized")
    def get_status(self):
        """
        Respond to the get_status command (S1)
        """
        def bit(condition):
            return "!" if condition else "."

        def ilk(name):
            return bit(name in self.device.active_interlocks)

        response = "{power_off}{pol_normal}{pol_reversed}{reg_transformer}{dac16}{dac17}{is_percent}{spare}"\
                   "{transistor_fault}{sum_interlock}{dc_overcurrent}{dc_overload}{reg_mod_fail}{prereg_fail}" \
                   "{phase_fail}{mps_waterflow_fail}{earth_leak_fail}{thermal_fail}{mps_overtemperature}" \
                   "{door_switch}{mag_waterflow_fail}{mag_overtemp}{mps_not_ready}{spare}".format(
                        spare=bit(False),
                        power_off=bit(not self.device.power),
                        pol_normal=bit(not self.device.negative_polarity),
                        pol_reversed=bit(self.device.negative_polarity),
                        reg_transformer=bit(False),
                        dac16=bit(False),
                        dac17=bit(False),
                        is_percent=bit(False),
                        transistor_fault=ilk("transistor_fault"),
                        sum_interlock=bit(len(self.device.active_interlocks) > 0),
                        dc_overcurrent=ilk("dc_overcurrent"),
                        dc_overload=ilk("dc_overload"),
                        reg_mod_fail=ilk("reg_mod_fail"),
                        prereg_fail=ilk("prereg_fail"),
                        phase_fail=ilk("phase_fail"),
                        mps_waterflow_fail=ilk("mps_waterflow_fail"),
                        earth_leak_fail=ilk("earth_leak_fail"),
                        thermal_fail=ilk("thermal_fail"),
                        mps_overtemperature=ilk("mps_overtemperature"),
                        door_switch=ilk("door_switch"),
                        mag_waterflow_fail=ilk("mag_waterflow_fail"),
                        mag_overtemp=ilk("mag_overtemp"),
                        mps_not_ready=bit(not self.device.power),
                    )

        assert len(response) == 24, "length should have been 24 but was {}".format(len(response))
        return response
Example #5
0
class Dh2000StreamInterface(StreamInterface):

    # Commands that we expect via serial during normal operation
    commands = {
        CmdBuilder("get_status").escape("&STS!").eos().build(),
        CmdBuilder("close_shutter").escape("&CLOSEA!").eos().build(),
        CmdBuilder("open_shutter").escape("&OPENA!").eos().build(),
        CmdBuilder("invalid_command").eos().build(),
    }

    in_terminator = "\r"
    out_terminator = "\r"

    ACK = "&ACK!" + out_terminator

    @staticmethod
    def handle_error(request, error):
        """
        Prints an error message if a command is not recognised.

        Args:
            request : Request.
            error: The error that has occurred.
        Returns:
            None.
        """

        print("An error occurred at request {}: {}".format(request, error))

    @conditional_reply("is_connected")
    def invalid_command(self):
        return "&NAC!{}&TYPERR!".format(self.out_terminator)

    @conditional_reply("is_connected")
    def close_shutter(self):
        self._device.shutter_is_open = False

        return self.ACK

    @conditional_reply("is_connected")
    def open_shutter(self):
        self._device.shutter_is_open = True

        return self.ACK

    @conditional_reply("is_connected")
    def get_status(self):
        shutter = self._device.shutter_is_open
        interlock = self._device.interlock_is_triggered

        status_string = "{ACK}\n&A{shutter},B0,I{intlock}!".format(ACK=self.ACK, shutter=int(shutter), intlock=int(interlock))

        return status_string
class SuperlogicsStreamInterface(StreamInterface):
    """
    Stream interface for the serial port
    """

    commands = {
        CmdBuilder("get_values").escape("#").arg("[0-9]+").build(),
        CmdBuilder("get_version").escape("$").arg("[0-9]+").escape(
            "F").build()
    }

    in_terminator = "\r"
    out_terminator = "\r"

    def handle_error(self, request, error):
        """
        If command is not recognised print and error

        Args:
            request: requested string
            error: problem

        """
        print("An error occurred at request {}: {}".format(request, error))

    @if_connected
    def get_values(self, address):
        """
        Gets the values from the device

        Returns: List of values, one for each connected channel
        """
        if address not in EXPECTED_ADDRESSES:
            raise ValueError("Invalid address '{}'".format(address))

        values = self._device.values_1 if address == "01" else self._device.values_2
        formatted_values = map(lambda s: "+{0:.2f}".format(s), values)
        return ",".join(formatted_values)

    @if_connected
    def get_version(self, address):
        """
        Get the firmware version from the device
        :param address: the address to read the version from
        :return: string representing the firmware version for the address
        """
        if address not in EXPECTED_ADDRESSES:
            raise ValueError("Invalid address '{}'".format(address))

        version = self._device.version_1 if address == "01" else self._device.version_2
        return "!{0}{1}".format(address, version)
 def __init__(self):
     super(DEVICENAMEStreamInterface, self).__init__()
     # Commands that we expect via serial during normal operation
     self.commands = {
         CmdBuilder(self.catch_all).arg(
             "^#9.*$").build()  # Catch-all command for debugging
     }
    def __init__(self):

        super(Amint2lStreamInterface, self).__init__()
        self.commands = {
            CmdBuilder(self.get_pressure).stx().arg("[A-Fa-f0-9]+").escape(
                "r").build()
        }
Example #9
0
class RkndioStreamInterface(StreamInterface):

    # Commands that we expect via serial during normal operation
    commands = {
        CmdBuilder("get_idn").escape("*IDN?").eos().build(),
        CmdBuilder("get_status").escape("STATUS").eos().build(),
        CmdBuilder("get_error").escape("ERR").eos().build(),
        CmdBuilder("get_d_i_state").escape("READ ").arg("2|3|4|5|6|7").eos().build(),
        CmdBuilder("set_d_o_state").escape("WRITE ").arg("8|9|10|11|12|13").escape(" ").arg("FALSE|TRUE").eos().build()
    }

    in_terminator = "\r\n"
    out_terminator = "\r\n"

    def handle_error(self, request, error):
        """
        Prints an error message if a command is not recognised.

        Args:
            request : Request.
            error: The error that has occurred.
        Returns:
            None.
        """
        self.log.error("An error occurred at request " + repr(request) + ": " + repr(error))

        print("An error occurred at request {}: {}".format(request, error))

    @conditional_reply("connected")
    def get_idn(self):
        return self._device.idn

    @conditional_reply("connected")
    def get_status(self):
        return self._device.status

    @conditional_reply("connected")
    def get_error(self):
        return self._device.error

    @conditional_reply("connected")
    def get_d_i_state(self, pin):
        return self._device.get_input_state(pin)

    @conditional_reply("connected")
    def set_d_o_state(self, pin, state):
        return self._device.set_output_state(pin, state)
    def __init__(self):

        super(Knr1050StreamInterface, self).__init__()
        # Commands that we expect via serial during normal operation
        self.commands = {
            CmdBuilder(self.get_status).escape("STATUS?").eos().build(),
            CmdBuilder(self.start_pump).escape("RAMP:0,").int().escape(
                ",").int().escape(",").int().escape(",").int().escape(
                    ",").int().eos().build(),
            CmdBuilder(self.stop_pump).escape("STOP:1,0").eos().build(),
            CmdBuilder(self.stop_klv).escape("STOP:2").eos().build(),
            CmdBuilder(self.get_pressure_limits).escape("PLIM?").eos().build(),
            CmdBuilder(self.set_pressure_limits).escape("PLIM:").int().escape(
                ",").int().eos().build(),
            CmdBuilder(self.get_remote_mode).escape("REMOTE?").eos().build(),
            CmdBuilder(self.set_remote_mode).escape("REMOTE").eos().build(),
            CmdBuilder(self.set_local_mode).escape("LOCAL").eos().build(),
        }
Example #11
0
class LinmotStreamInterface(StreamInterface):

    in_terminator = "\r\n"
    out_terminator = "\r"

    # Commands that we expect via serial during normal operation
    commands = {
        CmdBuilder("set_position").escape("!SP").int().escape(
            "A").eos().build(),
        CmdBuilder("get_position").escape("!GPA").eos().build(),
        CmdBuilder("get_actual_speed_resolution").escape("!VIA").eos().build(),
        CmdBuilder("get_motor_warn_status").escape("!EWA").eos().build(),
        CmdBuilder("get_motor_error_status").escape("!EEA").eos().build(),
        CmdBuilder("set_maximal_speed").escape("!SV").int().escape(
            "A").eos().build(),
        CmdBuilder("set_maximal_acceleration").escape("!SA").int().escape(
            "A").eos().build(),
    }

    def handle_error(self, request, error):
        err_string = "command was: {}, error was: {}: {}\n".format(
            request, error.__class__.__name__, error)
        print(err_string)
        self.log.error(err_string)
        return err_string

    def set_position(self, target_position):
        self.device.move_to_target(target_position)
        return "#"

    def get_position(self):
        return "#{position}".format(position=self.device.position)

    def get_actual_speed_resolution(self):
        return "#{speed_resolution}".format(
            speed_resolution=self.device.speed_resolution)

    def get_motor_warn_status(self):
        return "#{motor_warn_status}".format(
            motor_warn_status=self.device.motor_warn_status.value)

    def get_motor_error_status(self):
        return "#{motor_error_status}".format(
            motor_error_status=self.device.motor_error_status.value)

    def set_maximal_speed(self, speed):
        self.device.velocity = int(speed)
        return "#"

    def set_maximal_acceleration(self, acceleration):
        self.device.maximal_acceleration = int(acceleration)
        return "#"
Example #12
0
    def __init__(self):

        super(Ilm200StreamInterface, self).__init__()
        # All commands preceded by "@i" where i is the ISOBUS address. Has no impact on emulator so is ignored.
        self.commands = {
            CmdBuilder(Ilm200StreamInterface.get_version).escape("@").int(ignore=True).escape("V").build(),
            CmdBuilder(self.get_status).escape("@").int(ignore=True).escape("X").build(),
            CmdBuilder(self.get_level).escape("@").int(ignore=True).escape("R").int().build(),
            CmdBuilder(self.set_rate_slow).escape("@").int(ignore=True).escape("S").int().build(),
            CmdBuilder(self.set_rate_fast).escape("@").int(ignore=True).escape("T").int().build(),
            CmdBuilder(Ilm200StreamInterface.set_remote_unlocked).escape("@").int(ignore=True).escape("C3").build(),
        }
 def __init__(self):
     super(DMA4500MStreamInterface, self).__init__()
     self.commands = {
         CmdBuilder(self.start).escape("start").eos().build(),
         CmdBuilder(self.abort).escape("abort").eos().build(),
         CmdBuilder(self.finished).escape("finished").eos().build(),
         CmdBuilder(self.set_temperature).escape("set").optional(
             " ").escape("temperature ").arg(".+").eos().build(),
         CmdBuilder(self.get_data).escape("get").optional(" ").escape(
             "data").eos().build(),
         CmdBuilder(self.get_raw_data).escape("get").optional(" ").escape(
             "raw").optional(" ").escape("data").eos().build(),
     }
Example #14
0
    def __init__(self):

        super(KeylkgStreamInterface, self).__init__()
        # Commands that we expect via serial during normal operation
        self.commands = {
            CmdBuilder(self.set_mode).arg("Q0|R0").eos().build(),
            CmdBuilder(self.set_measurement_offset).escape(
                "SW,OF,").int().escape(",").float().eos().build(),
            CmdBuilder(self.get_measurement_offset).escape(
                "SR,OF,").float().eos().build(),
            CmdBuilder(self.get_measurement_mode).escape(
                "SR,HB,").int().eos().build(),
            CmdBuilder(self.set_measurement_mode).escape(
                "SW,HB,").int().escape(",").int().eos().build(),
            CmdBuilder(
                self.get_measurement_value).escape("M").int().eos().build(),
            CmdBuilder(
                self.reset_measurement).escape("VR,").int().eos().build(),
        }
Example #15
0
class Wm323StreamInterface(StreamInterface):

    # Commands that we expect via serial during normal operation
    commands = {
        CmdBuilder("get_status").escape("1RS").eos().build(),
        CmdBuilder("set_speed").escape("1SP ").float().eos().build(),
        CmdBuilder("set_rotation_cw").escape("1RR").eos().build(),
        CmdBuilder("set_rotation_ccw").escape("1RL").eos().build(),
        CmdBuilder("set_running_start").escape("1GO").eos().build(),
        CmdBuilder("set_running_stop").escape("1ST").eos().build(),
    }

    in_terminator = "\r"
    out_terminator = "\r"

    def handle_error(self, request, error):
        err_string = "command was: {}, error was: {}: {}\n".format(
            request, error.__class__.__name__, error)
        print(err_string)
        self.log.error(err_string)
        return err_string

    def get_status(self):
        running_int = 0
        if self.device.running:
            running_int = 1
        return "{} {} {} {} !".format(self.device.type, self.device.speed,
                                      self.device.direction.name, running_int)

    def set_speed(self, speed):
        self.device.speed = speed

    def set_rotation_cw(self):
        self.device.direction = Direction.CW

    def set_rotation_ccw(self):
        self.device.direction = Direction.CCW

    def set_running_start(self):
        self.device.running = True

    def set_running_stop(self):
        self.device.running = False
class RknpsStreamInterface(StreamInterface):
    """
    Stream interface for the serial port
    """

    in_terminator = "\r"
    out_terminator = "\n\r"

    commands = {
        CmdBuilder("get_voltage").escape("AD 2").eos().build(),
        CmdBuilder("get_current").escape("AD 8").eos().build(),
        CmdBuilder("set_current").escape("WA ").int().eos().build(),
        CmdBuilder("get_ra").escape("RA").eos().build(),
        CmdBuilder("set_power").arg("F|N").eos().build(),
        CmdBuilder("reset").escape("RS").eos().build(),
        CmdBuilder("get_status").escape("S1").eos().build(),
        CmdBuilder("get_pol").escape("PO").eos().build(),
        CmdBuilder("set_pol").escape("PO ").arg("\+|-").eos().build(),
        CmdBuilder("get_cmd").escape("CMD").eos().build(),
        CmdBuilder("set_cmd").arg("LOC|REM").eos().build(),
        CmdBuilder("get_adr").escape("ADR").eos().build(),
        CmdBuilder("set_adr").escape("ADR ").any().eos().build(),
    }

    def handle_error(self, request, error):
        """
        If command is not recognised print and error.

        Args:
            request: requested string.
            error: problem.
        """
        err = "An error occurred at request '{}': {}".format(request, error)
        print(err)
        self.log.error(err)
        return err

    @if_connected
    def set_adr(self, address):
        """
        Sets the active address.

        Args:
            address: The address to use for the following commands.
        """
        self._device.set_adr(address)

    @if_connected
    def get_adr(self):
        """
        Gets the active address.

        Returns: a string address.
        """
        return "{}".format(self._device.get_adr())

    @if_connected
    def get_ra(self):
        """
        Gets the value for RA.

        Returns: a number in 10e-4 Amps.
        """
        return "{:d}".format(int(self._device.get_current()))

    @if_connected
    def get_voltage(self):
        """
        Gets the voltage of the power supply.
        """
        return "{:d}".format(int(self._device.get_voltage()))

    @if_connected
    def get_current(self):
        """
        Gets the current of the power supply.
        """
        return "{:d}".format(int(self._device.get_current()))

    @if_connected
    def get_cmd(self):
        """
        Check whether the device is in Local/Remote mode.

        Returns: LOC for local mode, REM for remote mode.
        """
        return "REM" if self.device.is_in_remote_mode() else "LOC"

    @if_connected
    def set_cmd(self, cmd):
        """
        Sets the active address to be in local or remote mode.

        Args:
            cmd: The mode to set.
        """
        if cmd == "REM":
            self._device.set_in_remote_mode(True)
        elif cmd == "LOC":
            self._device.set_in_remote_mode(False)
        else:
            raise ValueError("Invalid argument to set_cmd")

    @if_connected
    def get_pol(self):
        """
        Get the polarity of the device.

        Returns: The polarity as +/-.
        """
        return "+" if self._device.is_polarity_positive() else "-"

    @if_connected
    def set_pol(self, pol):
        """
        Set the polarity of the device.

        Args:
            pol: The polarity to set.
        """
        if pol == "+":
            self._device.set_polarity(True)
        elif pol == "-":
            self._device.set_polarity(False)
        else:
            raise ValueError("Invalid argument to set_polarity")

        return pol

    @if_connected
    def get_status(self):
        """
        Get the status of the device.

        Returns: A character string for the status.
        """
        device = self._device

        status = (
            "{POWER}.!.{SPARE}{SPARE}{SPARE}{SPARE}{TRANS}{ILK}{DCOC}{DCOLOAD}{REGMOD}{PREREG}{PHAS}"
            "{MPSWATER}{EARTHLEAK}{THERMAL}{MPSTEMP}{DOOR}{MAGWATER}{MAGTEMP}"
            "{MPSREADY}{SPARE}"
        ).format(
            POWER=PWR_STRING[device.is_power_on()],
            TRANS=ILK_STRING[device.get_TRANS()],
            ILK=ILK_STRING[device.is_interlock_active()],
            DCOC=ILK_STRING[device.get_DCOC()],
            DCOLOAD=ILK_STRING[device.get_DCOL()],
            REGMOD=ILK_STRING[device.get_REGMOD()],
            PREREG=ILK_STRING[device.get_PREREG()],
            PHAS=ILK_STRING[device.get_PHAS()],
            MPSWATER=ILK_STRING[device.get_MPSWATER()],
            EARTHLEAK=ILK_STRING[device.get_EARTHLEAK()],
            THERMAL=ILK_STRING[device.get_THERMAL()],
            MPSTEMP=ILK_STRING[device.get_MPSTEMP()],
            DOOR=ILK_STRING[device.get_DOOR()],
            MAGWATER=ILK_STRING[device.get_MAGWATER()],
            MAGTEMP=ILK_STRING[device.get_MAGTEMP()],
            MPSREADY=ILK_STRING[device.get_MPSREADY()],
            # The spare interlocks aren't always on/off.
            # Simulate this by making all of the spares "track" one of the other interlocks.
            SPARE=ILK_STRING[device.get_DOOR()],
        )

        return status

    @if_connected
    def set_power(self, power):
        """
        Turn the output power of the PSU on or off.

        Args:
            power: Whether to turn the PSU oN or ofF.
        """
        if power == "N":
            self._device.set_power(True)
            self.log.info('PWR ON')
        elif power == "F":
            self._device.set_power(False)
            self.log.info('PWR OFF')
        else:
            raise ValueError("Invalid argument to set_power")

    @if_connected
    def set_current(self, value):
        """
        Set a value for the appropriate DA.

        Considers only the channel for current.

        Args:
            value: The value to apply.
        """
        self._device.set_current(float(value))

    @if_connected
    def reset(self):
        """
        Reset the device, turn it off and set all values to 0.
        """
        self._device.reset()
Example #17
0
class TDKLambdaGenesysStreamInterface(StreamInterface):
    commands = {
        CmdBuilder("write_voltage").escape("PV ").float().build(),
        CmdBuilder("read_setpoint_voltage").escape("PV?").build(),
        CmdBuilder("read_voltage").escape("MV?").build(),
        CmdBuilder("write_current").escape("PC ").float().build(),
        CmdBuilder("read_setpoint_current").escape("PC?").build(),
        CmdBuilder("read_current").escape("MC?").build(),
        CmdBuilder("remote").escape("RMT 1").build(),
        CmdBuilder("write_power").escape("OUT ").arg("[OFF|ON]").build(),
        CmdBuilder("read_power").escape("OUT?").build(),
        CmdBuilder("initialize_comms").escape("ADR ").int().eos().build(),
    }

    in_terminator = "\r"
    out_terminator = "\r"

    def handle_error(self, request, error):
        self.log.error("Beep boop. Error occurred at " + repr(request) + ": " +
                       repr(error))
        print("Beep boop. Error occurred at " + repr(request) + ": " +
              repr(error))

    @conditional_reply("comms_initialized")
    def read_voltage(self):
        return self._device.voltage

    @conditional_reply("comms_initialized")
    def read_setpoint_voltage(self):
        return self._device.setpoint_voltage

    @conditional_reply("comms_initialized")
    def write_voltage(self, v):
        self._device.setpoint_voltage = v
        return "VOLTAGE SET TO: {}".format(v)

    @conditional_reply("comms_initialized")
    def read_current(self):
        return self._device.current

    @conditional_reply("comms_initialized")
    def read_setpoint_current(self):
        return self._device.setpoint_current

    @conditional_reply("comms_initialized")
    def write_current(self, c):
        self._device.setpoint_current = c
        return "VOLTAGE SET TO: {}".format(c)

    @conditional_reply("comms_initialized")
    def read_power(self):
        return self._device.powerstate

    @conditional_reply("comms_initialized")
    def write_power(self, p):
        self._device.powerstate = p
        return "POWER SET TO {}".format(p)

    @conditional_reply("comms_initialized")
    def remote(self):
        # We can ignore this command
        pass

    # This is the only command the device recognises when uninitialized
    def initialize_comms(self, addr):
        self.device.comms_initialized = True
Example #18
0
class HlgStreamInterface(StreamInterface):
    """
    Stream interface for the serial port
    """

    commands = {
        CmdBuilder("get_level").escape("PM").build(),
        CmdBuilder("set_verbosity").escape("CV").int().build(),
        CmdBuilder("set_prefix").escape("CP").int().build()
    }

    in_terminator = "\r\n"
    out_terminator = "\r\n"

    def handle_error(self, request, error):
        """
        If command is not recognised print and error

        Args:
            request: requested string
            error: problem

        """
        self.log.error("An error occurred at request " + repr(request) + ": " +
                       repr(error))

    def set_verbosity(self, verbosity):
        """
        Set the verbosity of the output from the device

        Args:
            verbosity: 0 normal, 1 labview style (more verbose)

        Returns: confirmation message

        """
        if verbosity not in [0, 1]:
            raise AssertionError(
                "Verbosity must be 0 or 1 was '{}'".format(verbosity))
        self._device.verbosity = verbosity
        if verbosity == 0:
            out_verbose = "Normal"
        else:
            out_verbose = "labVIEW"
        return self._format_output("CV{0}".format(verbosity), "Verbose=",
                                   out_verbose)

    def set_prefix(self, prefix):
        """
        Set the prefix the device returns
        Args:
            prefix: prefix id 0-5 see PREFIXES for details

        Returns: confirmation message

        """
        if not 0 <= prefix < len(PREFIXES):
            raise AssertionError(
                "Prefix must be between 0 and {1} '{0}'".format(
                    prefix, len(PREFIXES)))
        self._device.prefix = prefix
        return self._format_output("CP{0}".format(prefix), "Verbose=",
                                   str(prefix))

    def get_level(self):
        """
        Gets the current level

        Returns: level in correct units or None if no level is set

        """
        if self._device.level is None:
            return None
        else:
            return self._format_output(
                "PM", "Probe value=",
                "{level:.3f} mm".format(level=self._device.level))

    def _format_output(self, echo, verbose_prefix, data):
        """
        Format the output of a command depending on verbosity and prefix settings of device
        Args:
            echo: string to echo back to user
            verbose_prefix: prefix for value in normal verbose mode
            data: data to output

        Returns: formatted output from command

        """
        output_string = echo
        output_string += PREFIXES[self._device.prefix]
        if self._device.verbosity == 0:
            output_string += verbose_prefix
        output_string += data
        return output_string
Example #19
0
class NgpspsuStreamInterface(StreamInterface):

    # Commands that we expect via serial during normal operation
    commands = {
        CmdBuilder("get_version").escape("VER").eos().build(),
        CmdBuilder("start").escape("MON").eos().build(),
        CmdBuilder("stop").escape("MOFF").eos().build(),
        CmdBuilder("read_status").escape("MST").eos().build(),
        CmdBuilder("reset").escape("MRESET").eos().build(),
        CmdBuilder("read_voltage").escape("MRV").build(),
        CmdBuilder("set_voltage_setpoint").escape(
            "MWV:").float().eos().build(),
        CmdBuilder("read_voltage_setpoint").escape("MWV:?").eos().build(),
        CmdBuilder("read_current").escape("MRI").eos().build(),
        CmdBuilder("set_current_setpoint").escape(
            "MWI:").float().eos().build(),
        CmdBuilder("read_current_setpoint").escape("MWI:?").eos().build()
    }

    out_terminator = "\r\n"
    in_terminator = "\r"

    def handle_error(self, request, error):
        """
        Prints an error message if a command is not recognised.

        Args:
            request : Request.
            error: The error that has occurred.
        Returns:
            None.
        """
        self.log.error("An error occurred at request " + repr(request) + ": " +
                       repr(error))

        print("An error occurred at request {}: {}".format(request, error))

    @if_connected
    def get_version(self):
        """
        Returns the model number and firmware of the device

        E.g. "#VER:NGPS 100-50:0.9.01" where "NGPS 100-50" is the model
            number and "0.9.01" is the firmware number.
        """

        return "#VER:{}".format(self._device.model_number_and_firmware)

    @if_connected
    def start(self):
        """
        Starts the device.

        Returns:
            string: "#AK" if successful, #NK:%i if not (%i is an error code.).
        """

        return self._device.start_device()

    @if_connected
    def stop(self):
        """
        Stops the device.

        Returns:
            string: "#AK" if successful, #NK:%i if not (%i is an error code.).
        """

        return self._device.stop_device()

    @if_connected
    def read_status(self):
        """
        Reads the status of the device.

        Returns:
            string: The status of the device as a string of 8 hexadecimal digits.
        """
        device_status = DeviceStatus(self._device.status)
        hexadecimal_status = device_status.in_hexadecimal()
        return "#MST:{}".format(hexadecimal_status)

    @if_connected
    def reset(self):
        """
        Resets the device.

        Returns:
            string: "#AK" if successful, #NK:%i if not (%i is an error code.).
        """

        return self._device.reset_device()

    @if_connected
    def read_voltage(self):
        """
        Reads the voltage.

        Returns:
            string: "#MRV:%f" where %f is the voltage of the device to 6 decimal places.
        """

        return "#MRV:{}".format(self._device.voltage)

    @if_connected
    def set_voltage_setpoint(self, value):
        """
        Sets the voltage setpoint.

        Args:
            value: string of a decimal to 6 decimal places

        Returns:
            string: "#AK" if successful, #NK:%i if not (%i is an error code.).
        """

        return self._device.try_setting_voltage_setpoint(value)

    @if_connected
    def read_voltage_setpoint(self):
        """
        Reads the voltage setpoint.

        Returns:
            string: #MWV:%f" where %f is the voltage setpoint value.
        """

        return "#MWV:{}".format(self._device.voltage_setpoint)

    @if_connected
    def read_current(self):
        """
        Reads the current.

        Returns:
            string: "#MRI:%f" where %f is the current of the device to 6 decimal places.
        """

        return "#MRI:{}".format(self._device.current)

    @if_connected
    def set_current_setpoint(self, value):
        """
        Sets the current setpoint.

        Args:
            value: string of a decimal to 6 decimal places

        Returns:
            string: "#AK" if successful, #NK:%i if not (%i is an error code.).
        """

        return self._device.try_setting_current_setpoint(value)

    @if_connected
    def read_current_setpoint(self):
        """
        Reads the current setpoint.

        Returns:
            string: #MWV:%f" where %f is the current setpoint value.
        """

        return "#MWI:{}".format(self._device.current_setpoint)
class IndfurnStreamInterface(StreamInterface):
    commands = {
        # ID
        CmdBuilder("get_version").escape("?ver").eos().build(),
        CmdBuilder("get_setpoint").escape("?pidSP").eos().build(),
        CmdBuilder("set_setpoint").escape(">pidSP ").float().eos().build(),
        CmdBuilder("get_psu_voltage").escape("?powV").eos().build(),
        CmdBuilder("set_psu_voltage").escape(">powV ").float().eos().build(),
        CmdBuilder("get_psu_current").escape("?powI").eos().build(),
        CmdBuilder("set_psu_current").escape(">powI ").float().eos().build(),
        CmdBuilder("get_output").escape("?pidOUTM").eos().build(),
        CmdBuilder("set_output").escape(">pidOUTM ").float().eos().build(),
        CmdBuilder("get_thermocouple_temperature").escape(
            "?tempTC").eos().build(),
        CmdBuilder("get_thermocouple2_temperature").escape(
            "?tmpTC2").eos().build(),
        CmdBuilder("get_pipe_temperature").escape("?tempP").eos().build(),
        CmdBuilder("get_capacitor_bank_temperature").escape(
            "?tempC").eos().build(),
        CmdBuilder("get_fet_temperature").escape("?tempS").eos().build(),
        CmdBuilder("get_pid_params").escape("?pidTu").eos().build(),
        CmdBuilder("set_pid_params").escape(">pidTu ").float().escape(
            " ").float().escape(" ").float().eos().build(),
        CmdBuilder("get_sample_time").escape("?pidSt").eos().build(),
        CmdBuilder("set_sample_time").escape(">pidSt ").int().eos().build(),
        CmdBuilder("get_psu_direction").escape("?pidDir").eos().build(),
        CmdBuilder("set_psu_direction").escape(">pidDir ").any().eos().build(),
        CmdBuilder("get_pid_mode").escape("?pidMODE").eos().build(),
        CmdBuilder("set_pid_mode").escape(">pidMODE ").char().eos().build(),
        CmdBuilder("set_psu_remote").escape(">powR").eos().build(),
        CmdBuilder("set_psu_local").escape(">powL").eos().build(),
        CmdBuilder("get_psu_control_mode").escape("?powRL").eos().build(),
        CmdBuilder("set_psu_on").escape(">powON").eos().build(),
        CmdBuilder("set_psu_off").escape(">powOFF").eos().build(),
        CmdBuilder("get_psu_power").escape("?powOnOff").eos().build(),
        CmdBuilder("set_led_on").escape(">ledON").eos().build(),
        CmdBuilder("set_led_off").escape(">ledOFF").eos().build(),
        CmdBuilder("get_led").escape("?ledOnOff").eos().build(),
        CmdBuilder("set_hf_on").escape(">oscON").eos().build(),
        CmdBuilder("set_hf_off").escape(">oscOFF").eos().build(),
        CmdBuilder("get_hf_power").escape("?oscOnOff").eos().build(),
        CmdBuilder("get_pid_limits").escape("?pidOUTL").eos().build(),
        CmdBuilder("set_pid_limits").escape(">pidOUTL ").float().escape(
            " ").float().eos().build(),
        CmdBuilder("get_psu_overtemp").escape("?alarmh").eos().build(),
        CmdBuilder("get_psu_overvolt").escape("?alarmv").eos().build(),
        CmdBuilder("get_cooling_water_flow_status").escape(
            "?flowSt").eos().build(),
        CmdBuilder("get_cooling_water_flow").escape("?flowCw").eos().build(),
        CmdBuilder("reset_alarms").escape(">ackAlarm").eos().build(),
        CmdBuilder("set_runmode_on").escape(">pidRUN").eos().build(),
        CmdBuilder("set_runmode_off").escape(">pidSTP").eos().build(),
        CmdBuilder("get_runmode").escape("?pidRUN").eos().build(),
        CmdBuilder("get_sample_holder_material").escape(
            "?sHold").eos().build(),
        CmdBuilder("set_sample_holder_material").escape(
            ">sHold ").string().eos().build(),
        CmdBuilder("get_tc_fault").escape("?faultTC").eos().build(),
        CmdBuilder("get_tc2_fault").escape("?fltTC2").eos().build(),
    }

    in_terminator = "\r\n"
    out_terminator = "\r\n"

    def handle_error(self, request, error):
        err_string = "command was: {}, error was: {}: {}\n".format(
            request, error.__class__.__name__, error)
        print(err_string)
        self.log.error(err_string)
        return "<Unsupported command"

    def get_pid_mode(self):
        return "<pmode {}".format(
            "a" if self.device.pid_mode_automatic else "m")

    def set_pid_mode(self, pid_mode):
        if pid_mode == "a":
            self.device.pid_mode_automatic = True
            return "<ack"
        elif pid_mode == "m":
            self.device.pid_mode_automatic = False
            return "<ack"
        else:
            return "<nak"

    def get_version(self):
        name = "EMULATED FURNACE"
        return "<{name}{terminator}<{name}".format(name=name,
                                                   terminator="\r\n")

    def get_setpoint(self):
        return "<pidsp {:.2f}".format(self.device.setpoint)

    def set_setpoint(self, sp):
        self.device.setpoint = sp
        return "<ack"

    def get_psu_voltage(self):
        return "<powv {:.2f}".format(self.device.psu_voltage)

    def set_psu_voltage(self, voltage):
        self.device.psu_voltage = voltage
        return "<ack"

    def get_psu_current(self):
        return "<powa {:.2f}".format(self.device.psu_current)

    def set_psu_current(self, current):
        self.device.psu_current = current
        return "<ack"

    def get_output(self):
        return "<pidoutm {:.2f}".format(self.device.output)

    def set_output(self, output):
        self.device.output = output
        return "<ack"

    def get_thermocouple_temperature(self):
        return "<temptc {:.2f}".format(self.device.setpoint)

    def get_thermocouple2_temperature(self):
        return "<temptc2 {:.2f}".format(self.device.setpoint)

    def get_pipe_temperature(self):
        return "<tempp {:.2f}".format(self.device.pipe_temperature)

    def get_capacitor_bank_temperature(self):
        return "<tempc {:.2f}".format(self.device.capacitor_bank_temperature)

    def get_fet_temperature(self):
        return "<temps {:.2f}".format(self.device.fet_temperature)

    def get_pid_params(self):
        return "<pidtu {:.2f} {:.2f} {:.2f}".format(self.device.p,
                                                    self.device.i,
                                                    self.device.d)

    def set_pid_params(self, p, i, d):
        self.device.p = p
        self.device.i = i
        self.device.d = d
        return "<ack"

    def get_pid_limits(self):
        return "<PID out limit min max:  {:.2f} {:.2f}".format(
            self.device.pid_lower_limit, self.device.pid_upper_limit)

    def set_pid_limits(self, pid_lower_limit, pid_upper_limit):
        self.device.pid_lower_limit = pid_lower_limit
        self.device.pid_upper_limit = pid_upper_limit
        return "<ack"

    def get_sample_time(self):
        return "<pidst {:d}".format(self.device.sample_time)

    def set_sample_time(self, sample_time):
        self.device.sample_time = sample_time
        return "<ack"

    def get_psu_direction(self):
        return "<piddir {}".format(
            "p" if self.device.direction_heating else "n")

    def set_psu_direction(self, direction):
        if direction == "DIR":
            self.device.direction_heating = True
            return "<ack"
        elif direction == "REV":
            self.device.direction_heating = False
            return "<ack"
        else:
            return "<nak"

    def get_psu_overtemp(self):
        return "<{}".format("on" if self.device.psu_overtemp else "off")

    def get_psu_overvolt(self):
        return "<{}".format("on" if self.device.psu_overvolt else "off")

    def get_cooling_water_flow_status(self):
        return "<{}".format(
            "ok" if self.device.is_cooling_water_flow_ok() else "nok")

    def get_cooling_water_flow(self):
        return "<flow {}".format(self.device.cooling_water_flow)

    def reset_alarms(self):
        self.device.psu_overtemp = False
        self.device.psu_overvolt = False
        return "<ack"

    def set_psu_remote(self):
        self.device.remote_mode = True
        return "<ack"

    def set_psu_local(self):
        self.device.remote_mode = False
        return "<ack"

    def set_psu_on(self):
        self.device.power_supply_on = True
        return "<ack"

    def set_psu_off(self):
        self.device.power_supply_on = False
        return "<ack"

    def set_led_on(self):
        self.device.sample_area_led_on = True
        return "<ack"

    def set_led_off(self):
        self.device.sample_area_led_on = False
        return "<ack"

    def set_hf_on(self):
        self.device.hf_on = True
        return "<ack"

    def set_hf_off(self):
        self.device.hf_on = False
        return "<ack"

    def set_runmode_on(self):
        self.device.running = True
        return "<ack"

    def set_runmode_off(self):
        self.device.running = False
        return "<ack"

    def get_runmode(self):
        return "<{}".format("on" if self.device.running else "off")

    def get_psu_control_mode(self):
        return "<{}".format("remote" if self.device.remote_mode else "local")

    def get_psu_power(self):
        return "<{}".format("on" if self.device.power_supply_on else "off")

    def get_led(self):
        return "<{}".format("on" if self.device.sample_area_led_on else "off")

    def get_hf_power(self):
        return "<{}".format("on" if self.device.hf_on else "off")

    def get_sample_holder_material(self):
        for k, v in SAMPLE_HOLDER_MATERIALS.items():
            if v == self.device.sample_holder_material:
                return "<{}".format(k)
        else:
            return "<nak"

    def set_sample_holder_material(self, material):
        try:
            self.device.sample_holder_material = SAMPLE_HOLDER_MATERIALS[
                material]
            return "<ack"
        except KeyError:
            return "<nak"

    def get_tc_fault(self):
        return "<faulttc {}".format(self.device.thermocouple_1_fault)

    def get_tc2_fault(self):
        return "<faulttc2 {}".format(self.device.thermocouple_2_fault)
Example #21
0
    def __init__(self):
        super(MKS_PR4000B_StreamInterface, self).__init__()

        self.commands = {
            CmdBuilder("get_value").escape("AV").int().eos().build(),
            CmdBuilder("get_valve_status").escape("?VL").int().eos().build(),
            CmdBuilder("set_valve_status").escape("VL").int().escape(",").enum(
                "ON", "OFF").eos().build(),
            CmdBuilder("get_relay_status").escape("?RL").int().eos().build(),
            CmdBuilder("set_relay_status").escape("RL").int().escape(",").enum(
                "ON", "OFF").eos().build(),
            CmdBuilder("get_formula_relay").escape("?FR").int().eos().build(),
            CmdBuilder("set_formula_relay").escape("FR").int().escape(
                ",").any().eos().build(),
            CmdBuilder("get_remote_mode").escape("?RT").eos().build(),
            CmdBuilder("set_remote_mode").escape("RT").escape(",").enum(
                "ON", "OFF").eos().build(),
            CmdBuilder("get_external_input").escape("EX").int().eos().build(),
            CmdBuilder("get_status").escape("ST").eos().build(),
            CmdBuilder("set_range").escape("RG").int().escape(
                ",").float().escape(",").int().eos().build(),
            CmdBuilder("get_range").escape("?RG").int().eos().build(),
            CmdBuilder("get_id").escape("?ID").eos().build(),
        }

        # These get appended to the list of commands above - just map command syntax against emulator property
        numeric_get_and_set_commands = {
            "SP": "setpoint",
            "GN": "gain",
            "OF": "offset",
            "RO": "rtd_offset",
            "IN": "input_range",
            "OT": "output_range",
            "EI": "ext_input_range",
            "EO": "ext_output_range",
            "SC": "scale",
            "UL": "upper_limit",
            "LL": "lower_limit",
            "SM":
            "signalmode",  # As far as the emulator is concerned, this is an int. IOC treats is as enum.
            "LM":
            "limitmode",  # As far as the emulator is concerned, this is an int. IOC treats is as enum.
        }

        getter_factory, setter_factory = self._get_getter_and_setter_factories(
        )

        for command_name, emulator_name in six.iteritems(
                numeric_get_and_set_commands):
            self.commands.update({
                CmdBuilder(setter_factory(emulator_name)).escape(
                    command_name).int().escape(",").float().eos().build(),
                CmdBuilder(getter_factory(emulator_name)).escape(
                    "?{}".format(command_name)).int().eos().build(),
            })
class TpgStreamInterfaceBase(object):
    """
    Stream interface for the serial port for either a TPG26x or TPG36x.
    """

    _last_command = None

    @abc.abstractmethod
    def acknowledgement(self):
        """
        Returns a string which is the device's "acknowledgement" message.
        """

    @abc.abstractmethod
    def output_terminator(self):
        """
        A terminator to add to every reply except acknowledgement messages.
        """

    commands = {
        CmdBuilder("acknowledge_pressure").escape("PRX").build(),
        CmdBuilder("acknowledge_units").escape("UNI").build(),
        CmdBuilder("set_units").escape("UNI").arg("{0|1|2}").build(),
        CmdBuilder("handle_enquiry").enq().build()
    }

    def handle_error(self, request, error):
        """
        If command is not recognised print and error.

        :param request: requested string
        :param error: problem
        :return:
        """
        print("An error occurred at request " + repr(request) + ": " + repr(error))

    def acknowledge_pressure(self):
        """
        Acknowledge that the request for current pressure was received.

        :return: ASCII acknowledgement character (0x6)
        """
        self._last_command = "PRX"
        return self.acknowledgement()

    def acknowledge_units(self):
        """
        Acknowledge that the request for current units was received.

        :return: ASCII acknowledgement character (0x6)
        """
        self._last_command = "UNI"
        return self.acknowledgement()

    def handle_enquiry(self):
        """
        Handle an enquiry using the last command sent.

        :return:
        """

        if self._last_command == "PRX":
            return "{}{}".format(self.get_pressure(), self.output_terminator())
        elif self._last_command == "UNI":
            return "{}{}".format(self.get_units(), self.output_terminator())
        else:
            print("Last command was unknown: " + repr(self._last_command))

    def get_pressure(self):
        """
        Get the current pressure of the TPG26x.

        Returns: a string with pressure and error codes
        """
        return "{},{},{},{}{}".format(self._device.error1, self._device.pressure1, self._device.error2,
                                      self._device.pressure2, self.output_terminator())

    def get_units(self):
        """
        Get the current units of the TPG26x.

        Returns: a string representing the units
        """
        return self._device.units

    def set_units(self, units):
        """
        Set the units of the TPG26x.

        :param units: the unit flag to change the units to
        """
        if self._last_command is None:
            self._last_command = "UNI"
            return self.acknowledgement()

        self._device.units = units
        self._last_command = None
Example #23
0
class MercuryitcInterface(StreamInterface):
    commands = {
        # System-level commands
        CmdBuilder("get_catalog").optional(ISOBUS_PREFIX).escape(
            "READ:SYS:CAT").eos().build(),
        CmdBuilder("read_calib_tables").optional(ISOBUS_PREFIX).escape(
            "READ:FILE:calibration_tables:LIST").eos().build(),
        CmdBuilder("get_nickname").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":NICK").eos().build(),
        CmdBuilder("set_nickname").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":NICK:").any_except(":").eos().build(),

        # Calibration files
        CmdBuilder("get_calib_file").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":CAL:FILE").eos().build(),
        CmdBuilder("set_calib_file").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":CAL:FILE:").any_except(":").eos().build(),

        # Commands to read all info at once
        CmdBuilder("get_all_temp_sensor_details").optional(ISOBUS_PREFIX).
        escape("READ:DEV:").any_except(":").escape(":TEMP").eos().build(),
        CmdBuilder("get_all_pressure_sensor_details").optional(ISOBUS_PREFIX).
        escape("READ:DEV:").any_except(":").escape(":PRES").eos().build(),
        CmdBuilder("get_all_heater_details").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":HTR").eos().build(),
        CmdBuilder("get_all_aux_details").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":AUX").eos().build(),
        CmdBuilder("get_all_level_sensor_details").optional(ISOBUS_PREFIX).
        escape("READ:DEV:").any_except(":").escape(":LVL").eos().build(),

        # Get heater & aux card associations
        CmdBuilder("get_associated_heater").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:HTR").eos().build(),
        CmdBuilder("set_associated_heater").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:HTR:").any_except(":").eos().build(),
        CmdBuilder("get_associated_aux").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:AUX").eos().build(),
        CmdBuilder("set_associated_aux").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:AUX:").any_except(":").eos().build(),

        # PID settings
        CmdBuilder("get_autopid").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:PIDT").eos().build(),
        CmdBuilder("set_autopid").optional(ISOBUS_PREFIX).escape("SET:DEV:").
        any_except(":").escape(":").any_except(":").escape(":LOOP:PIDT:").enum(
            "ON", "OFF").eos().build(),
        CmdBuilder("get_temp_p").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:P").eos().build(),
        CmdBuilder("set_temp_p").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:P:").float().eos().build(),
        CmdBuilder("get_temp_i").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:I").eos().build(),
        CmdBuilder("set_temp_i").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:I:").float().eos().build(),
        CmdBuilder("get_temp_d").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:D").eos().build(),
        CmdBuilder("set_temp_d").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:D:").float().eos().build(),

        # Raw measurements
        CmdBuilder("get_temp_measured").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":TEMP:SIG:TEMP"
                                                ).eos().build(),
        CmdBuilder("get_pres_measured").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(
                ":PRES:SIG:PRES").eos().build(),
        CmdBuilder("get_resistance").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":TEMP:SIG:RES").eos().build(),
        CmdBuilder("get_voltage").optional(ISOBUS_PREFIX).escape("READ:DEV:").
        any_except(":").escape(":PRES:SIG:VOLT").eos().build(),

        # Control loop
        CmdBuilder("get_temp_setpoint").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":TEMP:LOOP:TSET"
                                                ).eos().build(),
        CmdBuilder("get_pres_setpoint").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(
                ":PRES:LOOP:PRST").eos().build(),
        CmdBuilder("set_temp_setpoint").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(
                ":TEMP:LOOP:TSET:").float().escape("K").eos().build(),
        CmdBuilder("set_pres_setpoint").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(
                ":PRES:LOOP:PRST:").float().escape("mB").eos().build(),

        # Heater
        CmdBuilder("get_heater_auto").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:ENAB").eos().build(),
        CmdBuilder("set_heater_auto").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:ENAB:").enum("ON", "OFF").eos().build(),
        CmdBuilder("get_heater_percent").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:HSET").eos().build(),
        CmdBuilder("set_heater_percent").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:HSET:").float().eos().build(),
        CmdBuilder("get_heater_voltage").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":HTR:SIG:VOLT").eos().build(),
        CmdBuilder("get_heater_current").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":HTR:SIG:CURR").eos().build(),
        CmdBuilder("get_heater_power").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":HTR:SIG:POWR").eos().build(),
        CmdBuilder("get_heater_voltage_limit").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":HTR:VLIM").eos().build(),
        CmdBuilder("set_heater_voltage_limit").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(
                ":HTR:VLIM:").float().eos().build(),

        # Gas flow
        CmdBuilder("get_gas_flow_auto").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:FAUT").eos().build(),
        CmdBuilder("set_gas_flow_auto").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:FAUT:").enum("ON", "OFF").eos().build(),
        CmdBuilder("get_gas_flow").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":AUX:SIG:PERC").eos().build(),
        CmdBuilder("set_gas_flow").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(":").any_except(":").escape(
                ":LOOP:FSET:").float().eos().build(),

        # Gas levels
        CmdBuilder("get_nitrogen_level").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":LVL:SIG:NIT:LEV"
                                                ).eos().build(),
        CmdBuilder("get_helium_level").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(
                ":LVL:SIG:HEL:LEV").eos().build(),

        # Level card probe rates
        CmdBuilder("get_helium_probe_speed").optional(ISOBUS_PREFIX).escape(
            "READ:DEV:").any_except(":").escape(":LVL:HEL:PULS:SLOW"
                                                ).eos().build(),
        CmdBuilder("set_helium_probe_speed").optional(ISOBUS_PREFIX).escape(
            "SET:DEV:").any_except(":").escape(
                ":LVL:HEL:PULS:SLOW:").float().eos().build(),
    }

    in_terminator = "\n"
    out_terminator = "\n"

    def handle_error(self, request, error):
        err_string = "command was: {}, error was: {}: {}\n".format(
            request, error.__class__.__name__, error)
        print(err_string)
        self.log.error(err_string)

        return "{}:INVALID".format(request.lstrip(ISOBUS_PREFIX))

    @if_connected
    def get_catalog(self):
        resp = "STAT:SYS:CAT"

        for chan_location, chan in self.device.channels.items():
            resp += ":DEV:{}:{}".format(chan_location, chan.channel_type)

        self.log.info("Device catalog: {}".format(resp))
        return resp

    def _chan_from_id(self, deviceid, expected_type=None):
        """
        Gets a channel object from a provided device identifier.

        Args:
            deviceid: the device identifier e.g. "MB0", "DB1"
            expected_type: The type of the returned channel, one of ChannelTypes (None to skip check; default).
        """
        if deviceid not in self.device.channels.keys():
            msg = "Unknown channel {}".format(deviceid)
            self.log.error(msg)
            raise ValueError(msg)

        if expected_type is not None and self.device.channels[
                deviceid].channel_type != expected_type:
            msg = "Unexpected channel type for {} (expected {}, was {})"\
                .format(deviceid, expected_type, self.device.channels[deviceid].channel_type)
            self.log.error(msg)
            raise ValueError(msg)

        return self.device.channels[deviceid]

    @if_connected
    def get_nickname(self, deviceid, devicetype):
        chan = self._chan_from_id(deviceid, expected_type=devicetype)
        return "STAT:DEV:{}:{}:NICK:{}".format(deviceid, devicetype,
                                               chan.nickname)

    @if_connected
    def set_nickname(self, deviceid, devicetype, nickname):
        chan = self._chan_from_id(deviceid, expected_type=devicetype)
        chan.nickname = nickname
        return "STAT:SET:DEV:{}:{}:NICK:{}:VALID".format(
            deviceid, devicetype, chan.nickname)

    @if_connected
    def read_calib_tables(self):
        return "STAT:FILE:calibration_tables:LIST:fake_table_1;fake_table_2"

    @if_connected
    def get_all_temp_sensor_details(self, deviceid):
        """
        Gets the details for an entire temperature sensor all at once. This is only used by the LabVIEW VI, not by
        the IOC (the ioc queries each parameter individually)
        """

        temp_chan = self._chan_from_id(deviceid,
                                       expected_type=ChannelTypes.TEMP)
        aux_chan = self._chan_from_id(temp_chan.associated_aux_channel,
                                      expected_type=ChannelTypes.AUX)

        return "STAT:DEV:{}:TEMP:".format(deviceid) + \
               ":NICK:{}".format(temp_chan.nickname) + \
               ":LOOP" + \
                 ":AUX:{}".format(temp_chan.associated_aux_channel) + \
                 ":D:{}".format(temp_chan.d) + \
                 ":HTR:{}".format(temp_chan.associated_heater_channel) + \
                 ":I:{}".format(temp_chan.i) + \
                 ":HSET:{}".format(temp_chan.heater_percent) + \
                 ":PIDT:{}".format("ON" if temp_chan.autopid else "OFF") + \
                 ":ENAB:{}".format("ON" if temp_chan.heater_auto else "OFF") + \
                 ":FAUT:{}".format("ON" if temp_chan.gas_flow_auto else "OFF") + \
                 ":FSET:{}".format(aux_chan.gas_flow) + \
                 ":PIDF:{}".format(temp_chan.autopid_file if temp_chan.autopid else "None") + \
                 ":P:{}".format(temp_chan.p) + \
                 ":TSET:{:.4f}K".format(temp_chan.temperature_sp) + \
               ":CAL" + \
                 ":FILE:{}".format(temp_chan.calibration_file) + \
               ":SIG" + \
                 ":TEMP:{:.4f}K".format(temp_chan.temperature) + \
                 ":RES:{:.4f}O".format(temp_chan.resistance)

    @if_connected
    def get_all_pressure_sensor_details(self, deviceid):
        """
        Gets the details for an entire temperature sensor all at once. This is only used by the LabVIEW VI, not by
        the IOC (the ioc queries each parameter individually)
        """

        pres_chan = self._chan_from_id(deviceid,
                                       expected_type=ChannelTypes.PRES)
        aux_chan = self._chan_from_id(pres_chan.associated_aux_channel,
                                      expected_type=ChannelTypes.AUX)

        return "STAT:DEV:{}:PRES:".format(deviceid) + \
               ":NICK:{}".format(pres_chan.nickname) + \
               ":LOOP" + \
                 ":AUX:{}".format(pres_chan.associated_aux_channel) + \
                 ":D:{}".format(pres_chan.d) + \
                 ":HTR:{}".format(pres_chan.associated_heater_channel) + \
                 ":I:{}".format(pres_chan.i) + \
                 ":HSET:{}".format(pres_chan.heater_percent) + \
                 ":PIDT:{}".format("ON" if pres_chan.autopid else "OFF") + \
                 ":ENAB:{}".format("ON" if pres_chan.heater_auto else "OFF") + \
                 ":FAUT:{}".format("ON" if pres_chan.gas_flow_auto else "OFF") + \
                 ":FSET:{}".format(aux_chan.gas_flow) + \
                 ":PIDF:{}".format(pres_chan.autopid_file if pres_chan.autopid else "None") + \
                 ":P:{}".format(pres_chan.p) + \
                 ":TSET:{:.4f}K".format(pres_chan.pressure_sp) + \
               ":CAL" + \
                 ":FILE:{}".format(pres_chan.calibration_file) + \
               ":SIG" + \
                 ":PRES:{:.4f}mBar".format(pres_chan.pressure) + \
                 ":VOLT:{:.4f}V".format(pres_chan.voltage)

    @if_connected
    def get_calib_file(self, deviceid, chan_type):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        return "STAT:DEV:{}:{}:CAL:FILE:{}".format(deviceid, chan_type,
                                                   chan.calibration_file)

    @if_connected
    def set_calib_file(self, deviceid, chan_type, calib_file):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        if not hasattr(chan, "calibration_file"):
            raise ValueError("Unexpected channel type in set_calib_file")
        chan.calibration_file = calib_file
        return "STAT:SET:DEV:{}:{}:CAL:FILE:{}:VALID".format(
            deviceid, chan_type, chan.calibration_file)

    @if_connected
    def get_associated_heater(self, deviceid, chan_type):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        return "STAT:DEV:{}:{}:LOOP:HTR:{}".format(
            deviceid, chan_type, chan.associated_heater_channel)

    @if_connected
    def set_associated_heater(self, deviceid, chan_type, new_heater):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        if new_heater == "None":
            chan.associated_heater_channel = None
        else:
            self._chan_from_id(new_heater, expected_type=ChannelTypes.HTR)
            chan.associated_heater_channel = new_heater
        return "STAT:SET:DEV:{}:{}:LOOP:HTR:{}:VALID".format(
            deviceid, chan_type, chan.associated_heater_channel)

    @if_connected
    def get_associated_aux(self, deviceid, chan_type):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        return "STAT:DEV:{}:{}:LOOP:AUX:{}".format(deviceid, chan_type,
                                                   chan.associated_aux_channel)

    @if_connected
    def set_associated_aux(self, deviceid, chan_type, new_aux):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        if new_aux == "None":
            chan.associated_aux_channel = None
        else:
            self._chan_from_id(new_aux, expected_type=ChannelTypes.AUX)
            chan.associated_aux_channel = new_aux
        return "STAT:SET:DEV:{}:{}:LOOP:AUX:{}:VALID".format(
            deviceid, chan_type, chan.associated_aux_channel)

    @if_connected
    def get_autopid(self, deviceid, chan_type):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        return "STAT:DEV:{}:{}:LOOP:PIDT:{}".format(
            deviceid, chan_type, "ON" if chan.autopid else "OFF")

    @if_connected
    def set_autopid(self, deviceid, chan_type, sp):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        chan.autopid = (sp == "ON")
        return "STAT:SET:DEV:{}:{}:LOOP:PIDT:{}:VALID".format(
            deviceid, chan_type, sp)

    @if_connected
    def get_temp_p(self, deviceid, chan_type):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        return "STAT:DEV:{}:{}:LOOP:P:{:.4f}".format(deviceid, chan_type,
                                                     chan.p)

    @if_connected
    def set_temp_p(self, deviceid, chan_type, p):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        chan.p = p
        return "STAT:SET:DEV:{}:{}:LOOP:P:{:.4f}:VALID".format(
            deviceid, chan_type, p)

    @if_connected
    def get_temp_i(self, deviceid, chan_type):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        return "STAT:DEV:{}:{}:LOOP:I:{:.4f}".format(deviceid, chan_type,
                                                     chan.i)

    @if_connected
    def set_temp_i(self, deviceid, chan_type, i):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        chan.i = i
        return "STAT:SET:DEV:{}:{}:LOOP:I:{:.4f}:VALID".format(
            deviceid, chan_type, i)

    @if_connected
    def get_temp_d(self, deviceid, chan_type):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        return "STAT:DEV:{}:{}:LOOP:D:{:.4f}".format(deviceid, chan_type,
                                                     chan.d)

    @if_connected
    def set_temp_d(self, deviceid, chan_type, d):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        chan.d = d
        return "STAT:SET:DEV:{}:{}:LOOP:D:{:.4f}:VALID".format(
            deviceid, chan_type, d)

    @if_connected
    def get_temp_measured(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.TEMP)
        return "STAT:DEV:{}:TEMP:SIG:TEMP:{:.4f}K".format(
            deviceid, chan.temperature)

    @if_connected
    def get_pres_measured(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.PRES)
        return "STAT:DEV:{}:PRES:SIG:PRES:{:.4f}mB".format(
            deviceid, chan.pressure)

    @if_connected
    def get_temp_setpoint(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.TEMP)
        return "STAT:DEV:{}:TEMP:LOOP:TSET:{:.4f}K".format(
            deviceid, chan.temperature_sp)

    @if_connected
    def get_pres_setpoint(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.PRES)
        return "STAT:DEV:{}:PRES:LOOP:PRST:{:.4f}mB".format(
            deviceid, chan.pressure_sp)

    @if_connected
    def set_temp_setpoint(self, deviceid, sp):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.TEMP)
        chan.temperature_sp = sp
        return "STAT:SET:DEV:{}:TEMP:LOOP:TSET:{:.4f}K:VALID".format(
            deviceid, sp)

    @if_connected
    def set_pres_setpoint(self, deviceid, sp):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.PRES)
        chan.pressure_sp = sp
        return "STAT:SET:DEV:{}:PRES:LOOP:PRST:{:.4f}mB:VALID".format(
            deviceid, sp)

    @if_connected
    def get_resistance(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.TEMP)
        return "STAT:DEV:{}:TEMP:SIG:RES:{:.4f}{}".format(
            deviceid, chan.resistance, self.device.resistance_suffix)

    @if_connected
    def get_voltage(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.PRES)
        return "STAT:DEV:{}:PRES:SIG:VOLT:{:.4f}V".format(
            deviceid, chan.voltage)

    @if_connected
    def get_heater_auto(self, deviceid, chan_type):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        return "STAT:DEV:{}:{}:LOOP:ENAB:{}".format(
            deviceid, chan_type, "ON" if chan.heater_auto else "OFF")

    @if_connected
    def set_heater_auto(self, deviceid, chan_type, sp):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        chan.heater_auto = (sp == "ON")
        return "STAT:SET:DEV:{}:{}:LOOP:ENAB:{}:VALID".format(
            deviceid, chan_type, "ON" if chan.heater_auto else "OFF")

    @if_connected
    def get_gas_flow_auto(self, deviceid, chan_type):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        return "STAT:DEV:{}:{}:LOOP:FAUT:{}".format(
            deviceid, chan_type, "ON" if chan.gas_flow_auto else "OFF")

    @if_connected
    def set_gas_flow_auto(self, deviceid, chan_type, sp):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        chan.gas_flow_auto = (sp == "ON")
        return "STAT:SET:DEV:{}:{}:LOOP:FAUT:{}:VALID".format(
            deviceid, chan_type, "ON" if chan.gas_flow_auto else "OFF")

    @if_connected
    def get_heater_percent(self, deviceid, chan_type):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        return "STAT:DEV:{}:{}:LOOP:HSET:{:.4f}".format(
            deviceid, chan_type, chan.heater_percent)

    @if_connected
    def set_heater_percent(self, deviceid, chan_type, sp):
        chan = self._chan_from_id(deviceid, expected_type=chan_type)
        chan.heater_percent = sp
        return "STAT:SET:DEV:{}:{}:LOOP:HSET:{:.4f}:VALID".format(
            deviceid, chan_type, sp)

    @if_connected
    def get_gas_flow(self, deviceid):
        aux_chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.AUX)
        return "STAT:DEV:{}:AUX:SIG:PERC:{:.4f}%".format(
            deviceid, aux_chan.gas_flow)

    @if_connected
    def set_gas_flow(self, deviceid, chan_type, sp):
        temp_chan = self._chan_from_id(deviceid, expected_type=chan_type)
        aux_chan = self._chan_from_id(temp_chan.associated_aux_channel,
                                      expected_type=ChannelTypes.AUX)
        aux_chan.gas_flow = sp
        return "STAT:SET:DEV:{}:{}:LOOP:FSET:{:.4f}:VALID".format(
            deviceid, chan_type, sp)

    @if_connected
    def get_all_heater_details(self, deviceid):
        """
        Gets the details for an entire heater sensor all at once. This is only used by the LabVIEW VI, not by
        the IOC (the ioc queries each parameter individually)
        """

        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.HTR)

        return "STAT:DEV:{}:HTR".format(deviceid) + \
               ":NICK:{}".format(chan.nickname) + \
               ":VLIM:{}".format(chan.voltage_limit) + \
               ":SIG" + \
                 ":VOLT:{:.4f}V".format(chan.voltage) + \
                 ":CURR:{:.4f}A".format(chan.current) + \
                 ":POWR:{:.4f}W".format(chan.power)

    @if_connected
    def get_heater_voltage_limit(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.HTR)
        return "STAT:DEV:{}:HTR:VLIM:{:.4f}".format(deviceid,
                                                    chan.voltage_limit)

    @if_connected
    def set_heater_voltage_limit(self, deviceid, sp):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.HTR)
        chan.voltage_limit = sp
        return "STAT:SET:DEV:{}:HTR:VLIM:{:.4f}:VALID".format(
            deviceid, chan.voltage_limit)

    @if_connected
    def get_heater_voltage(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.HTR)
        return "STAT:DEV:{}:HTR:SIG:VOLT:{:.4f}V".format(
            deviceid, chan.voltage)

    @if_connected
    def get_heater_current(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.HTR)
        return "STAT:DEV:{}:HTR:SIG:CURR:{:.4f}A".format(
            deviceid, chan.current)

    @if_connected
    def get_heater_power(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.HTR)
        return "STAT:DEV:{}:HTR:SIG:POWR:{:.4f}W".format(deviceid, chan.power)

    @if_connected
    def get_all_aux_details(self, deviceid):
        """
        Gets the details for an entire aux sensor all at once. This is only used by the LabVIEW VI, not by
        the IOC (the ioc queries each parameter individually)
        """
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.AUX)

        return "STAT:DEV:{}:AUX".format(deviceid) + \
               ":NICK:{}".format(chan.nickname) + \
               ":SIG" \
                 ":PERC:{:.4f}".format(chan.gas_flow)

    @if_connected
    def get_all_level_sensor_details(self, deviceid):
        """
        Gets the details for an entire temperature sensor all at once. This is only used by the LabVIEW VI, not by
        the IOC (the ioc queries each parameter individually)
        """

        lvl_chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.LVL)

        return "STAT:DEV:{}:TEMP:".format(deviceid) + \
               ":NICK:{}".format(lvl_chan.nickname) + \
               ":SIG" + \
                 ":NIT:LEV:{:.3f}%".format(lvl_chan.nitrogen_level) + \
                 ":HEL:LEV:{:.3f}%".format(lvl_chan.helium_level)

    @if_connected
    def get_nitrogen_level(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.LVL)

        return "STAT:DEV:{}:LVL:SIG:NIT:LEV:{:.3f}%".format(
            deviceid, chan.nitrogen_level)

    @if_connected
    def get_helium_level(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.LVL)

        return "STAT:DEV:{}:LVL:SIG:HEL:LEV:{:.3f}%".format(
            deviceid, chan.helium_level)

    @if_connected
    def get_helium_probe_speed(self, deviceid):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.LVL)

        return "STAT:DEV:{}:LVL:HEL:PULS:SLOW:{}".format(
            deviceid, "ON" if chan.slow_helium_read_rate else "OFF")

    @if_connected
    def set_helium_probe_speed(self, deviceid, sp):
        chan = self._chan_from_id(deviceid, expected_type=ChannelTypes.LVL)

        chan.slow_helium_read_rate = sp == 1

        return "STAT:SET:DEV:{}:LVL:HEL:PULS:SLOW:{}:VALID".format(
            deviceid, "ON" if chan.slow_helium_read_rate else "OFF")
class MezfliprStreamInterface(StreamInterface):

    # Commands that we expect via serial during normal operation
    commands = {
        CmdBuilder("set_compensation").escape("compensation_current=").float().eos().build(),
        CmdBuilder("get_compensation").escape("compensation_current?").eos().build(),

        CmdBuilder("set_state").escape("device_state=").enum("off", "on").eos().build(),
        CmdBuilder("get_state").escape("device_state?").eos().build(),

        CmdBuilder("get_mode").escape("mode?").eos().build(),
        CmdBuilder("get_params").escape("flipper_params?").eos().build(),

        CmdBuilder("set_flipper_current").escape("flipper_current=").float().eos().build(),
        CmdBuilder("set_flipper_steps").escape("flipper_steps=").any().eos().build(),
        CmdBuilder("set_flipper_analytical").escape("flipper_analytical=").any().eos().build(),
        CmdBuilder("set_flipper_filename").escape("flipper_filename=").any().eos().build(),

    }

    in_terminator = "\n"
    out_terminator = "\n"

    def handle_error(self, request, error):
        err_string = "command was: {}, error was: {}: {}\n".format(request, error.__class__.__name__, error)
        print(err_string)
        self.log.error(err_string)
        return err_string

    @if_connected
    def set_compensation(self, compensation):
        self._device.compensation = compensation
        return "compensation_current={}".format(compensation)

    @if_connected
    def get_compensation(self):
        return "{}".format(self._device.compensation)

    @if_connected
    def get_state(self):
        return "on" if self._device.powered_on else "off"

    @if_connected
    def set_state(self, state):
        if state not in ["on", "off"]:
            raise ValueError("Invalid state")

        self._device.powered_on = state == "on"
        return "device_state={}".format(state)

    @if_connected
    def get_mode(self):
        return self._device.mode

    @if_connected
    def get_params(self):
        self.log.info("Params are: {}".format(self._device.params))
        return "{}".format(self._device.params)

    @if_connected
    def set_flipper_current(self, params):
        self._device.params = params
        self._device.mode = "static"
        return "flipper_current={}".format(params)

    @if_connected
    def set_flipper_steps(self, params):
        self._device.params = params
        self._device.mode = "steps"
        return "flipper_steps={}".format(params)

    @if_connected
    def set_flipper_analytical(self, params):
        self._device.params = params
        self._device.mode = "analytical"
        return "flipper_analytical={}".format(params)

    @if_connected
    def set_flipper_filename(self, params):
        self._device.params = params
        self._device.mode = "file"
        return "flipper_filename={}".format(params)
    def __init__(self):

        super(Jsco4180StreamInterface, self).__init__()
        # Commands that we expect via serial during normal operation
        self.commands = {
            CmdBuilder(self.set_flowrate).float().escape(" flowrate set").eos().build(),
            CmdBuilder(self.get_flowrate_rbv).escape("flowrate load p").eos().build(),
            CmdBuilder(self.get_flowrate).escape("a_flow load p").eos().build(),

            CmdBuilder(self.get_pressure).escape("a_press1 load p").eos().build(),
            CmdBuilder(self.set_pressure_max).int().escape(" pmax set").build(),
            CmdBuilder(self.get_pressure_max).escape("a_pmax load p").eos().build(),
            CmdBuilder(self.set_pressure_min).int().escape(" pmin set").build(),
            CmdBuilder(self.get_pressure_min).escape("a_pmin load p").eos().build(),

            CmdBuilder(self.get_program_runtime).escape("current_time load p").eos().build(),

            CmdBuilder(self.get_component_a).escape("compa load p").eos().build(),
            CmdBuilder(self.get_component_b).escape("compb load p").eos().build(),
            CmdBuilder(self.get_component_c).escape("compc load p").eos().build(),
            CmdBuilder(self.get_component_d).escape("compd load p").eos().build(),
            CmdBuilder(self.set_composition).float().escape(" ").float().escape(" ").float().escape(" ").float().escape(" comp set").eos().build(),

            CmdBuilder(self.get_error).escape("trouble load p").eos().build(),
            CmdBuilder(self.set_error).escape("0 trouble set").build(),

            CmdBuilder(self.set_pump).int().escape(" pump set").eos().build(),
            CmdBuilder(self.get_status).escape("status load p").eos().build(),

            CmdBuilder(self.set_file_number).int().escape(" fileno set").eos().build(),
            CmdBuilder(self.set_file_open).int().escape(" openfile").eos().build(),
            CmdBuilder(self.set_file_closed).int().escape(" closefile").eos().build(),
        }
class Keithley2700StreamInterface(StreamInterface):
    in_terminator = "\r"
    out_terminator = "\r"
    commands = {
        # get_multicmds splits commands by ';' if multiple command strings are received
        CmdBuilder("get_multicmds", arg_sep="",
                   ignore_case=True).get_multicommands(";").build(),

        # Extra split on newline to make compatible with VI
        CmdBuilder("get_multicmds", arg_sep="",
                   ignore_case=True).get_multicommands("\n").build(),
        CmdBuilder("get_idn", ignore_case=True).escape("*IDN?").eos().build(),
        CmdBuilder("empty_queue",
                   ignore_case=True).escape(":SYST:CLE").eos().build(),
        CmdBuilder("clear_buffer",
                   ignore_case=True).escape("TRAC:CLE").eos().build(),
        CmdBuilder("set_measurement",
                   ignore_case=True).escape(":FUNC ").regex("(?:\'|\")").
        arg("VOLT:DC|VOLT:AC|CURR:DC|CURR:AC|RES|FRES|CONT|FREQ|PER").spaces(
            at_least_one=False).regex("(?:\'|\")").spaces(
                at_least_one=False).escape(",").spaces(
                    at_least_one=False).escape("(@101:210)").eos().build(),
        CmdBuilder("get_measurement",
                   ignore_case=True).escape(":FUNC?").eos().build(),
        CmdBuilder("set_buffer_feed", ignore_case=True).escape(
            "TRAC:FEED ").arg("SENS|CALC|NONE").eos().build(),
        CmdBuilder("set_buffer_control", ignore_case=True).escape(
            "TRAC:FEED:CONT ").arg("NEV|NEXT|ALW").eos().build(),
        CmdBuilder("set_buffer_state", ignore_case=True).escape(
            "TRAC:CLE:AUTO ").arg("OFF|ON").eos().build(),
        CmdBuilder("get_buffer_state",
                   ignore_case=True).escape("TRAC:CLE:AUTO?").eos().build(),
        CmdBuilder("get_next_buffer_location",
                   ignore_case=True).escape("TRAC:NEXT?").eos().build(),
        CmdBuilder("get_buffer_stats",
                   ignore_case=True).escape("TRAC:FREE?").eos().build(),
        CmdBuilder("get_readings", arg_sep="", ignore_case=True).escape(
            "TRAC:DATA:SEL? ").int().escape(",").int().eos().build(),
        CmdBuilder("set_buffer_size",
                   ignore_case=True).escape("TRAC:POIN ").int().eos().build(),
        CmdBuilder("get_buffer_size",
                   ignore_case=True).escape("TRAC:POIN?").eos().build(),
        CmdBuilder("set_time_stamp_format", ignore_case=True).escape(
            "TRAC:TST:FORM ").arg("ABS|DELT").eos().build(),
        CmdBuilder("get_time_stamp_format",
                   ignore_case=True).escape("TRAC:TST:FORM?").eos().build(),
        CmdBuilder("get_delay_state",
                   ignore_case=True).escape("TRIG:DEL:AUTO?").eos().build(),
        CmdBuilder("set_delay_state", ignore_case=True).escape(
            "TRIG:DEL:AUTO ").arg("OFF|ON").eos().build(),
        CmdBuilder(
            "set_init_state",
            ignore_case=True).escape("INIT:CONT ").arg("OFF|ON").eos().build(),
        CmdBuilder("get_init_state",
                   ignore_case=True).escape("INIT:CONT?").eos().build(),
        CmdBuilder("set_sample_count",
                   ignore_case=True).escape("SAMP:COUN ").int().eos().build(),
        CmdBuilder("get_sample_count",
                   ignore_case=True).escape("SAMP:COUN?").eos().build(),
        CmdBuilder("set_source", ignore_case=True).escape("TRIG:SOUR ").arg(
            "IMM|TIM|MAN|BUS|EXT").eos().build(),
        CmdBuilder("set_data_elements",
                   ignore_case=True).escape("FORM:ELEM READ,").spaces(
                       at_least_one=False).escape("CHAN,").spaces(
                           at_least_one=False).escape("TST").spaces(
                               at_least_one=False).eos().build(),
        CmdBuilder(
            "set_auto_range_status", arg_sep="",
            ignore_case=True).optional(":").escape("FRES:RANG:AUTO ").arg(
                "OFF|ON").spaces(at_least_one=False).optional(",").spaces(
                    at_least_one=False).optional("(@101:210)").eos().build(),
        CmdBuilder("get_auto_range_status", ignore_case=True).optional(
            ":").escape("FRES:RANG:AUTO?").eos().build(),
        CmdBuilder(
            "set_resistance_digits", arg_sep="",
            ignore_case=True).optional(";").escape(":FRES:DIG ").int().spaces(
                at_least_one=False).escape(", (@").int().escape(
                    ":").int().escape(")").eos().build(),
        CmdBuilder("set_resistance_rate", ignore_case=True).optional(
            ";").escape(":FRES:NPLC ").float().optional("E+0").eos().build(),
        CmdBuilder("set_scan_state", ignore_case=True).escape(
            "ROUT:SCAN:LSEL ").arg("INT|NONE").eos().build(),
        CmdBuilder("get_scan_state",
                   ignore_case=True).escape("ROUT:SCAN:LSEL?").eos().build(),
        CmdBuilder("set_scan_channels", arg_sep="", ignore_case=True).escape(
            "ROUT:SCAN (@").int().escape(":").int().escape(")").eos().build(),
        CmdBuilder("get_scan_channels",
                   ignore_case=True).escape("ROUT:SCAN?").eos().build(),
    }

    def handle_error(self, request, error):
        self.log.error("An error occurred at request" + repr(request) + ": " +
                       repr(error))
        self.log.error(traceback.format_exc())
        print("An error occurred at request '" + repr(request) + "': '" +
              repr(error) + "'")

    def bool_onoff_value(self, string_value):
        if string_value not in ["ON", "OFF"]:
            raise ValueError("Invalid on/off value!")
        return string_value == "ON"

    def enum_onoff_value(self, bool_value):
        if bool_value not in [True, False]:
            raise ValueError("Invalid on/off value!")
        else:
            return int(bool_value)

    def get_idn(self):
        return self._device.idn

    def empty_queue(self):
        self.log.info("Error log emptied")

    def clear_buffer(self):
        self._device.clear_buffer()

    def set_measurement(self, measurement):
        if measurement in MEASUREMENT_TYPE.values():
            self._device.measurement = MEASUREMENT_TYPE.keys()[
                MEASUREMENT_TYPE.values().index(measurement)]
        else:
            raise ValueError("Invalid measurement value!")

    def get_measurement(self):
        return "\"{}\"".format(MEASUREMENT_TYPE[self._device.measurement])

    def set_buffer_feed(self, feed):
        if feed in BUFFER_SOURCE.values():
            self._device.buffer_feed = BUFFER_SOURCE.keys()[
                BUFFER_SOURCE.values().index(feed)]
        else:
            raise ValueError("Invalid feed source value!")

    def set_buffer_control(self, control):
        if control in BUFFER_CONTROL_MODE.values():
            self._device.buffer_control = BUFFER_CONTROL_MODE.keys()[
                BUFFER_CONTROL_MODE.values().index(control)]
        else:
            raise ValueError("Invalid buffer control source value!")

    def set_buffer_state(self, state):
        self._device.buffer_autoclear_on = state

    def get_buffer_state(self):
        return "{}".format(1 if self._device.buffer_autoclear_on else 0)

    def get_next_buffer_location(self):
        """
        :return: String-formatted integer of the next buffer location to retrieve
        """
        next_location = self._device.get_next_buffer_location()
        self.log.info("Next buffer location: {}".format(next_location))
        return "{}".format(next_location)

    def get_buffer_stats(self):
        """
        :return: String containing number of bytes available, and number of bytes used
        """
        return "{}, {}".format(str(self._device.bytes_available),
                               str(self._device.bytes_used))

    def get_readings(self, start, count):
        """
        :param start: Start location in buffer
        :param count:number of readings to retrieve
        :return: String value of readings from buffer
        """

        chunks = []
        start, count = int(start), int(count)
        for buffer_location in range(start, start + count):
            chunks.append("{},{},{}".format(
                self._device.buffer[buffer_location].reading,
                self._device.buffer[buffer_location].timestamp,
                self._device.buffer[buffer_location].channel))

        self.log.info("Returned readings: {}".format("No readings" if (
            len(chunks) == 0) else ",".join(chunks)))
        return ", ".join(chunks)

    def set_buffer_size(self, size):
        self._device.buffer_size = int(size)

    def get_buffer_size(self):
        return self._device.buffer_size

    def set_time_stamp_format(self, timestamp_format):
        if timestamp_format in TIMESTAMP_FORMAT.values():
            self._device.time_stamp_format = TIMESTAMP_FORMAT.keys()[
                TIMESTAMP_FORMAT.values().index(timestamp_format)]
        else:
            raise ValueError("Invalid timestamp format value")

    def get_time_stamp_format(self):
        return TIMESTAMP_FORMAT[self._device.time_stamp_format]

    def get_delay_state(self):
        return self.enum_onoff_value(self._device.auto_delay_on)

    def set_delay_state(self, state):
        self._device.auto_delay_on = self.bool_onoff_value(state)

    def set_init_state(self, state):
        self._device.init_state_on = self.bool_onoff_value(state)

    def get_init_state(self):
        return self.enum_onoff_value(self._device.init_state_on)

    def set_sample_count(self, count):
        self._device.sample_count = count

    def get_sample_count(self):
        return "{0}".format(self._device.sample_count)

    def set_source(self, source):
        if source in CONTROL_SOURCE.values():
            self._device.source = CONTROL_SOURCE.keys()[
                CONTROL_SOURCE.values().index(source)]
        else:
            raise ValueError("Invalid control source value")

    def set_data_elements(self):
        self._device.data_elements = "READ, CHAN, TST"

    def set_auto_range_status(self, state):
        self._device.auto_range_on = self.bool_onoff_value(state)

    def get_auto_range_status(self):
        return self.enum_onoff_value(self._device.auto_range_on)

    def set_resistance_digits(self, digit, start, end):
        self._device.measurement_digits = digit
        self._device.scan_channel_start = start
        self._device.scan_channel_end = end

    def set_resistance_rate(self, rate):
        self._device.nplc = rate

    def set_scan_state(self, state):
        if state in SCAN_STATE.values():
            self._device.scan_state_status = SCAN_STATE.keys()[
                SCAN_STATE.values().index(state)]
        else:
            raise ValueError("Invalid scan state source value")

    def get_scan_state(self):
        return SCAN_STATE[self._device.scan_state_status]

    def get_scan_channels(self):
        return "(@{}:110,201:{})".format(self._device.scan_channel_start,
                                         self._device.scan_channel_end)

    def set_scan_channels(self, start, end):
        self._device.scan_channel_start = start
        self._device.scan_channel_end = end

    def get_multicmds(self, command, other_commands):
        """
             Added specifically to support use of the VI with the emulator, as the VI sends multiple commands
             in one go, separated by semicolons.
        """
        replies = []
        for cmd_to_find in [command, other_commands]:
            if cmd_to_find != "":
                self.log.info(
                    "Processing {} from combined command".format(cmd_to_find))
                reply = self._process_part_command(cmd_to_find)
                if reply is not None:
                    replies.append(self._process_part_command(cmd_to_find))
        return self.out_terminator.join(replies)

    def _process_part_command(self, cmd_to_find):
        for cmd in self.bound_commands:
            if cmd.can_process(cmd_to_find):
                return cmd.process_request(cmd_to_find)
        self.log.info(
            "Error, unable to find command: '{}'".format(cmd_to_find))
Example #27
0
class FZJDDFCHStreamInterface(StreamInterface):

    """
    Stream interface for the Ethernet port
    """

    commands = {
        CmdBuilder("get_magnetic_bearing_status").arg(".{3}").escape("?;MBON?").build(),
        CmdBuilder("get_all_status").arg(".{3}").escape("?;ASTA?").build(),
        CmdBuilder("set_frequency", arg_sep="").arg(".{3}").escape("!;FACT!;").int().build(),
        CmdBuilder("set_phase", arg_sep="").arg(".{3}").escape("!;PHAS!;").float().build(),
        CmdBuilder("set_magnetic_bearing", arg_sep="").arg(".{3}").escape("!;MAGB!;").any().build(),
        CmdBuilder("set_drive_mode", arg_sep="").arg(".{3}").escape("!;DRIV!;").any().build()
    }

    in_terminator = "\r\n"
    out_terminator = "\r\n"

    def handle_error(self, request, error):

        """
        If command is not recognised, print and error

        Args:
            request: requested string
            error: problem
        """

        self.log.error("An error occurred at request " + repr(request) + ": " + repr(error))

    def set_frequency(self, chopper_name, frequency):

        """
        Sets the frequency setpoint by multiplying input value by reference frequency

        Args:
            chopper_name: Chopper name (C01, C02, C2B, C03)
            frequency: Frequency setpoint multiple (1, 2, 3, ... 12)

        Returns: OK or error
        """

        if self._device.disconnected:
            return None
        if self._device.error_on_set_frequency is None:
            self._device.frequency_setpoint = frequency * self._device.frequency_reference
            reply = "{chopper_name}OK".format(chopper_name=chopper_name)
        else:
            reply = "ERROR;{}".format(self._device.error_on_set_frequency)

        self.log.info(reply)
        return reply

    def set_phase(self, chopper_name, phase):

        """
        Sets the phase setpoint

        Args:
            chopper_name:  Chopper name (C01, C02, C2B, C03)
            phase: Phase setpoint (0.01 ... 359.99)

        Returns: OK or error
        """

        if self._device.disconnected:
            return None
        if self._device.error_on_set_phase is None:
            self._device.phase_setpoint = phase
            reply = "{chopper_name}OK".format(chopper_name=chopper_name)
        else:
            reply = "ERROR;{}".format(self._device.error_on_set_phase)

        self.log.info(reply)
        return reply

    def set_magnetic_bearing(self, chopper_name, magnetic_bearing):

        """
        Sets the state of the magnetic bearings

        Args:
            chopper_name:  Chopper name (C01, C02, C2B, C03)
            magnetic_bearing: boolean value to set magnetic bearings on or off ("ON", "OFF")

        Returns: OK or error
        """

        if self._device.disconnected:
            return None
        if self._device.error_on_set_magnetic_bearing is None:
            self._device.magnetic_bearing_is_on = ON_OFF.keys()[ON_OFF.values().index(magnetic_bearing)]
            reply = "{chopper_name}OK".format(chopper_name=chopper_name)
        else:
            reply = "ERROR;{}".format(self._device.error_on_set_magnetic_bearing)

        self.log.info(reply)
        return reply

    def set_drive_mode(self, chopper_name, drive_mode):

        """
        Sets the drive mode

        Args:
            chopper_name:   Chopper name (C01, C02, C2B, C03)
            drive_mode: boolean value to set drive ("START", "STOP")

        Returns: OK or error
        """

        if self._device.disconnected:
            return None
        if self._device.error_on_set_drive_mode is None:
            self._device.drive_mode_is_start = START_STOP.keys()[START_STOP.values().index(drive_mode)]
            reply = "{chopper_name}OK".format(chopper_name=chopper_name)
        else:
            reply = "ERROR;{}".format(self._device.error_on_set_drive_mode)

        self.log.info(reply)
        return reply

    def get_magnetic_bearing_status(self, chopper_name):

        """
        Gets the magnetic bearing status

        Args:
            chopper_name:  Chopper name (e.g. C01, C02, C2B, C03)

        Returns: magnetic bearing status
        """

        if self._device.disconnected:
            return None
        device = self._device
        return "{0:3s};MBON?;{}".format(device.chopper_name, self._device.magnetic_bearing_status)

    def get_all_status(self, chopper_name):

        """
        Gets the status as a single string

        Args:
            chopper_name:  Chopper name (e.g. C01, C02, C2B, C03)

        Returns: string containing values for all parameters
        """

        device = self._device
        if self._device.disconnected or chopper_name != device.chopper_name:
            return None

        values = [
            "{0:3s}".format(device.chopper_name),
            "ASTA?",  # device echoes command
            "{0:3s}".format(device.chopper_name),
            "{0:2d}".format(device.frequency_setpoint / device.frequency_reference),  # multiplier of reference frequency
            "{0:.2f}".format(device.frequency_setpoint),
            "{0:.2f}".format(device.frequency),
            "{0:.1f}".format(device.phase_setpoint),
            "{0:.1f}".format(device.phase),
            "{0:s}".format(OK_NOK[device.phase_status_is_ok]),
            "{0:s}".format(ON_OFF[device.magnetic_bearing_is_on]),
            "{0:s}".format(OK_NOK[device.magnetic_bearing_status_is_ok]),
            "{0:s}".format(ON_OFF[device.drive_is_on]),
            "{0:s}".format(START_STOP[device.drive_mode_is_start]),
            "{0:.2f}".format(device.drive_l1_current),
            "{0:.2f}".format(device.drive_l2_current),
            "{0:.2f}".format(device.drive_l3_current),
            "{0:s}".format(CW_CCW[device.drive_direction_is_cw]),
            "{0:.2f}".format(device.drive_temperature),
            "{0:.2f}".format(device.phase_outage),
            "{0:2s}".format(device.master_chopper),
            "{0:s}".format(ON_OFF[device.logging_is_on]),
            "{0:s}".format(OK_NOK[False]),  # Device always responds with "NOK" - constant defined in server code
            "{0:s}".format(OK_NOK[device.dsp_status_is_ok]),
            "{0:s}".format(OK_NOK[device.interlock_er_status_is_ok]),
            "{0:s}".format(OK_NOK[device.interlock_vacuum_status_is_ok]),
            "{0:s}".format(OK_NOK[device.interlock_frequency_monitoring_status_is_ok]),
            "{0:s}".format(OK_NOK[device.interlock_magnetic_bearing_amplifier_temperature_status_is_ok]),
            "{0:s}".format(OK_NOK[device.interlock_magnetic_bearing_amplifier_current_status_is_ok]),
            "{0:s}".format(OK_NOK[device.interlock_drive_amplifier_temperature_status_is_ok]),
            "{0:s}".format(OK_NOK[device.interlock_drive_amplifier_current_status_is_ok]),
            "{0:s}".format(OK_NOK[device.interlock_ups_status_is_ok])
        ]

        status_string = ";".join(values)

        return status_string
class TritonStreamInterface(StreamInterface):

    # Commands that we expect via serial during normal operation
    commands = {
        # ID
        CmdBuilder("get_idn").escape("*IDN?").eos().build(),

        # UIDs
        CmdBuilder("get_uid").escape("READ:SYS:DR:CHAN:").arg("[A-Z0-9]+"
                                                              ).eos().build(),

        # PID setpoints
        CmdBuilder("set_p").escape("SET:DEV:").arg("T[0-9]+").escape(
            ":TEMP:LOOP:P:").float().eos().build(),
        CmdBuilder("set_i").escape("SET:DEV:").arg("T[0-9]+").escape(
            ":TEMP:LOOP:I:").float().eos().build(),
        CmdBuilder("set_d").escape("SET:DEV:").arg("T[0-9]+").escape(
            ":TEMP:LOOP:D:").float().eos().build(),

        # PID readbacks
        CmdBuilder("get_p").escape("READ:DEV:").arg("T[0-9]+").escape(
            ":TEMP:LOOP:P").eos().build(),
        CmdBuilder("get_i").escape("READ:DEV:").arg("T[0-9]+").escape(
            ":TEMP:LOOP:I").eos().build(),
        CmdBuilder("get_d").escape("READ:DEV:").arg("T[0-9]+").escape(
            ":TEMP:LOOP:D").eos().build(),

        # Setpoint temperature
        CmdBuilder("set_temperature_setpoint").escape("SET:DEV:").arg(
            "T[0-9]+").escape(":TEMP:LOOP:TSET:").float().eos().build(),
        CmdBuilder("get_temperature_setpoint").escape("READ:DEV:").arg(
            "T[0-9]+").escape(":TEMP:LOOP:TSET").eos().build(),

        # Temperature
        CmdBuilder("get_temp").escape("READ:DEV:").arg("T[0-9]+").escape(
            ":TEMP:SIG:TEMP").eos().build(),

        # Heater range
        CmdBuilder("set_heater_range").escape("SET:DEV:").arg(
            "T[0-9]+").escape(":TEMP:LOOP:RANGE:").float().eos().build(),
        CmdBuilder("get_heater_range").escape("READ:DEV:").arg(
            "T[0-9]+").escape(":TEMP:LOOP:RANGE").eos().build(),

        # Heater type
        CmdBuilder("get_heater_type").escape("READ:DEV:").arg(
            "T[0-9]+").escape(":TEMP:LOOP:HTR").eos().build(),

        # Get heater power
        CmdBuilder("get_heater_power").escape(
            "READ:DEV:{}:HTR:SIG:POWR".format(HEATER_NAME)).eos().build(),

        # Get heater resistance
        CmdBuilder("get_heater_resistance").escape(
            "READ:DEV:{}:HTR:RES".format(HEATER_NAME)).eos().build(),

        # Heater control sensor
        CmdBuilder("get_heater_control_sensor").escape(
            "READ:DEV:{}:HTR:LOOP".format(HEATER_NAME)).eos().build(),

        # Loop mode
        CmdBuilder("get_closed_loop_mode").escape("READ:DEV:").arg(
            "T[0-9]+").escape(":TEMP:LOOP:MODE").eos().build(),
        CmdBuilder("set_closed_loop_mode").escape("SET:DEV:").arg(
            "T[0-9]+").escape(":TEMP:LOOP:MODE:").any().eos().build(),

        # Channel enablement
        CmdBuilder("get_channel_enabled").escape("READ:DEV:").arg(
            "T[0-9]+").escape(":TEMP:MEAS:ENAB").eos().build(),
        CmdBuilder("set_channel_enabled").escape("SET:DEV:").arg(
            "T[0-9]+").escape(":TEMP:MEAS:ENAB:").any().eos().build(),

        # Status
        CmdBuilder("get_status").escape("READ:SYS:DR:STATUS").eos().build(),
        CmdBuilder("get_automation").escape("READ:SYS:DR:ACTN").eos().build(),

        # Pressures
        CmdBuilder("get_pressure").escape("READ:DEV:").arg("P[0-9]+").escape(
            ":PRES:SIG:PRES").eos().build(),

        # System
        CmdBuilder("get_time").escape("READ:SYS:TIME").eos().build(),

        # Sensor info
        CmdBuilder("get_sig").escape("READ:DEV:").arg("T[0-9]+").escape(
            ":TEMP:SIG").eos().build(),
        CmdBuilder("get_excitation").escape("READ:DEV:").arg("T[0-9]+").escape(
            ":TEMP:EXCT").eos().build(),
        CmdBuilder("get_meas").escape("READ:DEV:").arg("T[0-9]+").escape(
            ":TEMP:MEAS").eos().build(),
    }

    in_terminator = "\r\n"
    out_terminator = "\n"

    def handle_error(self, request, error):
        err_string = "command was: {}, error was: {}: {}\n".format(
            request, error.__class__.__name__, error)
        print(err_string)
        self.log.error(err_string)
        return err_string

    def raise_if_channel_is_not_sample_channel(self, chan):
        if str(chan) != self.device.sample_channel:
            raise ValueError("Channel should have been sample channel")

    def get_idn(self):
        return "This is the IDN of this device"

    def get_uid(self, chan):
        return "STAT:SYS:DR:CHAN:{}:{}".format(
            chan, self.device.find_temperature_channel(chan))

    def set_p(self, stage, value):
        self.raise_if_channel_is_not_sample_channel(stage)
        self.device.set_p(float(value))
        return "ok"

    def set_i(self, stage, value):
        self.raise_if_channel_is_not_sample_channel(stage)
        self.device.set_i(float(value))
        return "ok"

    def set_d(self, stage, value):
        self.raise_if_channel_is_not_sample_channel(stage)
        self.device.set_d(float(value))
        return "ok"

    def get_p(self, stage):
        self.raise_if_channel_is_not_sample_channel(stage)
        return "STAT:DEV:{}:TEMP:LOOP:P:{}".format(stage, self.device.get_p())

    def get_i(self, stage):
        self.raise_if_channel_is_not_sample_channel(stage)
        return "STAT:DEV:{}:TEMP:LOOP:I:{}".format(stage, self.device.get_i())

    def get_d(self, stage):
        self.raise_if_channel_is_not_sample_channel(stage)
        return "STAT:DEV:{}:TEMP:LOOP:D:{}".format(stage, self.device.get_d())

    def set_temperature_setpoint(self, chan, value):
        self.raise_if_channel_is_not_sample_channel(chan)
        self.device.set_temperature_setpoint(float(value))
        return "ok"

    def get_temperature_setpoint(self, chan):
        self.raise_if_channel_is_not_sample_channel(chan)
        return "STAT:DEV:{}:TEMP:LOOP:TSET:{}K".format(
            chan, self.device.get_temperature_setpoint())

    def set_heater_range(self, chan, value):
        self.raise_if_channel_is_not_sample_channel(chan)
        self.device.set_heater_range(float(value))
        return "ok"

    def get_heater_range(self, chan):
        self.raise_if_channel_is_not_sample_channel(chan)
        return "STAT:DEV:{}:TEMP:LOOP:RANGE:{}mA".format(
            chan, self.device.get_heater_range())

    def get_heater_type(self, chan):
        self.raise_if_channel_is_not_sample_channel(chan)
        return "STAT:DEV:{}:TEMP:LOOP:HTR:{}".format(chan, HEATER_NAME)

    def get_heater_power(self):
        return "STAT:DEV:{}:HTR:SIG:POWR:{}uW".format(HEATER_NAME,
                                                      self.device.heater_power)

    def get_heater_resistance(self):
        return "STAT:DEV:{}:HTR:RES:{}Ohm".format(
            HEATER_NAME, self.device.heater_resistance)

    def get_heater_current(self):
        return "STAT:DEV:{}:HTR:SIG:CURR:{}mA".format(
            HEATER_NAME, self.device.heater_current)

    def get_closed_loop_mode(self, chan):
        self.raise_if_channel_is_not_sample_channel(chan)
        return "STAT:DEV:{}:TEMP:LOOP:MODE:{}".format(
            chan, "ON" if self.device.get_closed_loop_mode() else "OFF")

    def set_closed_loop_mode(self, chan, mode):
        self.raise_if_channel_is_not_sample_channel(chan)

        if mode not in ["ON", "OFF"]:
            raise ValueError("Invalid mode")

        self.device.set_closed_loop_mode(mode == "ON")
        return "STAT:SET:DEV:{}:TEMP:LOOP:MODE:{}:VALID".format(chan, mode)

    def get_channel_enabled(self, channel):
        return "STAT:DEV:{}:TEMP:MEAS:ENAB:{}"\
            .format(channel, "ON" if self.device.is_channel_enabled(channel) else "OFF")

    def set_channel_enabled(self, channel, newstate):
        newstate = str(newstate)

        if newstate not in ["ON", "OFF"]:
            raise ValueError("New state '{}' not valid.".format(newstate))

        self.device.set_channel_enabled(channel, newstate == "ON")
        return "ok"

    def get_status(self):
        return "STAT:SYS:DR:STATUS:{}".format(self.device.get_status())

    def get_automation(self):
        return "STAT:SYS:DR:ACTN:{}".format(self.device.get_automation())

    def get_temp(self, stage):
        return "STAT:DEV:{}:TEMP:SIG:TEMP:{}K".format(
            stage, self.device.get_temp(str(stage)))

    def get_pressure(self, sensor):
        return "STAT:DEV:{}:PRES:SIG:PRES:{}mB".format(
            sensor, self.device.get_pressure(sensor))

    def get_time(self):
        return datetime.now().strftime("STAT:SYS:TIME:%H:%M:%S")

    def get_heater_control_sensor(self):
        # Always assume heater controls sample. This is true so far at ISIS
        return "STAT:DEV:{}:HTR:LOOP:SENS:{}".format(
            HEATER_NAME, self.device.sample_channel)

    def get_sig(self, chan):
        return "STAT:DEV:{}:TEMP:SIG:TEMP:{}K:RES:{}Ohm".format(
            chan,
            self.device.temperature_stages[chan].temperature,
            self.device.temperature_stages[chan].resistance,
        )

    def get_excitation(self, chan):
        return "STAT:DEV:{}:TEMP:EXCT:TYPE:{}:MAG:{}V".format(
            chan,
            self.device.temperature_stages[chan].excitation_type,
            self.device.temperature_stages[chan].excitation,
        )

    def get_meas(self, chan):
        return "STAT:DEV:{}:TEMP:MEAS:PAUS:{}s:DWEL:{}s:ENAB:ON".format(
            chan,
            self.device.temperature_stages[chan].pause,
            self.device.temperature_stages[chan].dwell,
        )
class ChtobisrStreamInterface(StreamInterface):
    """
        Stream interface for the Coherent OBIS Laser Remote
    """

    commands = {
        CmdBuilder("get_id").escape("*IDN?").build(),
        CmdBuilder("set_reset").escape("*RST").build(),
        CmdBuilder("get_interlock").escape("SYSTEM:LOCK?").build(),
        CmdBuilder("get_status").escape("SYSTEM:STATUS?").build(),
        CmdBuilder("get_faults").escape("SYSTEM:FAULT?").build(),
    }

    in_terminator = "\r\n"
    out_terminator = "\r\n"

    def handle_error(self, request, error):
        """
            If command is not recognised, print and error

        Args:
            request: requested string
            error: problem
        """

        self.log.error("An error occurred at request " + repr(request) + ": " +
                       repr(error))

    @conditional_reply("connected")
    def get_id(self):
        """
            Gets the device Identification string

        :return:  Device ID string
        """

        return "{}".format(self._device.id)

    @conditional_reply("connected")
    def set_reset(self):
        """
            Resets the device

        :return:  none
        """

        self._device.reset()

    @conditional_reply("connected")
    def get_interlock(self):
        """
            Gets the device interlock status

        :return: Interlock status
        """

        return "{}".format(self._device.interlock)

    @conditional_reply("connected")
    def get_status(self):
        """
            Returns status code
        :return: Formatted status code
        """
        return "{:08X}".format(self._device.build_status_code())

    @conditional_reply("connected")
    def get_faults(self):
        """
            Returns faults code
        :return: Formatted fault code
        """
        return "{:08X}".format(self._device.build_fault_code())
Example #30
0
class EurothermStreamInterface(StreamInterface):
    """
    Stream interface for the serial port
    """

    commands = {
        CmdBuilder("get_current_temperature").eot().escape("0011PV").enq().build(),
        CmdBuilder("get_ramp_setpoint").eot().escape("0011SP").enq().build(),
        CmdBuilder("get_output").eot().escape("0011OP").enq().build(),
        CmdBuilder("get_max_output").eot().escape("0011HO").enq().build(),
        CmdBuilder("get_autotune").eot().escape("0011AT").enq().build(),
        CmdBuilder("get_proportional").eot().escape("0011XP").enq().build(),
        CmdBuilder("get_derivative").eot().escape("0011TD").enq().build(),
        CmdBuilder("get_integral").eot().escape("0011TI").enq().build(),
        CmdBuilder("get_highlim").eot().escape("0011HS").enq().build(),
        CmdBuilder("get_lowlim").eot().escape("0011LS").enq().build(),

        CmdBuilder("set_ramp_setpoint", arg_sep="").eot().escape("0011").stx().escape("SL").float().etx().any().build(),
    }

    # The real Eurotherm uses timeouts instead of terminators to assess when a command is finished. To make this work
    # with the emulator we manually added terminators via asyn commands to the device. Lewis will be able to handle this
    # natively in future versions. See: https://github.com/DMSC-Instrument-Data/lewis/pull/262
    in_terminator = "\r\n"
    out_terminator = chr(3)

    def handle_error(self, request, error):
        """
        If command is not recognised print and error

        Args:
            request: requested string
            error: problem

        """
        self.log.error("An error occurred at request " + repr(request) + ": " + repr(error))

    @if_connected
    def get_proportional(self):
        """
        TODO: Get the proportional of the device's PID values
        """
        return "\x02XP0"

    @if_connected
    def get_integral(self):
        """
        TODO: Get the integral of the device's PID values
        """
        return "\x02TI0"

    @if_connected
    def get_derivative(self):
        """
        TODO: Get the derivative of the device's PID values
        """
        return "\x02TD0"

    @if_connected
    def get_output(self):
        """
        TODO: Get the output of the device
        """
        return "\x02OP0"

    @if_connected
    def get_highlim(self):
        """
        TODO: Get the high limit of the device
        """
        return "\x02HS0"

    @if_connected
    def get_lowlim(self):
        """
        TODO: Get the low limit of the device
        """
        return "\x02LS0"

    @if_connected
    def get_max_output(self):
        """
        TODO: Get the max output of the device
        """
        return "\x02HO0"

    @if_connected
    def get_autotune(self):
        """
        TODO: Get the max output of the device
        """
        return "\x02AT0"

    @if_connected
    def get_current_temperature(self):
        """
        Get the current temperature of the device.

        Returns: the current temperature formatted like the Eurotherm protocol.
        """
        return "\x02PV{}".format(self._device.current_temperature)

    @if_connected
    def get_ramp_setpoint(self):
        """
        Get the set point temperature.

        Returns: the current set point temperature formatted like the Eurotherm protocol.
        """
        return "\x02SP{}".format(self._device.ramp_setpoint_temperature)

    @if_connected
    def set_ramp_setpoint(self, temperature, _):
        """
        Set the set point temperature.

        Args:
            temperature: the temperature to set the setpoint to.
            _: unused argument captured by the command.

        """
        self._device.ramp_setpoint_temperature = temperature