Exemplo n.º 1
0
    def query(self, msg_str):
        """Try to query the device.

        Args:
            msg_str (string): Message to be sent.

        Returns:
            success (bool): True if the message was sent and a reply was
                received successfully, False otherwise.
            ans_str (string): Reply received from the device. [numpy.nan] if
                unsuccessful.
        """
        success = False
        ans_str = np.nan

        if not self.is_alive:
            print("ERROR: Device is not connected yet or already closed.")
        else:
            try:
                ans_str = self.device.query(msg_str)
            except visa.VisaIOError as err:
                # Print error and struggle on
                pft(err)
            except:
                raise
            else:
                ans_str = ans_str.strip()
                success = True

        return (success, ans_str)
Exemplo n.º 2
0
    def write(self, msg_str):
        """Try to write a command to the device.

        Args:
            msg_str (string): Message to be sent.

        Returns: True if the message was sent successfully, False otherwise.
            NOTE: It does not indicate whether the message made sense to the
            device.
        """

        if not self.is_alive:
            print("ERROR: Device is not connected yet or already closed.")
            return False

        try:
            self.device.write(msg_str)
        except visa.VisaIOError as err:
            # Print error and struggle on
            pft(err)
            return False
        except:
            raise

        return True
def DAQ_function():
    # Query the Arduino for its state
    success, tmp_state = ard.query_ascii_values("?", delimiter="\t")
    if not (success):
        dprint("'%s' reports IOError" % ard.name)
        return False

    # Parse readings into separate state variables
    try:
        state.time, state.reading_1 = tmp_state
        state.time /= 1000
    except Exception as err:
        pft(err, 3)
        dprint("'%s' reports IOError" % ard.name)
        return False

    # Use Arduino time or PC time?
    USE_PC_TIME = True
    now = time.perf_counter() if USE_PC_TIME else state.time
    if qdev_ard.update_counter_DAQ == 1:
        state.time_0 = now
        state.time = 0
    else:
        state.time = now - state.time_0

    # For demo purposes: Quit automatically after N updates
    if qdev_ard.update_counter_DAQ > 1000:
        app.quit()

    return True
Exemplo n.º 4
0
    def query_status(self):
        """Query the status or error message of the Julabo and store it in the
        class member 'state'. Will be set to numpy.nan if unsuccessful.

        Returns: True if successful, False otherwise.
        """
        success, reply = self.query_("STATUS")
        if success:
            self.state.status = reply

            try:
                status_number = int(self.state.status[:3])
            except (TypeError, ValueError) as err:
                self.state.has_error = np.nan
                pft(err)
            else:
                if status_number < 0:
                    self.state.has_error = True
                else:
                    self.state.has_error = False

            return True

        self.state.status = np.nan
        self.state.has_error = np.nan
        return False
def DAQ_function():
    # Date-time keeping
    str_cur_date, str_cur_time, str_cur_datetime = get_current_date_time()

    # Query the Arduino for its state
    success, tmp_state = ard.query_ascii_values("?", delimiter="\t")
    if not (success):
        dprint("'%s' reports IOError @ %s %s" %
               (ard.name, str_cur_date, str_cur_time))
        return False

    # Parse readings into separate state variables
    try:
        state.time, state.reading_1 = tmp_state
        state.time /= 1000
    except Exception as err:
        pft(err, 3)
        dprint("'%s' reports IOError @ %s %s" %
               (ard.name, str_cur_date, str_cur_time))
        return False

    if USE_PC_TIME:
        state.time = time.perf_counter()

    # Add readings to chart history
    window.history_chart_curve.appendData(state.time, state.reading_1)

    # Logging to file
    log.update(filepath=str_cur_datetime + ".txt")

    # Return success
    return True
Exemplo n.º 6
0
 def jobs_function(self, func, args):
     # Send I/O operation to the device
     try:
         func(*args)
         self.dev.wait_for_OPC()  # Wait for OPC
     except Exception as err:
         pft(err)
