예제 #1
0
    def get_measurements_from_str(self, device):
        try:
            measurements = []
            device_list = device.split(';')
            for each_device_set in device_list:
                device_id = each_device_set.split(',')[0]
                device_measure_id = each_device_set.split(',')[1]

                measurement = get_measurement(
                    device_measure_id)
                if not measurement:
                    return False, None

                last_measurement = read_last_influxdb(
                    device_id,
                    measurement.unit,
                    measurement.measurement,
                    measurement.channel,
                    self.max_measure_age)
                if not last_measurement:
                    return False, None
                else:
                    measurements.append(last_measurement[1])
            return True, measurements
        except urllib3.exceptions.NewConnectionError:
            return False, "Influxdb: urllib3.exceptions.NewConnectionError"
        except Exception as msg:
            return False, "Influxdb: Unknown Error: {err}".format(err=msg)
예제 #2
0
def get_last_measurement(unique_id, unit, measurement, channel, duration_sec):
    """
    Retrieve the latest input measurement

    :return: The latest input value or None if no data available
    :rtype: float or None

    :param unique_id: What unique_id tag to query in the Influxdb
        database (eg. '00000001')
    :type unique_id: str
    :param unit: What unit to query in the Influxdb
        database (eg. 'C', 's')
    :type unit: str
    :param measurement: What measurement to query in the Influxdb
        database (eg. 'temperature', 'duration_time')
    :type measurement: str or None
    :param channel: Channel
    :type channel: int or None
    :param duration_sec: How many seconds to look for a past measurement
    :type duration_sec: int or None
    """

    last_measurement = read_last_influxdb(
        unique_id,
        unit,
        measurement,
        channel,
        duration_sec=duration_sec)

    if last_measurement is not None:
        last_value = last_measurement[1]
        return last_value
예제 #3
0
    def get_last_measurement(self):
        """
        Retrieve the latest input measurement from InfluxDB

        :rtype: None
        """
        self.last_measurement_success = False

        # Get latest measurement from influxdb
        try:
            device_measurement = get_measurement(self.measurement_id)

            if device_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=device_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                device_measurement, conversion)

            self.last_measurement = read_last_influxdb(
                self.device_id,
                unit,
                measurement,
                channel,
                int(self.max_measure_age))

            if self.last_measurement:
                self.last_time = self.last_measurement[0]
                self.last_measurement = self.last_measurement[1]

                utc_dt = datetime.datetime.strptime(
                    self.last_time.split(".")[0],
                    '%Y-%m-%dT%H:%M:%S')
                utc_timestamp = calendar.timegm(utc_dt.timetuple())
                local_timestamp = str(datetime.datetime.fromtimestamp(utc_timestamp))
                self.logger.debug("Latest (CH{ch}, Unit: {unit}): {last} @ {ts}".format(
                    ch=channel,
                    unit=unit,
                    last=self.last_measurement,
                    ts=local_timestamp))
                if calendar.timegm(time.gmtime()) - utc_timestamp > self.max_measure_age:
                    self.logger.error(
                        "Last measurement was {last_sec} seconds ago, however"
                        " the maximum measurement age is set to {max_sec}"
                        " seconds.".format(
                            last_sec=calendar.timegm(time.gmtime()) - utc_timestamp,
                            max_sec=self.max_measure_age
                        ))
                self.last_measurement_success = True
            else:
                self.logger.warning("No data returned from influxdb")
        except requests.ConnectionError:
            self.logger.error("Failed to read measurement from the "
                              "influxdb database: Could not connect.")
        except Exception as except_msg:
            self.logger.exception(
                "Exception while reading measurement from the influxdb "
                "database: {err}".format(err=except_msg))
예제 #4
0
    def get_measurements_from_id(self, device_id, measure_id):
        measurement = get_measurement(measure_id)

        measurement = read_last_influxdb(
            device_id,
            measurement.unit,
            measurement.measurement,
            measurement.channel,
            self.max_measure_age)
        if not measurement:
            return False, None
        return True, measurement
예제 #5
0
    def get_last_measurement(unique_id, measurement, duration_sec):
        """
        Retrieve the latest input measurement

        :return: The latest input value or None if no data available
        :rtype: float or None

        :param unique_id: ID of controller
        :type unique_id: str
        :param measurement: Environmental condition of a input (e.g.
            temperature, humidity, pressure, etc.)
        :type measurement: str
        :param duration_sec: number of seconds to check for a measurement
            in the past.
        :type duration_sec: int
        """
        last_measurement = read_last_influxdb(unique_id,
                                              measurement,
                                              duration_sec=duration_sec)

        if last_measurement is not None:
            last_value = last_measurement[1]
            return last_value
예제 #6
0
    def loop(self):
        if self.timer_loop < time.time():
            while self.timer_loop < time.time():
                self.timer_loop += self.period

            measure = []
            for each_id_set in self.select_measurement:
                device_device_id = each_id_set.split(",")[0]
                device_measure_id = each_id_set.split(",")[1]

                device_measurement = get_measurement(device_measure_id)

                if not device_measurement:
                    self.logger.error("Could not find Device Measurement")
                    return

                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=device_measurement.conversion_id)
                channel, unit, measurement = return_measurement_info(
                    device_measurement, conversion)

                last_measurement = read_last_influxdb(
                    device_device_id,
                    unit,
                    channel,
                    measure=measurement,
                    duration_sec=self.max_measure_age)

                if not last_measurement:
                    self.logger.error(
                        "Could not find measurement within the set Max Age for Device {} and Measurement {}"
                        .format(device_device_id, device_measure_id))
                    return False
                else:
                    measure.append(last_measurement[1])

            stat_mean = float(sum(measure) / float(len(measure)))
            stat_median = median(measure)
            stat_minimum = min(measure)
            stat_maximum = max(measure)
            stdev_ = stdev(measure)
            stdev_mean_upper = stat_mean + stdev_
            stdev_mean_lower = stat_mean - stdev_

            list_measurement = [
                stat_mean, stat_median, stat_minimum, stat_maximum, stdev_,
                stdev_mean_upper, stdev_mean_lower
            ]

            for each_measurement in self.device_measurements.all():
                if each_measurement.is_enabled:
                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=each_measurement.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        each_measurement, conversion)

                    write_influxdb_value(self.unique_id,
                                         unit,
                                         value=list_measurement[channel],
                                         measure=measurement,
                                         channel=0)
예제 #7
0
    def setup_output(self):
        import pigpio

        self.pigpio = pigpio

        self.setup_output_variables(OUTPUT_INFORMATION)

        error = []
        if self.options_channels['pin'][0] is None:
            error.append("Pin must be set")
        if self.options_channels['pwm_hertz'][0] <= 0:
            error.append("PWM Hertz must be a positive value")
        if error:
            for each_error in error:
                self.logger.error(each_error)
            return

        try:
            self.pwm_output = self.pigpio.pi()
            if not self.pwm_output.connected:
                self.logger.error("Could not connect to pigpiod")
                self.pwm_output = None
                return
            if self.options_channels['pwm_library'][0] == 'pigpio_hardware':
                self.pwm_output.hardware_PWM(
                    self.options_channels['pin'][0],
                    self.options_channels['pwm_hertz'][0], 0)
                self.logger.info(
                    "Output setup on Hardware pin {pin} at {hz} Hertz".format(
                        pin=self.options_channels['pin'][0],
                        hz=self.options_channels['pwm_hertz'][0]))
            elif self.options_channels['pwm_library'][0] == 'pigpio_any':
                self.pwm_output.set_PWM_frequency(
                    self.options_channels['pin'][0],
                    self.options_channels['pwm_hertz'][0])
                self.logger.info(
                    "Output setup on Any pin {pin} at {hz} Hertz".format(
                        pin=self.options_channels['pin'][0],
                        hz=self.options_channels['pwm_hertz'][0]))

            self.output_setup = True

            state_string = ""
            if self.options_channels['state_startup'][0] == 0:
                self.output_switch('off')
                self.logger.info("PWM turned off (0 % duty cycle)")
            elif self.options_channels['state_startup'][0] == 'set_duty_cycle':
                self.output_switch(
                    'on', amount=self.options_channels['startup_value'][0])
                self.logger.info(
                    "PWM set to {} % duty cycle (user-specified value)".format(
                        self.options_channels['startup_value'][0]))
            elif self.options_channels['state_startup'][
                    0] == 'last_duty_cycle':
                device_measurement = db_retrieve_table_daemon(
                    DeviceMeasurements).filter(
                        and_(DeviceMeasurements.device_id == self.unique_id,
                             DeviceMeasurements.channel == 0)).first()

                last_measurement = None
                if device_measurement:
                    channel, unit, measurement = return_measurement_info(
                        device_measurement, None)
                    last_measurement = read_last_influxdb(self.unique_id,
                                                          unit,
                                                          channel,
                                                          measure=measurement,
                                                          duration_sec=None)

                if last_measurement:
                    self.logger.info(
                        "PWM set to {} % duty cycle (last known value)".format(
                            last_measurement[1]))
                    self.output_switch('on', amount=last_measurement[1])
                else:
                    self.logger.error(
                        "Output instructed at startup to be set to "
                        "the last known duty cycle, but a last known "
                        "duty cycle could not be found in the measurement "
                        "database")
        except Exception as except_msg:
            self.logger.exception(
                "Output was unable to be setup on pin {pin}: {err}".format(
                    pin=self.options_channels['pin'][0], err=except_msg))
예제 #8
0
    def get_measurement(self, display_id, i):
        try:
            if self.lcd_line[display_id][i]['measure'] == 'BLANK':
                self.lcd_line[display_id][i]['name'] = ''
                self.lcd_line[display_id][i]['unit'] = ''
                self.lcd_line[display_id][i]['measure_val'] = ''
                return True
            elif self.lcd_line[display_id][i]['measure'] == 'IP':
                str_ip_cmd = "ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1  -d'/'"
                ip_out, _, _ = cmd_output(str_ip_cmd)
                self.lcd_line[display_id][i]['name'] = ''
                self.lcd_line[display_id][i]['unit'] = ''
                self.lcd_line[display_id][i]['measure_val'] = ip_out.rstrip(
                ).decode("utf-8")
                return True
            elif self.lcd_line[display_id][i]['measure'] == 'output_state':
                self.lcd_line[display_id][i][
                    'measure_val'] = self.output_state(
                        self.lcd_line[display_id][i]['id'])
                return True
            else:
                if self.lcd_line[display_id][i]['measure'] == 'time':
                    last_measurement = read_last_influxdb(
                        self.lcd_line[display_id][i]['id'],
                        '/.*/',
                        None,
                        None,
                        duration_sec=self.lcd_max_age[display_id][i])
                else:
                    last_measurement = read_last_influxdb(
                        self.lcd_line[display_id][i]['id'],
                        self.lcd_line[display_id][i]['unit'],
                        self.lcd_line[display_id][i]['measure'],
                        self.lcd_line[display_id][i]['channel'],
                        duration_sec=self.lcd_max_age[display_id][i])

                if last_measurement:
                    self.lcd_line[display_id][i]['time'] = last_measurement[0]
                    if self.lcd_decimal_places[display_id][i] == 0:
                        self.lcd_line[display_id][i]['measure_val'] = int(
                            last_measurement[1])
                    else:
                        self.lcd_line[display_id][i]['measure_val'] = round(
                            last_measurement[1],
                            self.lcd_decimal_places[display_id][i])
                    utc_dt = datetime.datetime.strptime(
                        self.lcd_line[display_id][i]['time'].split(".")[0],
                        '%Y-%m-%dT%H:%M:%S')
                    utc_timestamp = calendar.timegm(utc_dt.timetuple())
                    local_timestamp = str(
                        datetime.datetime.fromtimestamp(utc_timestamp))
                    self.logger.debug("Latest {}: {} @ {}".format(
                        self.lcd_line[display_id][i]['measure'],
                        self.lcd_line[display_id][i]['measure_val'],
                        local_timestamp))
                    return True

                else:
                    self.lcd_line[display_id][i]['time'] = None
                    self.lcd_line[display_id][i]['measure_val'] = None
                    self.logger.debug("No data returned from influxdb")
            return False
        except Exception as except_msg:
            self.logger.debug(
                "Failed to read measurement from the influxdb database: "
                "{err}".format(err=except_msg))
            return False