Exemplo n.º 7
0
def DAQ_function():
    # Date-time keeping
    str_cur_date, str_cur_time, str_cur_datetime = get_current_date_time()

    # Query the Arduino for its state
    success, tmp_state = ard.query_ascii_values("?", delimiter="\t")
    if not (success):
        dprint("'%s' reports IOError @ %s %s" %
               (ard.name, str_cur_date, str_cur_time))
        return False

    # Parse readings into separate state variables
    try:
        (
            state.time,
            state.dht22_temp,
            state.dht22_humi,
            ds18b20_temp,
        ) = tmp_state
        state.time /= 1000  # Arduino time, [msec] to [s]
    except Exception as err:
        pft(err, 3)
        dprint("'%s' reports IOError @ %s %s" %
               (ard.name, str_cur_date, str_cur_time))
        return False

    # Optional extra sensor to register the heater surface temperature
    # print("%.2f" % ds18b20_temp)

    # We will use PC time instead
    state.time = time.perf_counter()

    # PID control
    pid.set_mode(
        mode=(psu.state.ENA_output and state.pid_enabled
              and not np.isnan(state.dht22_temp)),
        current_input=state.dht22_temp,
        current_output=psu.state.V_source,
    )

    if pid.compute(current_input=state.dht22_temp):
        # New PID output got computed -> send new voltage to PSU
        qdev_psu.send(qdev_psu.dev.set_V_source, pid.output)

        # Print debug info to the terminal
        dprint("Tp=%7.3f   Ti=%7.3f   outp=%7.3f" %
               (pid.pTerm, pid.iTerm, pid.output))

    # Add readings to chart histories
    window.tscurve_dht22_temp.appendData(state.time, state.dht22_temp)
    window.tscurve_dht22_humi.appendData(state.time, state.dht22_humi)
    window.tscurve_power.appendData(state.time, psu.state.P_meas)

    # Logging to file
    log.update(filepath=str_cur_datetime + ".txt", mode="w")

    # Return success
    return True
def test_pft_overshoot_callstack():
    try:
        0 / 0
    except ZeroDivisionError as err:
        with mock.patch("sys.stdout", new=io.StringIO()) as fake_stdout:
            pft(err, 50)

        assert (fake_stdout.getvalue().split("\n")[-2] ==
                "\x1b[1;31mZeroDivisionError: \x1b[1;37mdivision by zero")
    def query_ascii_values(
        self,
        msg: str,
        delimiter="\t",
        raises_on_timeout: bool = False,
    ) -> Tuple[bool, list]:
        r"""Send a message to the serial device and subsequently read the reply.
        Expects a reply in the form of an ASCII string containing a list of
        numeric values, separated by a delimiter. These values will be parsed
        into a list and returned.

        Args:
            msg (:obj:`str`):
                ASCII string to be sent to the serial device.

            delimiter (:obj:`str`, optional):
                Delimiter used in the device's reply.

                Default: `"\\t"`

            raises_on_timeout (:obj:`bool`, optional):
                Should an exception be raised when a write or read timeout
                occurs?

                Default: :const:`False`

        Returns:
            :obj:`tuple`:
                success (:obj:`bool`):
                    True if successful, False otherwise.

                reply_list (:obj:`list`):
                    Reply received from the device and parsed into a list of
                    separate values. The list is empty if unsuccessful.
        """
        success, reply = self.query(msg,
                                    raises_on_timeout=raises_on_timeout,
                                    returns_ascii=True)

        if not success:
            return (False, ())  # --> leaving

        try:
            # NOTE: `ast.literal_eval` chokes when it receives 'nan' so we ditch
            # it and just interpret everything as `float` instead.
            # reply_list = list(map(literal_eval, reply.split(delimiter)))
            reply_list = list(map(float, reply.split(delimiter)))
        except ValueError as err:
            pft(err, 3)
            return (False, ())  # --> leaving
        except Exception as err:
            pft(err, 3)
            sys.exit(0)  # --> leaving

        return (True, reply_list)
Exemplo n.º 10
0
 def jobs_function(self, func, args):
     if func == "signal_GUI_input_field_update":
         # Special instruction
         self.signal_GUI_input_field_update.emit(*args)
     else:
         # Default job processing:
         # Send I/O operation to the device
         try:
             func(*args)
         except Exception as err:
             pft(err)
Exemplo n.º 11
0
    def query_OCP_level(self, channel: int = 1) -> bool:
        """Returns: True if successful, False otherwise.
        """
        success, reply = self.query("OCP%d?" % channel)
        if success:
            if reply[:3] == "CP%d" % channel:
                self.state.OCP_level = float(reply[4:])
                return True
            else:
                pft("Received incorrect reply: %s" % reply)

        return False