예제 #9
0
 def get_measurements_from_id(self, measure_id, measure_name):
     measurement = read_last_influxdb(measure_id, measure_name,
                                      self.max_measure_age)
     if not measurement:
         return False, None
     return True, measurement
예제 #10
0
    def get_measurement(self):
        """ Gets the sensor's pH measurement via UART/I2C """
        ph = None

        return_dict = measurements_dict.copy()

        # Calibrate the pH measurement based on a temperature measurement
        if (self.calibrate_sensor_measure and
                ',' in self.calibrate_sensor_measure):
            self.logger.debug("pH sensor set to calibrate temperature")

            device_id = self.calibrate_sensor_measure.split(',')[0]
            measurement_id = self.calibrate_sensor_measure.split(',')[1]

            device_measurement = self.device_measurements.filter(
                DeviceMeasurements.unique_id == measurement_id).first()
            if device_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=device_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                device_measurement, conversion)

            last_measurement = read_last_influxdb(
                device_id,
                measurement.unit,
                measurement.measurement,
                measurement.channel,
                self.max_age)

            if last_measurement:
                self.logger.debug(
                    "Latest temperature used to calibrate: {temp}".format(
                        temp=last_measurement[1]))

                atlas_command = AtlasScientificCommand(self.input_dev)
                ret_value, ret_msg = atlas_command.calibrate(
                    'temperature', temperature=last_measurement[1])
                time.sleep(0.5)

                self.logger.debug(
                    "Calibration returned: {val}, {msg}".format(
                        val=ret_value, msg=ret_msg))
            else:
                self.logger.error(
                    "Calibration measurement not found within the past "
                    "{} seconds".format(self.max_age))

        # Read sensor via FTDI
        if self.interface == 'FTDI':
            if self.atlas_sensor_ftdi.setup:
                lines = self.atlas_sensor_ftdi.query('R')
                if lines:
                    self.logger.debug(
                        "All Lines: {lines}".format(lines=lines))

                    # 'check probe' indicates an error reading the sensor
                    if 'check probe' in lines:
                        self.logger.error(
                            '"check probe" returned from sensor')
                    # if a string resembling a float value is returned, this
                    # is out measurement value
                    elif str_is_float(lines[0]):
                        ph = float(lines[0])
                        self.logger.debug(
                            'Value[0] is float: {val}'.format(val=ph))
                    else:
                        # During calibration, the sensor is put into
                        # continuous mode, which causes a return of several
                        # values in one string. If the return value does
                        # not represent a float value, it is likely to be a
                        # string of several values. This parses and returns
                        # the first value.
                        if str_is_float(lines[0].split(b'\r')[0]):
                            ph = lines[0].split(b'\r')[0]
                        # Lastly, this is called if the return value cannot
                        # be determined. Watchthe output in the GUI to see
                        # what it is.
                        else:
                            ph = lines[0]
                            self.logger.error(
                                'Value[0] is not float or "check probe": '
                                '{val}'.format(val=ph))
            else:
                self.logger.error('FTDI device is not set up.'
                                  'Check the log for errors.')

        # Read sensor via UART
        elif self.interface == 'UART':
            if self.atlas_sensor_uart.setup:
                lines = self.atlas_sensor_uart.query('R')
                if lines:
                    self.logger.debug(
                        "All Lines: {lines}".format(lines=lines))

                    # 'check probe' indicates an error reading the sensor
                    if 'check probe' in lines:
                        self.logger.error(
                            '"check probe" returned from sensor')
                    # if a string resembling a float value is returned, this
                    # is out measurement value
                    elif str_is_float(lines[0]):
                        ph = float(lines[0])
                        self.logger.debug(
                            'Value[0] is float: {val}'.format(val=ph))
                    else:
                        # During calibration, the sensor is put into
                        # continuous mode, which causes a return of several
                        # values in one string. If the return value does
                        # not represent a float value, it is likely to be a
                        # string of several values. This parses and returns
                        # the first value.
                        if str_is_float(lines[0].split(b'\r')[0]):
                            ph = lines[0].split(b'\r')[0]
                        # Lastly, this is called if the return value cannot
                        # be determined. Watchthe output in the GUI to see
                        # what it is.
                        else:
                            ph = lines[0]
                            self.logger.error(
                                'Value[0] is not float or "check probe": '
                                '{val}'.format(val=ph))
            else:
                self.logger.error('UART device is not set up.'
                                  'Check the log for errors.')

        # Read sensor via I2C
        elif self.interface == 'I2C':
            if self.atlas_sensor_i2c.setup:
                ph_status, ph_str = self.atlas_sensor_i2c.query('R')
                if ph_status == 'error':
                    self.logger.error(
                        "Sensor read unsuccessful: {err}".format(
                            err=ph_str))
                elif ph_status == 'success':
                    ph = float(ph_str)
            else:
                self.logger.error(
                    'I2C device is not set up. Check the log for errors.')

        return_dict[0]['value'] = ph

        return return_dict
예제 #11
0
    def get_measurement(self, display_id, i):
        try:
            if self.lcd_line[display_id][i]['measure'] == 'TEXT':
                # Display custom, user-entered text
                self.lcd_line[display_id][i]['name'] = ''
                self.lcd_line[display_id][i]['unit'] = ''
                if len(self.lcd_text[display_id][i]) > self.lcd_x_characters:
                    self.lcd_line[display_id][i]['measure_val'] = self.lcd_text[display_id][i][:self.lcd_x_characters]
                else:
                    self.lcd_line[display_id][i]['measure_val'] = self.lcd_text[display_id][i]
                return True
            elif self.lcd_line[display_id][i]['measure'] == 'BLANK':
                # Display an empty line
                self.lcd_line[display_id][i]['name'] = ''
                self.lcd_line[display_id][i]['unit'] = ''
                self.lcd_line[display_id][i]['measure_val'] = ''
                return True
            elif self.lcd_line[display_id][i]['measure'] == 'IP':
                # Display the device's IP address
                str_ip_cmd = "ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1  -d'/'"
                ip_out, _, _ = cmd_output(str_ip_cmd)
                self.lcd_line[display_id][i]['name'] = ''
                self.lcd_line[display_id][i]['unit'] = ''
                self.lcd_line[display_id][i]['measure_val'] = ip_out.rstrip().decode("utf-8")
                return True
            elif self.lcd_line[display_id][i]['measure'] == 'output_state':
                # Display the GPIO state
                # TODO: Currently not a selectable option in the LCD Line dropdown
                self.lcd_line[display_id][i]['measure_val'] = self.output_state(
                    self.lcd_line[display_id][i]['id'])
                return True
            else:
                if self.lcd_line[display_id][i]['measure'] == 'time':
                    # Display the time of the last measurement
                    last_measurement = read_last_influxdb(
                        self.lcd_line[display_id][i]['id'],
                        '/.*/',
                        None,
                        duration_sec=self.lcd_max_age[display_id][i])
                else:
                    last_measurement = read_last_influxdb(
                        self.lcd_line[display_id][i]['id'],
                        self.lcd_line[display_id][i]['unit'],
                        self.lcd_line[display_id][i]['channel'],
                        measure=self.lcd_line[display_id][i]['measure'],
                        duration_sec=self.lcd_max_age[display_id][i])

                if last_measurement:
                    self.lcd_line[display_id][i]['time'] = last_measurement[0]
                    if self.lcd_decimal_places[display_id][i] == 0:
                        self.lcd_line[display_id][i]['measure_val'] = int(last_measurement[1])
                    else:
                        self.lcd_line[display_id][i]['measure_val'] = round(
                            last_measurement[1], self.lcd_decimal_places[display_id][i])
                    utc_dt = datetime.datetime.strptime(
                        self.lcd_line[display_id][i]['time'].split(".")[0],
                        '%Y-%m-%dT%H:%M:%S')
                    utc_timestamp = calendar.timegm(utc_dt.timetuple())
                    local_timestamp = str(datetime.datetime.fromtimestamp(utc_timestamp))
                    self.logger.debug("Latest {}: {} @ {}".format(
                        self.lcd_line[display_id][i]['measure'],
                        self.lcd_line[display_id][i]['measure_val'], local_timestamp))
                    return True

                else:
                    self.lcd_line[display_id][i]['time'] = None
                    self.lcd_line[display_id][i]['measure_val'] = None
                    self.logger.debug("No data returned from influxdb")
            return False
        except Exception as except_msg:
            self.logger.debug(
                "Failed to read measurement from the influxdb database: "
                "{err}".format(err=except_msg))
            return False
예제 #12
0
    def setup_output(self):
        import Adafruit_PCA9685

        self.setup_output_variables(OUTPUT_INFORMATION)

        error = []
        if self.pwm_hertz < 40:
            error.append("PWM Hertz must be a value between 40 and 1600")
        if error:
            for each_error in error:
                self.logger.error(each_error)
            return

        try:
            self.pwm_output = Adafruit_PCA9685.PCA9685(
                address=int(str(self.output.i2c_location), 16),
                busnum=self.output.i2c_bus)

            self.pwm_output.set_pwm_freq(self.pwm_hertz)

            self.output_setup = True
            self.logger.debug("Output setup on bus {} at {}".format(
                self.output.i2c_bus, self.output.i2c_location))

            for i in range(16):
                if self.options_channels['state_startup'][i] == 0:
                    self.logger.debug(
                        "Startup state channel {ch}: off".format(ch=i))
                    self.output_switch('off', output_channel=i)
                elif self.options_channels['state_startup'][
                        i] == 'set_duty_cycle':
                    self.logger.debug(
                        "Startup state channel {ch}: on ({dc:.2f} %)".format(
                            ch=i,
                            dc=self.options_channels['startup_value'][i]))
                    self.output_switch(
                        'on',
                        output_channel=i,
                        amount=self.options_channels['startup_value'][i])
                elif self.options_channels['state_startup'][
                        i] == 'last_duty_cycle':
                    self.logger.debug(
                        "Startup state channel {ch}: last".format(ch=i))
                    device_measurement = db_retrieve_table_daemon(
                        DeviceMeasurements).filter(
                            and_(
                                DeviceMeasurements.device_id == self.unique_id,
                                DeviceMeasurements.channel == i)).first()

                    last_measurement = None
                    if device_measurement:
                        channel, unit, measurement = return_measurement_info(
                            device_measurement, None)
                        last_measurement = read_last_influxdb(
                            self.unique_id,
                            unit,
                            channel,
                            measure=measurement,
                            duration_sec=None)

                    if last_measurement:
                        self.logger.debug(
                            "Setting channel {ch} startup duty cycle to last known value of {dc} %"
                            .format(ch=i, dc=last_measurement[1]))
                        self.output_switch('on', amount=last_measurement[1])
                    else:
                        self.logger.error(
                            "Output channel {} instructed at startup to be set to "
                            "the last known duty cycle, but a last known "
                            "duty cycle could not be found in the measurement "
                            "database".format(i))
                else:
                    self.logger.debug(
                        "Startup state channel {ch}: no change".format(ch=i))
        except Exception as except_msg:
            self.logger.exception(
                "Output was unable to be setup: {err}".format(err=except_msg))