Exemplo n.º 12
0
def DAQ_function():
    # Date-time keeping
    str_cur_date, str_cur_time, str_cur_datetime = get_current_date_time()

    # Query the Arduino for its state
    success, tmp_state = ard.query_ascii_values("?", delimiter="\t")
    if not (success):
        dprint(
            "'%s' reports IOError @ %s %s"
            % (ard.name, str_cur_date, str_cur_time)
        )
        return False

    # Parse readings into separate state variables
    try:
        (
            state.time,
            state.ds_temp,
            state.bme_temp,
            state.bme_humi,
            state.bme_pres,
        ) = tmp_state
        state.time /= 1000  # Arduino time, [msec] to [s]
        state.bme_pres /= 100  # [Pa] to [mbar]
    except Exception as err:
        pft(err, 3)
        dprint(
            "'%s' reports IOError @ %s %s"
            % (ard.name, str_cur_date, str_cur_time)
        )
        return False

    # Catch very intermittent DS18B20 sensor errors
    if state.ds_temp <= -127.0:
        state.ds_temp = np.nan

    # We will use PC time instead
    state.time = time.perf_counter()

    # Add readings to chart histories
    window.tscurve_julabo_setp.appendData(state.time, julabo.state.setpoint)
    window.tscurve_julabo_bath.appendData(state.time, julabo.state.bath_temp)
    window.tscurve_ds_temp.appendData(state.time, state.ds_temp)
    window.tscurve_bme_temp.appendData(state.time, state.bme_temp)
    window.tscurve_bme_humi.appendData(state.time, state.bme_humi)
    window.tscurve_bme_pres.appendData(state.time, state.bme_pres)

    # Logging to file
    log.update(filepath=str_cur_datetime + ".txt", mode="w")

    # Return success
    return True
Exemplo n.º 13
0
    def query_ENA_output(self, channel: int = 1) -> bool:
        """Returns: True if the query was received successfully, False otherwise.
        """
        success, reply = self.query("OP%d?" % channel)
        if success:
            try:
                self.state.ENA_output = bool(int(reply))
            except Exception as err:
                pft("Received incorrect reply: %s" % reply)
                self.ser.flushOutput()
                self.ser.flushInput()

        return success
def DAQ_function():
    # Date-time keeping
    str_cur_date, str_cur_time, str_cur_datetime = get_current_date_time()

    state.update_counter_DAQ += 1

    # Keep track of the obtained DAQ rate
    if not state.QET_rate.isValid():
        state.QET_rate.start()
    else:
        # Obtained DAQ rate
        state.rate_accumulator += 1
        dT = state.QET_rate.elapsed()

        if dT >= 1000:  # Evaluate every N elapsed milliseconds. Hard-coded.
            state.QET_rate.restart()
            try:
                state.obtained_DAQ_rate_Hz = state.rate_accumulator / dT * 1e3
            except ZeroDivisionError:
                state.obtained_DAQ_rate_Hz = np.nan

            state.rate_accumulator = 0

    # Query the Arduino for its state
    success, tmp_state = ard.query_ascii_values("?", delimiter="\t")
    if not (success):
        dprint("'%s' reports IOError @ %s %s" %
               (ard.name, str_cur_date, str_cur_time))
        sys.exit(0)

    # Parse readings into separate state variables
    try:
        state.time, state.reading_1 = tmp_state
        state.time /= 1000
    except Exception as err:
        pft(err, 3)
        dprint("'%s' reports IOError @ %s %s" %
               (ard.name, str_cur_date, str_cur_time))
        sys.exit(0)

    if USE_PC_TIME:
        state.time = time.perf_counter()

    # Add readings to chart histories
    window.history_chart_curve.appendData(state.time, state.reading_1)

    # Logging to file
    log.update(filepath=str_cur_datetime + ".txt")

    # We update the GUI right now because this is a singlethread demo
    window.update_GUI()
Exemplo n.º 15
0
    def query_I_source(self, channel: int = 1) -> bool:
        """Returns: True if the query was received successfully, False otherwise.
        """
        success, reply = self.query("I%d?" % channel)
        if success:
            if reply[:2] == "I%d" % channel:
                self.state.I_source = float(reply[3:])
                return True

            pft("Received incorrect reply: %s" % reply)
            self.ser.flushOutput()
            self.ser.flushInput()

        return False
Exemplo n.º 16
0
    def query_I_meas(self, channel: int = 1) -> bool:
        """Returns: True if the query was received successfully, False otherwise.
        """
        success, reply = self.query("I%dO?" % channel)
        if success:
            if reply[-1:] == "A":
                self.state.I_meas = float(reply[:-1])
                self.state.P_meas = self.state.I_meas * self.state.V_meas
                return True

            pft("Received incorrect reply: %s" % reply)
            self.ser.flushOutput()
            self.ser.flushInput()

        return False