예제 #13
0
    def check_pid(self):
        """ Get measurement and apply to PID controller """
        # If PID is active, retrieve measurement and update
        # the control variable.
        # A PID on hold will sustain the current output and
        # not update the control variable.
        if self.is_activated and (not self.is_paused or not self.is_held):
            self.get_last_measurement_pid()

            if self.last_measurement_success:
                if self.setpoint_tracking_type == 'method' and self.setpoint_tracking_id != '':
                    # Update setpoint using a method
                    this_pid = db_retrieve_table_daemon(
                        PID, unique_id=self.unique_id)
                    new_setpoint, ended = calculate_method_setpoint(
                        self.setpoint_tracking_id,
                        PID,
                        this_pid,
                        Method,
                        MethodData,
                        self.logger)
                    if ended:
                        self.method_start_act = 'Ended'
                    if new_setpoint is not None:
                        self.PID_Controller.setpoint = new_setpoint
                    else:
                        self.PID_Controller.setpoint = self.setpoint

                if self.setpoint_tracking_type == 'input-math' and self.setpoint_tracking_id != '':
                    # Update setpoint using an Input or Math
                    device_id = self.setpoint_tracking_id.split(',')[0]
                    measurement_id = self.setpoint_tracking_id.split(',')[1]

                    measurement = get_measurement(measurement_id)
                    if not measurement:
                        return False, None

                    last_measurement = read_last_influxdb(
                        device_id,
                        measurement.unit,
                        measurement.channel,
                        measure=measurement.measurement,
                        duration_sec=self.setpoint_tracking_max_age)

                    if last_measurement[1] is not None:
                        self.PID_Controller.setpoint = last_measurement[1]
                    else:
                        self.logger.debug(
                            "Could not find measurement for Setpoint "
                            "Tracking. Max Age of {} exceeded for measuring "
                            "device ID {} (measurement {})".format(
                                self.setpoint_tracking_max_age,
                                device_id,
                                measurement_id))
                        self.PID_Controller.setpoint = None

                # If autotune activated, determine control variable (output) from autotune
                if self.autotune_activated:
                    if not self.autotune.run(self.last_measurement):
                        self.PID_Controller.control_variable = self.autotune.output

                        if self.autotune_debug:
                            self.logger.info('')
                            self.logger.info("state: {}".format(self.autotune.state))
                            self.logger.info("output: {}".format(self.autotune.output))
                    else:
                        # Autotune has finished
                        timestamp = time.time() - self.autotune_timestamp
                        self.logger.info('')
                        self.logger.info('time:  {0} min'.format(round(timestamp / 60)))
                        self.logger.info('state: {0}'.format(self.autotune.state))

                        if self.autotune.state == PIDAutotune.STATE_SUCCEEDED:
                            for rule in self.autotune.tuning_rules:
                                params = self.autotune.get_pid_parameters(rule)
                                self.logger.info('')
                                self.logger.info('rule: {0}'.format(rule))
                                self.logger.info('Kp: {0}'.format(params.Kp))
                                self.logger.info('Ki: {0}'.format(params.Ki))
                                self.logger.info('Kd: {0}'.format(params.Kd))

                        self.stop_controller(deactivate_pid=True)
                else:
                    # Calculate new control variable (output) from PID Controller
                    self.PID_Controller.update_pid_output(self.last_measurement)

                self.write_pid_values()  # Write variables to database

        # Is PID in a state that allows manipulation of outputs
        if (self.is_activated and
                self.PID_Controller.setpoint is not None and
                (not self.is_paused or self.is_held)):
            self.manipulate_output()
예제 #14
0
    def all_outputs_set_state(self):
        """Turn all outputs on that are set to be on at startup"""
        for each_output_id in self.output_unique_id:
            try:
                if self.output_state_startup[each_output_id] is None:
                    pass  # Don't turn on or off

                # PWM Outputs
                elif self.output_type[each_output_id] in self.output_types[
                        'pwm']:
                    if self.output_state_startup[each_output_id] == '0':
                        self.logger.info(
                            "Setting Output {id} startup duty cycle to 0 %".
                            format(id=each_output_id.split('-')[0]))
                        self.output_on_off(
                            each_output_id,
                            'off',
                            trigger_conditionals=self.
                            trigger_functions_at_start[each_output_id])

                    elif self.output_state_startup[
                            each_output_id] == 'set_duty_cycle':
                        self.logger.info(
                            "Setting Output {id} startup duty cycle to user-set value of {dc} %"
                            .format(
                                id=each_output_id.split('-')[0],
                                dc=self.output_startup_value[each_output_id]))
                        self.output_on_off(
                            each_output_id,
                            'on',
                            output_type='pwm',
                            amount=self.output_startup_value[each_output_id],
                            trigger_conditionals=self.
                            trigger_functions_at_start[each_output_id])

                    elif self.output_state_startup[
                            each_output_id] == 'last_duty_cycle':
                        device_measurement = db_retrieve_table_daemon(
                            DeviceMeasurements).filter(
                                DeviceMeasurements.device_id ==
                                each_output_id).all()
                        measurement = None
                        for each_meas in device_measurement:
                            if each_meas.measurement == 'duty_cycle':
                                measurement = each_meas
                        channel, unit, measurement = return_measurement_info(
                            measurement, None)

                        last_measurement = read_last_influxdb(
                            each_output_id,
                            unit,
                            channel,
                            measure=measurement,
                            duration_sec=None)

                        if last_measurement:
                            self.logger.info(
                                "Setting Output {id} startup duty cycle to last known value of {dc} %"
                                .format(id=each_output_id.split('-')[0],
                                        dc=last_measurement[1]))
                            self.output_on_off(
                                each_output_id,
                                'on',
                                output_type='pwm',
                                amount=last_measurement[1],
                                trigger_conditionals=self.
                                trigger_functions_at_start[each_output_id])
                        else:
                            self.logger.error(
                                "Output {id} instructed at startup to be set to "
                                "the last known duty cycle, but a last known "
                                "duty cycle could not be found in the measurement "
                                "database".format(
                                    id=each_output_id.split('-')[0]))

                # Non-PWM outputs
                elif self.output_state_startup[each_output_id] == '1':
                    self.logger.info(
                        "Setting Output {id} startup state to ON".format(
                            id=each_output_id.split('-')[0]))
                    self.output_on_off(
                        each_output_id,
                        'on',
                        trigger_conditionals=self.
                        trigger_functions_at_start[each_output_id])

                elif self.output_state_startup[each_output_id] == '0':
                    self.logger.info(
                        "Setting Output {id} startup state to OFF".format(
                            id=each_output_id.split('-')[0]))
                    self.output_on_off(each_output_id,
                                       'off',
                                       trigger_conditionals=False)
            except:
                self.logger.error(
                    "Could not set state for output {}".format(each_output_id))
예제 #15
0
    def check_pid(self):
        """ Get measurement and apply to PID controller """
        # If PID is active, retrieve measurement and update
        # the control variable.
        # A PID on hold will sustain the current output and
        # not update the control variable.
        if self.is_activated and (not self.is_paused or not self.is_held):
            self.get_last_measurement_pid()

            if self.last_measurement_success:
                if self.setpoint_tracking_type == 'method' and self.setpoint_tracking_id != '':
                    # Update setpoint using a method
                    this_pid = db_retrieve_table_daemon(
                        PID, unique_id=self.unique_id)

                    now = datetime.datetime.now()

                    method = load_method_handler(self.setpoint_tracking_id,
                                                 self.logger)
                    new_setpoint, ended = method.calculate_setpoint(
                        now, this_pid.method_start_time)
                    self.logger.debug("Method {} {} {} {}".format(
                        self.setpoint_tracking_id, method, now,
                        this_pid.method_start_time))

                    if ended:
                        # point in time is out of method range
                        with session_scope(MYCODO_DB_PATH) as db_session:
                            # Overwrite this_controller with committable connection
                            this_pid = db_session.query(PID).filter(
                                PID.unique_id == self.unique_id).first()

                            self.logger.debug("Ended")
                            # Duration method has ended, reset method_start_time locally and in DB
                            this_pid.method_start_time = None
                            this_pid.method_end_time = None
                            this_pid.is_activated = False
                            db_session.commit()

                            self.is_activated = False
                            self.stop_controller()

                            db_session.commit()

                    if new_setpoint is not None:
                        self.logger.debug("New setpoint = {} {}".format(
                            new_setpoint, ended))
                        self.PID_Controller.setpoint = new_setpoint
                    else:
                        self.logger.debug(
                            "New setpoint = default {} {}".format(
                                self.setpoint, ended))
                        self.PID_Controller.setpoint = self.setpoint

                if self.setpoint_tracking_type == 'input-math' and self.setpoint_tracking_id != '':
                    # Update setpoint using an Input or Math
                    device_id = self.setpoint_tracking_id.split(',')[0]
                    measurement_id = self.setpoint_tracking_id.split(',')[1]

                    measurement = get_measurement(measurement_id)
                    if not measurement:
                        return False, None

                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=measurement.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        measurement, conversion)

                    last_measurement = read_last_influxdb(
                        device_id,
                        unit,
                        channel,
                        measure=measurement,
                        duration_sec=self.setpoint_tracking_max_age)

                    if last_measurement[1] is not None:
                        self.PID_Controller.setpoint = last_measurement[1]
                    else:
                        self.logger.debug(
                            "Could not find measurement for Setpoint "
                            "Tracking. Max Age of {} exceeded for measuring "
                            "device ID {} (measurement {})".format(
                                self.setpoint_tracking_max_age, device_id,
                                measurement_id))
                        self.PID_Controller.setpoint = None

                # Calculate new control variable (output) from PID Controller
                self.PID_Controller.update_pid_output(self.last_measurement)

                self.write_pid_values()  # Write variables to database

        # Is PID in a state that allows manipulation of outputs
        if (self.is_activated and self.PID_Controller.setpoint is not None
                and (not self.is_paused or self.is_held)):
            self.manipulate_output()
예제 #16
0
    def get_measurement(self):
        """ Gets the sensor's pH measurement via UART/I2C """
        self._ph = None
        ph = None

        # Calibrate the pH measurement based on a temperature measurement
        if (self.calibrate_sensor_measure
                and ',' in self.calibrate_sensor_measure):
            self.logger.debug("pH sensor set to calibrate temperature")

            device_id = self.calibrate_sensor_measure.split(',')[0]
            measurement = self.calibrate_sensor_measure.split(',')[1]
            last_measurement = read_last_influxdb(device_id,
                                                  measurement,
                                                  duration_sec=300)
            if last_measurement:
                self.logger.debug(
                    "Latest temperature used to calibrate: {temp}".format(
                        temp=last_measurement[1]))

                atlas_command = AtlasScientificCommand(self.input_dev)
                ret_value, ret_msg = atlas_command.calibrate(
                    'temperature', temperature=last_measurement[1])
                time.sleep(0.5)

                self.logger.debug("Calibration returned: {val}, {msg}".format(
                    val=ret_value, msg=ret_msg))

        # Read sensor via UART
        if self.interface == 'UART':
            if self.atlas_sensor_uart.setup:
                lines = self.atlas_sensor_uart.query('R')
                if lines:
                    self.logger.debug("All Lines: {lines}".format(lines=lines))

                    # 'check probe' indicates an error reading the sensor
                    if 'check probe' in lines:
                        self.logger.error('"check probe" returned from sensor')
                    # if a string resembling a float value is returned, this
                    # is out measurement value
                    elif str_is_float(lines[0]):
                        ph = float(lines[0])
                        self.logger.debug(
                            'Value[0] is float: {val}'.format(val=ph))
                    else:
                        # During calibration, the sensor is put into
                        # continuous mode, which causes a return of several
                        # values in one string. If the return value does
                        # not represent a float value, it is likely to be a
                        # string of several values. This parses and returns
                        # the first value.
                        if str_is_float(lines[0].split(b'\r')[0]):
                            ph = lines[0].split(b'\r')[0]
                        # Lastly, this is called if the return value cannot
                        # be determined. Watchthe output in the GUI to see
                        # what it is.
                        else:
                            ph = lines[0]
                            self.logger.error(
                                'Value[0] is not float or "check probe": '
                                '{val}'.format(val=ph))
            else:
                self.logger.error('UART device is not set up.'
                                  'Check the log for errors.')

        # Read sensor via I2C
        elif self.interface == 'I2C':
            if self.atlas_sensor_i2c.setup:
                ph_status, ph_str = self.atlas_sensor_i2c.query('R')
                if ph_status == 'error':
                    self.logger.error(
                        "Sensor read unsuccessful: {err}".format(err=ph_str))
                elif ph_status == 'success':
                    ph = float(ph_str)
            else:
                self.logger.error(
                    'I2C device is not set up. Check the log for errors.')

        return ph