Exemplo n.º 17
0
    def wait_for_OPC(self):
        """'Operation complete' query, used for event synchronization. Will wait
        for all device operations to complete or until a timeout is triggered.
        Blocking.

        Returns: True if successful, False otherwise.
        """
        # Returns an ASCII "1" when all pending overlapped operations have been
        # completed.
        success, reply = self.query("*opc?")
        if success and reply == "1":
            return True

        pft("Warning: *opc? timed out at device %s" % self.name)
        return False
Exemplo n.º 18
0
    def set_setpoint_3(self, value: float):
        """Set the temperature setpoint #3. Subsequently, the Julabo is queried
        for the obtained value, which might be different than the one requested.

        Returns: True if all communication was successful, False otherwise.
        """

        try:
            value = float(value)
        except (TypeError, ValueError) as err:
            pft(err)
            return False

        if self.write_("OUT_SP_02 %.2f" % value):
            return self.query_setpoint_3()
        else:
            return False
Exemplo n.º 19
0
    def query_setpoint_3(self):
        """Query the temperature setpoint #3 and store it in the class member
        'state'. Will be set to numpy.nan if unsuccessful.

        Returns: True if successful, False otherwise.
        """
        success, reply = self.query_("IN_SP_02")
        if success:
            try:
                num = float(reply)
            except (TypeError, ValueError) as err:
                pft(err)
            else:
                self.state.setpoint_3 = num
                return True

        self.state.setpoint_3 = np.nan
        return False
Exemplo n.º 20
0
    def query_running(self):
        """Query if the Julabo is running and store it in the class member
        'state'. Will be set to numpy.nan if unsuccessful.

        Returns: True if successful, False otherwise.
        """
        success, reply = self.query_("IN_MODE_05")
        if success:
            try:
                ans = bool(int(reply))
            except (TypeError, ValueError) as err:
                pft(err)
            else:
                self.state.running = ans
                return True

        self.state.running = np.nan
        return False
Exemplo n.º 21
0
    def query_over_temp(self):
        """Query the high-temperature warning limit and store it in the class
        member 'state'. Will be set to numpy.nan if unsuccessful.

        Returns: True if successful, False otherwise.
        """
        success, reply = self.query_("IN_SP_03")
        if success:
            try:
                num = float(reply)
            except (TypeError, ValueError) as err:
                pft(err)
            else:
                self.state.over_temp = num
                return True

        self.state.over_temp = np.nan
        return False
Exemplo n.º 22
0
    def query_bath_temp(self):
        """Query the current bath temperature and store it in the class member
        'state'. Will be set to numpy.nan if unsuccessful.

        Returns: True if successful, False otherwise.
        """
        success, reply = self.query_("IN_PV_00")
        if success:
            try:
                num = float(reply)
            except (TypeError, ValueError) as err:
                pft(err)
            else:
                self.state.bath_temp = num
                return True

        self.state.bath_temp = np.nan
        return False
Exemplo n.º 23
0
    def set_setpoint_preset(self, n: int):
        """Instruct the Julabo to select another setpoint preset.

        Args:
          n (:obj:`int`): Setpoint to be used, either 1, 2 or 3.

        Returns: True if successful, False otherwise.
        """

        if not (n == 1 or n == 2 or n == 3):
            pft("WARNING: Received illegal setpoint preset.\n"
                "Must be either 1, 2 or 3.")
            return False

        if self.write_("OUT_MODE_01 %i" % (n - 1)):
            self.state.setpoint_preset = n
            return True
        else:
            return False
Exemplo n.º 24
0
    def query_temp_unit(self):
        """Query the temperature unit used by the Julabo and store it in the
        class member 'state'. Will be set to numpy.nan if unsuccessful, else
        either "C" or "F".

        Returns: True if successful, False otherwise.
        """
        success, reply = self.query_("IN_SP_06")
        if success:
            try:
                num = int(reply)
            except (TypeError, ValueError) as err:
                pft(err)
            else:
                self.state.temp_unit = "C" if num == 0 else "F"
                return True

        self.state.temp_unit = np.nan
        return False
Exemplo n.º 25
0
    def query_setpoint_preset(self):
        """Query the setpoint preset currently used by the Julabo (#1, #2 or #3)
        and store it in the class member 'state'. Will be set to numpy.nan
        if unsuccessful.

        Returns: True if successful, False otherwise.
        """
        success, reply = self.query_("IN_MODE_01")
        if success:
            try:
                num = int(reply)
            except (TypeError, ValueError) as err:
                pft(err)
            else:
                self.state.setpoint_preset = num + 1
                return True

        self.state.setpoint_preset = np.nan
        return False