예제 #17
0
    def check_pid(self):
        """ Get measurement and apply to PID controller """
        # If PID is active, retrieve measurement and update
        # the control variable.
        # A PID on hold will sustain the current output and
        # not update the control variable.
        if self.is_activated and (not self.is_paused or not self.is_held):
            self.get_last_measurement_pid()

            if self.last_measurement_success:
                if self.setpoint_tracking_type == 'method' and self.setpoint_tracking_id != '':
                    # Update setpoint using a method
                    this_pid = db_retrieve_table_daemon(
                        PID, unique_id=self.unique_id)
                    new_setpoint, ended = calculate_method_setpoint(
                        self.setpoint_tracking_id, PID, this_pid, Method,
                        MethodData, self.logger)
                    if ended:
                        self.method_start_act = 'Ended'
                    if new_setpoint is not None:
                        self.PID_Controller.setpoint = new_setpoint
                    else:
                        self.PID_Controller.setpoint = self.setpoint

                if self.setpoint_tracking_type == 'input-math' and self.setpoint_tracking_id != '':
                    # Update setpoint using an Input or Math
                    device_id = self.setpoint_tracking_id.split(',')[0]
                    measurement_id = self.setpoint_tracking_id.split(',')[1]

                    measurement = get_measurement(measurement_id)
                    if not measurement:
                        return False, None

                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=measurement.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        measurement, conversion)

                    last_measurement = read_last_influxdb(
                        device_id,
                        unit,
                        channel,
                        measure=measurement,
                        duration_sec=self.setpoint_tracking_max_age)

                    if last_measurement[1] is not None:
                        self.PID_Controller.setpoint = last_measurement[1]
                    else:
                        self.logger.debug(
                            "Could not find measurement for Setpoint "
                            "Tracking. Max Age of {} exceeded for measuring "
                            "device ID {} (measurement {})".format(
                                self.setpoint_tracking_max_age, device_id,
                                measurement_id))
                        self.PID_Controller.setpoint = None

                # Calculate new control variable (output) from PID Controller
                self.PID_Controller.update_pid_output(self.last_measurement)

                self.write_pid_values()  # Write variables to database

        # Is PID in a state that allows manipulation of outputs
        if (self.is_activated and self.PID_Controller.setpoint is not None
                and (not self.is_paused or self.is_held)):
            self.manipulate_output()
예제 #18
0
    def get_measurement(self):
        """ Gets the sensor's pH measurement via UART/I2C """
        self._ph = None

        try:
            if ',' in self.sensor_sel.calibrate_sensor_measure:
                logger.debug("pH sensor set to calibrate temperature")

                device_id = self.sensor_sel.calibrate_sensor_measure.split(
                    ',')[0]
                measurement = self.sensor_sel.calibrate_sensor_measure.split(
                    ',')[1]
                last_measurement = read_last_influxdb(device_id,
                                                      measurement,
                                                      duration_sec=300)
                if last_measurement:
                    logger.debug(
                        "Latest temperature used to calibrate: {temp}".format(
                            temp=last_measurement[1]))

                    atlas_command = AtlasScientificCommand(self.sensor_sel)
                    ret_value, ret_msg = atlas_command.calibrate(
                        'temperature', temperature=last_measurement[1])
                    time.sleep(0.5)

                    logger.debug("Calibration returned: {val}, {msg}".format(
                        val=ret_value, msg=ret_msg))

            ph = None
            if self.interface == 'UART':
                if self.atlas_sensor_uart.setup:
                    lines = self.atlas_sensor_uart.query('R')
                    if lines:
                        logger.debug("All Lines: {lines}".format(lines=lines))

                        if 'check probe' in lines:
                            logger.error('"check probe" returned from sensor')
                        elif str_is_float(lines[0]):
                            ph = float(lines[0])
                            logger.debug(
                                'Value[0] is float: {val}'.format(val=ph))
                        else:
                            ph = lines[0]
                            logger.error(
                                'Value[0] is not float or "check probe": '
                                '{val}'.format(val=ph))
                else:
                    logger.error('UART device is not set up.'
                                 'Check the log for errors.')
            elif self.interface == 'I2C':
                if self.atlas_sensor_i2c.setup:
                    ph_status, ph_str = self.atlas_sensor_i2c.query('R')
                    if ph_status == 'error':
                        logger.error("Sensor read unsuccessful: {err}".format(
                            err=ph_str))
                    elif ph_status == 'success':
                        ph = float(ph_str)
                else:
                    logger.error('I2C device is not set up.'
                                 'Check the log for errors.')

            return ph
        except:
            logger.exception(1)
예제 #19
0
    def get_measurement(self, display_id, i):
        try:
            if self.lcd_line[display_id][i]['measure'] == 'BLANK':
                self.lcd_line[display_id][i]['name'] = ''
                self.lcd_line[display_id][i]['unit'] = ''
                self.lcd_line[display_id][i]['measure_val'] = ''
                return True
            elif self.lcd_line[display_id][i]['measure'] == 'IP':
                str_ip_cmd = "ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1  -d'/'"
                ip_out, _, _ = cmd_output(str_ip_cmd)
                self.lcd_line[display_id][i]['name'] = ''
                self.lcd_line[display_id][i]['unit'] = ''
                self.lcd_line[display_id][i]['measure_val'] = ip_out.rstrip().decode("utf-8")
                return True
            elif self.lcd_line[display_id][i]['measure'] == 'output_state':
                self.lcd_line[display_id][i]['measure_val'] = self.output_state(
                    self.lcd_line[display_id][i]['id'])
                return True
            else:
                if self.lcd_line[display_id][i]['measure'] == 'time':
                    last_measurement = read_last_influxdb(
                        self.lcd_line[display_id][i]['id'],
                        '/.*/',
                        None,
                        None,
                        duration_sec=self.lcd_max_age[display_id][i])
                else:
                    last_measurement = read_last_influxdb(
                        self.lcd_line[display_id][i]['id'],
                        self.lcd_line[display_id][i]['unit'],
                        self.lcd_line[display_id][i]['measure'],
                        self.lcd_line[display_id][i]['channel'],
                        duration_sec=self.lcd_max_age[display_id][i])

                if last_measurement:
                    self.lcd_line[display_id][i]['time'] = last_measurement[0]
                    if self.lcd_decimal_places[display_id][i] == 0:
                        self.lcd_line[display_id][i]['measure_val'] = int(last_measurement[1])
                    else:
                        self.lcd_line[display_id][i]['measure_val'] = round(
                            last_measurement[1], self.lcd_decimal_places[display_id][i])
                    utc_dt = datetime.datetime.strptime(
                        self.lcd_line[display_id][i]['time'].split(".")[0],
                        '%Y-%m-%dT%H:%M:%S')
                    utc_timestamp = calendar.timegm(utc_dt.timetuple())
                    local_timestamp = str(datetime.datetime.fromtimestamp(utc_timestamp))
                    self.logger.debug("Latest {}: {} @ {}".format(
                        self.lcd_line[display_id][i]['measure'],
                        self.lcd_line[display_id][i]['measure_val'], local_timestamp))
                    return True

                else:
                    self.lcd_line[display_id][i]['time'] = None
                    self.lcd_line[display_id][i]['measure_val'] = None
                    self.logger.debug("No data returned from influxdb")
            return False
        except Exception as except_msg:
            self.logger.debug(
                "Failed to read measurement from the influxdb database: "
                "{err}".format(err=except_msg))
            return False
예제 #20
0
    def loop(self):
        if self.timer_loop > time.time():
            return

        while self.timer_loop < time.time():
            self.timer_loop += self.period

        measure = []
        for each_id_set in self.select_measurement:
            device_device_id = each_id_set.split(",")[0]
            device_measure_id = each_id_set.split(",")[1]

            device_measurement = get_measurement(device_measure_id)

            if not device_measurement:
                self.logger.error("Could not find Device Measurement")
                return

            conversion = db_retrieve_table_daemon(
                Conversion, unique_id=device_measurement.conversion_id)
            channel, unit, measurement = return_measurement_info(
                device_measurement, conversion)

            last_measurement = read_last_influxdb(
                device_device_id,
                unit,
                channel,
                measure=measurement,
                duration_sec=self.max_measure_age)

            if not last_measurement:
                self.logger.error(
                    "Could not find measurement within the set Max Age for Device {} and Measurement {}"
                    .format(device_device_id, device_measure_id))
                if self.halt_on_missing_measure:
                    self.logger.debug(
                        "Instructed to halt on the first missing measurement. Halting."
                    )
                    return False
            else:
                measure.append(last_measurement[1])

        if len(measure) > 1:
            stat_mean = float(sum(measure) / float(len(measure)))
            stat_median = median(measure)
            stat_minimum = min(measure)
            stat_maximum = max(measure)
            stdev_ = stdev(measure)
            stdev_mean_upper = stat_mean + stdev_
            stdev_mean_lower = stat_mean - stdev_

            list_measurement = [
                stat_mean, stat_median, stat_minimum, stat_maximum, stdev_,
                stdev_mean_upper, stdev_mean_lower
            ]

            for each_channel, each_measurement in self.channels_measurement.items(
            ):
                if each_measurement.is_enabled:
                    channel, unit, measurement = return_measurement_info(
                        each_measurement,
                        self.channels_conversion[each_channel])

                    self.logger.debug(
                        "Saving {} to channel {} with measurement {} and unit {}"
                        .format(list_measurement[each_channel], each_channel,
                                measurement, unit))

                    write_influxdb_value(self.unique_id,
                                         unit,
                                         value=list_measurement[each_channel],
                                         measure=measurement,
                                         channel=each_channel)
        else:
            self.logger.debug(
                "Less than 2 measurements found within Max Age. "
                "Calculations need at least 2 measurements. Not calculating.")
예제 #21
0
    def get_last_measurement(self):
        """
        Retrieve the latest input measurement from InfluxDB

        :rtype: None
        """
        self.last_measurement_success = False

        # Get latest measurement from influxdb
        try:
            device_measurement = get_measurement(self.measurement_id)

            if device_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=device_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                device_measurement, conversion)

            self.last_measurement = read_last_influxdb(
                self.device_id,
                unit,
                channel,
                measure=measurement,
                duration_sec=int(self.max_measure_age))

            if self.last_measurement:
                self.last_time = self.last_measurement[0]
                self.last_measurement = self.last_measurement[1]

                utc_dt = datetime.datetime.strptime(
                    self.last_time.split(".")[0], '%Y-%m-%dT%H:%M:%S')
                utc_timestamp = calendar.timegm(utc_dt.timetuple())
                local_timestamp = str(
                    datetime.datetime.fromtimestamp(utc_timestamp))
                self.logger.debug(
                    "Latest (CH{ch}, Unit: {unit}): {last} @ {ts}".format(
                        ch=channel,
                        unit=unit,
                        last=self.last_measurement,
                        ts=local_timestamp))
                if calendar.timegm(
                        time.gmtime()) - utc_timestamp > self.max_measure_age:
                    self.logger.error(
                        "Last measurement was {last_sec} seconds ago, however"
                        " the maximum measurement age is set to {max_sec}"
                        " seconds.".format(
                            last_sec=calendar.timegm(time.gmtime()) -
                            utc_timestamp,
                            max_sec=self.max_measure_age))
                self.last_measurement_success = True
            else:
                self.logger.warning("No data returned from influxdb")
        except requests.ConnectionError:
            self.logger.error("Failed to read measurement from the "
                              "influxdb database: Could not connect.")
        except Exception as except_msg:
            self.logger.exception(
                "Exception while reading measurement from the influxdb "
                "database: {err}".format(err=except_msg))