Exemplo n.º 26
0
    def query(
        self,
        msg: Union[str, bytes],
        raises_on_timeout: bool = False,
        returns_ascii: bool = True,
    ) -> tuple:
        success, reply = super().query(
            msg,
            raises_on_timeout,
            returns_ascii=False  # Binary I/O, not ASCII
        )

        # The ThermoFlex is more complex in its replies than the average device.
        # Hence:
        if success:
            if (len(reply) >= 4) and reply[3] == 0x0F:
                # Error reported by chiller
                if reply[5] == 1:
                    pft("Bad command received by chiller", 3)
                elif reply[5] == 2:
                    pft("Bad data received by chiller", 3)
                elif reply[5] == 3:
                    pft("Bad checksum received by chiller", 3)
                success = False
            else:
                # We got a reply back from /a/ device, not necessarily a
                # ThermoFlex chiller.
                success = True

        if reply is None:
            reply = np.nan

        return (success, reply)
    def _jobs_function(self, func, args):
        # Send I/O operation to the device
        try:
            func(*args)
        except Exception as err:  # pylint: disable=broad-except
            pft(err)

        # Check to signal auto open or close of an optional peripheral valve
        if func == self.dev.send_setpoint:
            if args[0] == 0:
                # Setpoint was set to 0 --> signal auto close
                self.dev.valve_auto_close_briefly_prevent = False
                self.signal_valve_auto_close.emit()
            else:
                # Flow enabled --> start deadtime on auto close
                #              --> signal auto open
                self.dev.valve_auto_close_briefly_prevent = True
                self.dev.valve_auto_close_start_deadtime = (
                    QDateTime.currentDateTime()
                )
                self.dev.state.prev_flow_rate = -1  # Necessary reset
                self.signal_valve_auto_open.emit()
Exemplo n.º 28
0
    def parse_data_bytes(self, ans_bytes):
        """Parse the data bytes.

        The manual states:
            data_bytes[0] : the qualifier byte
              b.0 to b.3 indicates unit of measure index
              b.4 to b.7 indicates precision of measurement
            data_bytes[1:]: value

        Returns:
            value (float): The decoded float value. [numpy.nan] if unsuccessful.
            uom     (int): Unit of measure index. [numpy.nan] if unsuccessful.
        """
        value = np.nan
        uom = np.nan
        try:
            nn = ans_bytes[4]  # Number of data bytes to follow
            data_bytes = ans_bytes[5:5 + nn]
            pom = data_bytes[0] >> 4  # Precision of measurement
            uom = data_bytes[0] % 0x10  # Unit of measure index
            int_value = int.from_bytes(data_bytes[1:],
                                       byteorder="big",
                                       signed=False)
        except Exception as err:
            pft(err, 3)
        else:
            if pom == 0:
                value = int_value
            elif pom == 1:
                value = int_value * 0.1
            elif pom == 2:
                value = int_value * 0.01
            elif pom == 3:
                value = int_value * 0.001
            elif pom == 4:
                value = int_value * 0.0001

        return (value, uom)
Exemplo n.º 29
0
    def __init__(self,
                 trav_horz: Compax3_servo = None,
                 trav_vert: Compax3_servo = None,
                 **kwargs):
        super().__init__(**kwargs)

        if not (isinstance(trav_horz, Compax3_servo) or trav_horz is None):
            pft("Argument 'trav_horz' is of a wrong type.", 3)
            sys.exit(1)
        if not (isinstance(trav_vert, Compax3_servo) or trav_vert is None):
            pft("Argument 'trav_vert' is of a wrong type.", 3)
            sys.exit(1)

        self.listening_to_arrow_keys = False
        self.trav_horz = trav_horz
        self.trav_vert = trav_vert
        self.horz_pos = np.nan  # [mm]
        self.vert_pos = np.nan  # [mm]
        self.step_size = np.nan  # [mm]

        self.create_GUI()
        self.connect_signals_to_slots()
        self.process_editingFinished_qled_step_size()
    def close(self, ignore_exceptions=False):
        """Cancel all pending serial operations and close the serial port.
        """
        if self.ser is not None:
            try:
                self.ser.cancel_read()
            except:
                pass
            try:
                self.ser.cancel_write()
            except:
                pass

            try:
                self.ser.close()
            except Exception as err:
                if ignore_exceptions:
                    pass
                else:
                    pft(err, 3)
                    sys.exit(0)

        self.is_alive = False