Beispiel #1
0
 def record_dispersal(self, amount_ml=None, seconds_to_run=None):
     measure_dict = copy.deepcopy(measurements_dict)
     if amount_ml:
         measure_dict[0]['value'] = amount_ml
     if seconds_to_run:
         measure_dict[1]['value'] = seconds_to_run
     add_measurements_influxdb(self.unique_id, measure_dict)
Beispiel #2
0
    def output_switch(self, state, output_type=None, amount=None):
        measure_dict = copy.deepcopy(measurements_dict)

        if self.pwm_command:
            if state == 'on' and 0 <= amount <= 100:
                if self.pwm_invert_signal:
                    amount = 100.0 - abs(amount)
            elif state == 'off':
                if self.pwm_invert_signal:
                    amount = 100
                else:
                    amount = 0
            else:
                return

            self.pwm_state = amount

            cmd = self.pwm_command.replace('((duty_cycle))', str(amount))
            cmd_return, cmd_error, cmd_status = cmd_output(
                cmd, user=self.linux_command_user)

            measure_dict[0]['value'] = self.pwm_state
            add_measurements_influxdb(self.unique_id, measure_dict)

            self.logger.debug("Duty cycle set to {dc:.2f} %".format(dc=amount))
            self.logger.debug(
                "Output duty cycle {duty_cycle} command returned: "
                "Status: {stat}, "
                "Output: '{ret}', "
                "Error: '{err}'".format(duty_cycle=amount,
                                        stat=cmd_status,
                                        ret=cmd_return,
                                        err=cmd_error))
Beispiel #3
0
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=None,
                      duty_cycle=None):
        measure_dict = copy.deepcopy(measurements_dict)

        if state == 'on':
            if self.pwm_invert_signal:
                duty_cycle = 100.0 - abs(duty_cycle)
        elif state == 'off':
            if self.pwm_invert_signal:
                duty_cycle = 100
            else:
                duty_cycle = 0

        if self.pwm_library == 'pigpio_hardware':
            self.pwm_output.hardware_PWM(self.pin, self.pwm_hertz,
                                         int(duty_cycle * 10000))
        elif self.pwm_library == 'pigpio_any':
            self.pwm_output.set_PWM_frequency(self.pin, self.pwm_hertz)
            self.pwm_output.set_PWM_range(self.pin, 1000)
            self.pwm_output.set_PWM_dutycycle(
                self.pin, self.duty_cycle_to_pigpio_value(duty_cycle))

        measure_dict[0]['value'] = duty_cycle
        add_measurements_influxdb(self.unique_id, measure_dict)

        self.logger.debug("Duty cycle set to {dc:.2f} %".format(dc=duty_cycle))
Beispiel #4
0
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=None,
                      output_channel=None):
        measure_dict = copy.deepcopy(measurements_dict)

        if amount not in [None, 0]:
            if amount > 0:
                self.stepper_running = True
                self.stepper.enable(True)
                self.stepper.run(amount, True)
                self.stepper.enable(False)
                self.stepper_running = True
            else:
                self.stepper_running = True
                self.stepper.enable(True)
                self.stepper.run(abs(amount), False)
                self.stepper.enable(False)
                self.stepper_running = False
            measure_dict[0]['value'] = amount
        elif state == "off":
            self.stepper.enable(False)
            self.stepper_running = False
        else:
            self.logger.error(
                "Invalid parameters: State: {state}, Type: {ot}, Amount: {amt}"
                .format(state=state, ot=output_type, amt=amount))
            return

        add_measurements_influxdb(self.unique_id, measure_dict)
Beispiel #5
0
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=None,
                      output_channel=None):
        measure_dict = copy.deepcopy(measurements_dict)

        if amount not in [None, 0]:
            if amount > 0:
                self.stepper_running = True
                self.stepper.motor_run(
                    self.pins, self.options_channels['step_delay'][0],
                    int(abs(amount)), False, False,
                    self.options_channels['step_resolution'][0], .05)
                self.stepper_running = False
            elif amount < 0:
                self.stepper_running = True
                self.stepper.motor_run(
                    self.pins, self.options_channels['step_delay'][0],
                    int(abs(amount)), True, False,
                    self.options_channels['step_resolution'][0], .05)
                self.stepper_running = False
            measure_dict[0]['value'] = amount
            add_measurements_influxdb(self.unique_id, measure_dict)
        else:
            self.logger.error(
                "Invalid parameters: State: {state}, Type: {ot}, Amount: {amt}"
                .format(state=state, ot=output_type, amt=amount))
Beispiel #6
0
    def output_switch(self, state, amount=None, duty_cycle=None):
        measure_dict = measurements_dict.copy()

        if state == 'on' and amount > 0:
            if self.output_mode == 'fastest_flow_rate':
                minutes_to_run = amount * 105
                write_cmd = 'D,{ml:.2f}'.format(ml=amount)
            elif self.output_mode == 'specify_flow_rate':
                minutes_to_run = amount / self.output_flow_rate
                write_cmd = 'D,{ml:.2f},{min:.2f}'.format(
                    ml=amount, min=minutes_to_run)
            else:
                self.logger.error("Invalid output_mode: '{}'".format(
                    self.output_mode))
                return

        elif state == 'off' or amount <= 0:
            write_cmd = 'X'
            amount = 0
            minutes_to_run = 0

        else:
            self.logger.error(
                "Invalid parameters: State: {state}, Volume: {vol}, "
                "Flow Rate: {fr}".format(
                    state=state, vol=amount, fr=self.output_flow_rate))
            return

        self.atlas_command.write(write_cmd)
        self.logger.debug("EZO-PMP command: {}".format(write_cmd))

        if amount and minutes_to_run:
            measure_dict[0]['value'] = amount
            measure_dict[1]['value'] = minutes_to_run
            add_measurements_influxdb(self.output_unique_id, measure_dict)
Beispiel #7
0
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=None,
                      output_channel=None):
        measure_dict = copy.deepcopy(measurements_dict)

        if state == 'on':
            if self.options_channels['pwm_invert_signal'][0]:
                amount = 100.0 - abs(amount)
        elif state == 'off':
            if self.options_channels['pwm_invert_signal'][0]:
                amount = 100
            else:
                amount = 0

        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],
                                         int(amount * 10000))
        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.pwm_output.set_PWM_range(self.options_channels['pin'][0],
                                          1000)
            self.pwm_output.set_PWM_dutycycle(
                self.options_channels['pin'][0],
                self.duty_cycle_to_pigpio_value(amount))

        measure_dict[0]['value'] = amount
        add_measurements_influxdb(self.unique_id, measure_dict)

        self.logger.debug("Duty cycle set to {dc:.2f} %".format(dc=amount))
        return "success"
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=None,
                      output_channel=None):
        measure_dict = copy.deepcopy(measurements_dict)

        if amount not in [None, 0]:
            if (self.options_channels['pin_enable'][0]
                    and self.options_channels['enable_mode'][0] == "only_run"):
                self.stepper.enable(True)

            if amount > 0:
                self.stepper.run(int(amount), True)
            elif amount < 0:
                self.stepper.run(int(abs(amount)), False)

            if (self.options_channels['pin_enable'][0]
                    and self.options_channels['enable_mode'][0] == "only_run"):
                self.stepper.enable(False)

            measure_dict[0]['value'] = amount
        elif state == "off":
            if (self.options_channels['pin_enable'][0]
                    and self.options_channels['enable_mode'][0] == "only_run"):
                self.stepper.enable(False)
            if self.stepper.running:
                self.stepper.stop_running()
        else:
            self.logger.error(
                "Invalid parameters: State: {state}, Type: {ot}, Amount: {amt}"
                .format(state=state, ot=output_type, amt=amount))
            return

        add_measurements_influxdb(self.unique_id, measure_dict)
Beispiel #9
0
 def record_dispersal(self, amount, total_on_seconds,
                      total_dispense_seconds):
     measure_dict = copy.deepcopy(measurements_dict)
     measure_dict[0]['value'] = total_on_seconds
     measure_dict[1]['value'] = amount
     measure_dict[2]['value'] = total_dispense_seconds
     add_measurements_influxdb(self.unique_id, measure_dict)
Beispiel #10
0
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=None,
                      duty_cycle=None):
        measure_dict = copy.deepcopy(measurements_dict)

        if self.pwm_command:
            if state == 'on' and 100 >= duty_cycle >= 0:
                if self.pwm_invert_signal:
                    duty_cycle = 100.0 - abs(duty_cycle)
            elif state == 'off' or duty_cycle == 0:
                if self.pwm_invert_signal:
                    duty_cycle = 100
                else:
                    duty_cycle = 0
            else:
                return

            self.output_run_python_pwm.output_code_run(duty_cycle)
            self.pwm_state = duty_cycle

            measure_dict[0]['value'] = duty_cycle
            add_measurements_influxdb(self.unique_id, measure_dict)

            self.logger.debug(
                "Duty cycle set to {dc:.2f} %".format(dc=duty_cycle))
Beispiel #11
0
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=None,
                      output_channel=None):
        if not self.is_setup():
            msg = "Error 101: Device not set up. See https://kizniche.github.io/Mycodo/Error-Codes#error-101 for more info."
            self.logger.error(msg)
            return msg

        measure_dict = copy.deepcopy(measurements_dict)

        if amount not in [None, 0]:
            if amount > 0:
                self.stepper_running = True
                self.stepper.motor_run(
                    self.pins, self.options_channels['step_delay'][0],
                    int(abs(amount)), False, False,
                    self.options_channels['step_resolution'][0], .05)
                self.stepper_running = False
            elif amount < 0:
                self.stepper_running = True
                self.stepper.motor_run(
                    self.pins, self.options_channels['step_delay'][0],
                    int(abs(amount)), True, False,
                    self.options_channels['step_resolution'][0], .05)
                self.stepper_running = False
            measure_dict[0]['value'] = amount
            add_measurements_influxdb(self.unique_id, measure_dict)
        else:
            self.logger.error(
                "Invalid parameters: State: {state}, Type: {ot}, Amount: {amt}"
                .format(state=state, ot=output_type, amt=amount))
Beispiel #12
0
def test_influxdb():
    """Verify measurements can be written and read from influxdb."""
    print("\nTest: test_influxdb")
    device_id = 'ID_ASDF'
    channel = 0
    measurement = 'duty_cycle'
    unit = 'percent'
    written_measurement = 123.45

    measurements_dict = {
        channel: {
            'measurement': measurement,
            'unit': unit,
            'value': written_measurement
        }
    }

    add_measurements_influxdb(device_id, measurements_dict, block=True)

    last_measurement = read_influxdb_single(
        device_id,
        unit,
        0,
        measure=measurement,
        duration_sec=1000,
        value='LAST')

    print(f"Returned measurement: {last_measurement}")

    assert last_measurement

    returned_measurement = last_measurement[1]

    assert returned_measurement == written_measurement
Beispiel #13
0
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=None,
                      output_channel=None):
        measure_dict = copy.deepcopy(measurements_dict)

        if state == 'on':
            if self.options_channels['pwm_invert_signal'][output_channel]:
                amount = 100.0 - abs(amount)
        elif state == 'off':
            if self.options_channels['pwm_invert_signal'][output_channel]:
                amount = 100
            else:
                amount = 0

        self.pwm_output.set_pwm(
            self.options_channels['pwm_hertz'][output_channel], amount)
        self.pwm_duty_cycles[output_channel] = amount

        measure_dict[output_channel]['value'] = amount
        add_measurements_influxdb(self.unique_id, measure_dict)

        self.logger.debug(
            "Duty cycle of channel {ch} set to {dc:.2f} %".format(
                ch=output_channel, dc=amount))
        return "success"
Beispiel #14
0
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=0,
                      output_channel=None):
        measure_dict = copy.deepcopy(measurements_dict)

        output_amount = 0

        if state == 'on':
            if self.options_channels['pwm_invert_signal'][output_channel]:
                output_amount = 100.0 - abs(amount)
            else:
                output_amount = abs(amount)
        elif state == 'off':
            if self.options_channels['pwm_invert_signal'][output_channel]:
                output_amount = 100
            else:
                output_amount = 0

        off_at_tick = round(output_amount * 4095. / 100.)
        self.pwm_output.set_pwm(output_channel, 0, off_at_tick)

        self.pwm_duty_cycles[output_channel] = amount
        measure_dict[output_channel]['value'] = amount
        add_measurements_influxdb(self.unique_id, measure_dict)

        self.logger.debug(
            "Duty cycle of channel {ch} set to {dc:.2f} % (switched off for {off_at_tick:d} of 4095 ticks)"
            .format(ch=output_channel, dc=amount, off_at_tick=off_at_tick))
        return "success"
Beispiel #15
0
    def add_measurement_influxdb(self, channel, measurement):
        # Convert value/unit is conversion_id present and valid
        if self.channels_conversion[channel]:
            conversion = db_retrieve_table_daemon(
                Conversion,
                unique_id=self.channels_measurement[channel].conversion_id)
            if conversion:
                meas = parse_measurement(
                    self.channels_conversion[channel],
                    self.channels_measurement[channel],
                    measurement,
                    channel,
                    measurement[channel],
                    timestamp=measurement[channel]['timestamp_utc'])

                measurement[channel]['measurement'] = meas[channel][
                    'measurement']
                measurement[channel]['unit'] = meas[channel]['unit']
                measurement[channel]['value'] = meas[channel]['value']

        if measurement:
            self.logger.debug(
                "Adding measurement to influxdb: {}".format(measurement))
            add_measurements_influxdb(self.unique_id,
                                      measurement,
                                      use_same_timestamp=INPUT_INFORMATION[
                                          'measurements_use_same_timestamp'])
Beispiel #16
0
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=None,
                      output_channel=None):
        measure_dict = copy.deepcopy(measurements_dict)

        if self.options_channels['pwm_command'][0]:
            if state == 'on' and 100 >= amount >= 0:
                if self.options_channels['pwm_invert_signal'][0]:
                    amount = 100.0 - abs(amount)
            elif state == 'off' or amount == 0:
                if self.options_channels['pwm_invert_signal'][0]:
                    amount = 100
                else:
                    amount = 0
            else:
                return

            self.output_run_python_pwm.output_code_run(amount)
            self.output_states[0] = amount

            measure_dict[0]['value'] = amount
            add_measurements_influxdb(self.unique_id, measure_dict)

            self.logger.debug("Duty cycle set to {dc:.2f} %".format(dc=amount))
Beispiel #17
0
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=None,
                      output_channel=0):
        measure_dict = copy.deepcopy(measurements_dict)

        try:
            if state == 'on' and amount is not None:
                self.publish.single(
                    self.options_channels['topic'][0],
                    amount,
                    hostname=self.options_channels['hostname'][0],
                    port=self.options_channels['port'][0],
                    client_id=self.options_channels['clientid'][0],
                    keepalive=self.options_channels['keepalive'][0])
                self.output_states[output_channel] = amount
                measure_dict[0]['value'] = amount
            elif state == 'off':
                self.publish.single(
                    self.options_channels['topic'][0],
                    payload=self.options_channels['off_value'][0],
                    hostname=self.options_channels['hostname'][0],
                    port=self.options_channels['port'][0],
                    client_id=self.options_channels['clientid'][0],
                    keepalive=self.options_channels['keepalive'][0])
                self.output_states[output_channel] = False
                measure_dict[0]['value'] = self.options_channels['off_value'][
                    0]
        except Exception as e:
            self.logger.error("State change error: {}".format(e))
            return

        add_measurements_influxdb(self.unique_id, measure_dict)
Beispiel #18
0
 def acquire_measurements_now(self):
     try:
         self.update_measure()
         add_measurements_influxdb(
             self.unique_id, self.create_measurements_dict())
         return "Success"
     except Exception as msg:
         return "Failure: {}".format(msg)
Beispiel #19
0
 def acquire_measurements_now(self):
     try:
         self.update_measure()
         add_measurements_influxdb(
             self.unique_id, self.create_measurements_dict())
         return "Success"
     except Exception as msg:
         return "Failure: {}".format(msg)
Beispiel #20
0
    def loop(self):
        if self.timer_loop > time.time():
            return

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

        measurements = []
        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(
                    "One more more measurements were not within the set Max Age. Not calculating average."
                )
                return False
            else:
                measurements.append(last_measurement[1])

        average = float(sum(measurements) / float(len(measurements)))

        measurement_dict = {
            0: {
                'measurement': self.channels_measurement[0].measurement,
                'unit': self.channels_measurement[0].unit,
                'value': average
            }
        }

        if measurement_dict:
            self.logger.debug(
                "Adding measurements to InfluxDB with ID {}: {}".format(
                    self.unique_id, measurement_dict))
            add_measurements_influxdb(self.unique_id, measurement_dict)
        else:
            self.logger.debug(
                "No measurements to add to InfluxDB with ID {}".format(
                    self.unique_id))
    def output_switch(self, state, output_type=None, amount=None, duty_cycle=None):
        """Switch the output on or off"""
        measure_dict = measurements_dict.copy()

        if state == 'on' and duty_cycle != 0:
            self.output_device.value = duty_cycle / 100.0
            self.logger.debug("Output turned on")
        elif state == 'off' or duty_cycle == 0:
            self.output_device.value = 0
            self.logger.debug("Output turned off")

        measure_dict[0]['value'] = duty_cycle
        add_measurements_influxdb(self.output_unique_id, measure_dict)
Beispiel #22
0
    def write_pid_values(self):
        """ Write PID values to the measurement database """
        if self.PID_Controller.band:
            setpoint_band_lower = self.PID_Controller.setpoint - self.PID_Controller.band
            setpoint_band_upper = self.PID_Controller.setpoint + self.PID_Controller.band
        else:
            setpoint_band_lower = None
            setpoint_band_upper = None

        list_measurements = [
            self.PID_Controller.setpoint,
            setpoint_band_lower,
            setpoint_band_upper,
            self.PID_Controller.P_value,
            self.PID_Controller.I_value,
            self.PID_Controller.D_value
        ]

        measurement_dict = {}
        measurements = self.device_measurements.filter(
            DeviceMeasurements.device_id == self.unique_id).all()
        for each_channel, each_measurement in enumerate(measurements):
            if (each_measurement.channel not in measurement_dict and
                    each_measurement.channel < len(list_measurements)):

                # If setpoint, get unit from PID measurement
                if each_measurement.measurement_type == 'setpoint':
                    setpoint_pid = db_retrieve_table_daemon(
                        PID, unique_id=each_measurement.device_id)
                    if setpoint_pid and ',' in setpoint_pid.measurement:
                        pid_measurement = setpoint_pid.measurement.split(',')[1]
                        setpoint_measurement = db_retrieve_table_daemon(
                            DeviceMeasurements, unique_id=pid_measurement)
                        if setpoint_measurement:
                            conversion = db_retrieve_table_daemon(
                                Conversion, unique_id=setpoint_measurement.conversion_id)
                            _, unit, _ = return_measurement_info(
                                setpoint_measurement, conversion)
                            measurement_dict[each_channel] = {
                                'measurement': each_measurement.measurement,
                                'unit': unit,
                                'value': list_measurements[each_channel]
                            }
                else:
                    measurement_dict[each_channel] = {
                        'measurement': each_measurement.measurement,
                        'unit': each_measurement.unit,
                        'value': list_measurements[each_channel]
                    }

        add_measurements_influxdb(self.unique_id, measurement_dict)
Beispiel #23
0
    def write_pid_values(self):
        """ Write PID values to the measurement database """
        if self.band:
            setpoint_band_lower = self.setpoint - self.band
            setpoint_band_upper = self.setpoint + self.band
        else:
            setpoint_band_lower = None
            setpoint_band_upper = None

        list_measurements = [
            self.setpoint,
            setpoint_band_lower,
            setpoint_band_upper,
            self.P_value,
            self.I_value,
            self.D_value
        ]

        measurement_dict = {}
        measurements = self.device_measurements.filter(
            DeviceMeasurements.device_id == self.pid_id).all()
        for each_channel, each_measurement in enumerate(measurements):
            if (each_measurement.channel not in measurement_dict and
                    each_measurement.channel < len(list_measurements)):

                # If setpoint, get unit from PID measurement
                if each_measurement.measurement_type == 'setpoint':
                    setpoint_pid = db_retrieve_table_daemon(
                        PID, unique_id=each_measurement.device_id)
                    if setpoint_pid and ',' in setpoint_pid.measurement:
                        pid_measurement = setpoint_pid.measurement.split(',')[1]
                        setpoint_measurement = db_retrieve_table_daemon(
                            DeviceMeasurements, unique_id=pid_measurement)
                        if setpoint_measurement:
                            conversion = db_retrieve_table_daemon(
                                Conversion, unique_id=setpoint_measurement.conversion_id)
                            _, unit, _ = return_measurement_info(
                                setpoint_measurement, conversion)
                            measurement_dict[each_channel] = {
                                'measurement': each_measurement.measurement,
                                'unit': unit,
                                'value': list_measurements[each_channel]
                            }
                else:
                    measurement_dict[each_channel] = {
                        'measurement': each_measurement.measurement,
                        'unit': each_measurement.unit,
                        'value': list_measurements[each_channel]
                    }

        add_measurements_influxdb(self.pid_id, measurement_dict)
Beispiel #24
0
    def calculate_math(self):
        measurement_dict = {}

        # Average (multiple channels)
        if self.math_type == 'average':
            measurement_dict = self.math_average(measurement_dict)
        # Average (single channel)
        elif self.math_type == 'average_single':
            measurement_dict = self.math_average_single(measurement_dict)
        # Sum (multiple channels)
        elif self.math_type == 'sum':
            measurement_dict = self.math_sum(measurement_dict)
        # Sum (single channel)
        elif self.math_type == 'sum_single':
            measurement_dict = self.math_sum_single(measurement_dict)
        # Difference between two channels
        elif self.math_type == 'difference':
            measurement_dict = self.math_difference(measurement_dict)
        # Equation (math performed on measurement)
        elif self.math_type == 'equation':
            measurement_dict = self.math_equation(measurement_dict)
        # Redundancy (Use next measurement if one isn't currently available)
        elif self.math_type == 'redundancy':
            measurement_dict = self.math_redundancy(measurement_dict)
        # Statistical analysis on all measurements from a period of time
        elif self.math_type == 'statistics':
            measurement_dict = self.math_statistics(measurement_dict)
        # Verification (only use measurement if it's close to another measurement)
        elif self.math_type == 'verification':
            measurement_dict = self.math_verification(measurement_dict)
        # Calculate humidity from wet- and dry-bulb temperatures
        elif self.math_type == 'humidity':
            measurement_dict = self.math_humidity(measurement_dict)
        # Calculate vapor pressure deficit from temperature and humidity
        elif self.math_type == 'vapor_pressure_deficit':
            measurement_dict = self.math_vapor_pressure_deficit(
                measurement_dict)
        else:
            self.logger.error(
                "Unknown math type: {type}".format(type=self.math_type))

        # Add measurement(s) to influxdb
        if measurement_dict:
            self.logger.debug(
                "Adding measurements to InfluxDB with ID {}: {}".format(
                    self.unique_id, measurement_dict))
            add_measurements_influxdb(self.unique_id, measurement_dict)
        else:
            self.logger.debug(
                "No measurements to add to InfluxDB with ID {}".format(
                    self.unique_id))
    def loop(self):
        if self.timer_loop > time.time():
            return

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

        device_measurement = get_measurement(
            self.select_measurement_measurement_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)

        sum_measurements = sum_past_seconds(
            self.select_measurement_device_id,
            unit,
            channel,
            self.max_measure_age,
            measure=measurement)

        if not sum_measurements:
            self.logger.error(
                "Could not find measurement within the set Max Age")
            return False

        measurement_dict = {
            0: {
                'measurement': self.channels_measurement[0].measurement,
                'unit': self.channels_measurement[0].unit,
                'value': sum_measurements
            }
        }

        if measurement_dict:
            self.logger.debug(
                "Adding measurements to InfluxDB with ID {}: {}".format(
                    self.unique_id, measurement_dict))
            add_measurements_influxdb(self.unique_id, measurement_dict)
        else:
            self.logger.debug(
                "No measurements to add to InfluxDB with ID {}".format(
                    self.unique_id))
Beispiel #26
0
 def record_dispersal(self,
                      amount,
                      total_on_seconds,
                      total_dispense_seconds,
                      timestamp=None):
     measure_dict = copy.deepcopy(measurements_dict)
     measure_dict[0]['value'] = total_on_seconds
     measure_dict[1]['value'] = amount
     measure_dict[2]['value'] = total_dispense_seconds
     if timestamp:
         measure_dict[0]['timestamp_utc'] = timestamp
         measure_dict[1]['timestamp_utc'] = timestamp
         measure_dict[2]['timestamp_utc'] = timestamp
     add_measurements_influxdb(self.unique_id,
                               measure_dict,
                               use_same_timestamp=False)
Beispiel #27
0
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=0,
                      output_channel=None):
        if not self.is_setup():
            msg = "Error 101: Device not set up. See https://kizniche.github.io/Mycodo/Error-Codes#error-101 for more info."
            self.logger.error(msg)
            return msg

        measure_dict = copy.deepcopy(measurements_dict)

        output_amount = 0

        if state == 'on':
            if self.options_channels['pwm_invert_signal'][output_channel]:
                output_amount = 100.0 - abs(amount)
            else:
                output_amount = abs(amount)
        elif state == 'off':
            if self.options_channels['pwm_invert_signal'][output_channel]:
                output_amount = 100
            else:
                output_amount = 0

        off_at_tick = round(output_amount * 4095. / 100.)
        self.pwm_output.set_pwm(output_channel, 0, off_at_tick)

        self.pwm_duty_cycles[output_channel] = amount

        self.logger.debug(
            "Duty cycle of channel {ch} set to {dc:.2f} % (switched off for {off_at_tick:d} of 4095 ticks)"
            .format(ch=output_channel, dc=amount, off_at_tick=off_at_tick))

        if self.options_channels['pwm_invert_stored_signal'][output_channel]:
            amount = 100.0 - abs(amount)

        measure_dict[output_channel]['value'] = amount
        add_measurements_influxdb(self.unique_id, measure_dict)

        return "success"
    def output_switch(self,
                      state,
                      output_type=None,
                      amount=None,
                      output_channel=None):
        if not self.is_setup():
            msg = "Error 101: Device not set up. See https://kizniche.github.io/Mycodo/Error-Codes#error-101 for more info."
            self.logger.error(msg)
            return msg

        measure_dict = copy.deepcopy(measurements_dict)

        if amount not in [None, 0]:
            if (self.options_channels['pin_enable'][0]
                    and self.options_channels['enable_mode'][0] == "only_run"):
                self.stepper.enable(True)

            if amount > 0:
                self.stepper.run(int(amount), True)
            elif amount < 0:
                self.stepper.run(int(abs(amount)), False)

            if (self.options_channels['pin_enable'][0]
                    and self.options_channels['enable_mode'][0] == "only_run"):
                self.stepper.enable(False)

            measure_dict[0]['value'] = amount
        elif state == "off":
            if (self.options_channels['pin_enable'][0]
                    and self.options_channels['enable_mode'][0] == "only_run"):
                self.stepper.enable(False)
            if self.stepper.running:
                self.stepper.stop_running()
        else:
            self.logger.error(
                "Invalid parameters: State: {state}, Type: {ot}, Amount: {amt}"
                .format(state=state, ot=output_type, amt=amount))
            return

        add_measurements_influxdb(self.unique_id, measure_dict)
Beispiel #29
0
    def loop(self):
        if self.timer_loop < time.time():
            while self.timer_loop < time.time():
                self.timer_loop += self.period

            measurements = {}
            for channel in self.channels_measurement:
                # Original value/unit
                measurements[channel] = {}
                measurements[channel][
                    'measurement'] = self.channels_measurement[
                        channel].measurement
                measurements[channel]['unit'] = self.channels_measurement[
                    channel].unit
                measurements[channel]['value'] = random.randint(1, 100)

                # Convert value/unit is conversion_id present and valid
                if self.channels_conversion[channel]:
                    conversion = db_retrieve_table_daemon(
                        Conversion,
                        unique_id=self.channels_measurement[channel].
                        conversion_id)
                    if conversion:
                        meas = parse_measurement(
                            self.channels_conversion[channel],
                            self.channels_measurement[channel], measurements,
                            channel, measurements[channel])

                        measurements[channel]['measurement'] = meas[channel][
                            'measurement']
                        measurements[channel]['unit'] = meas[channel]['unit']
                        measurements[channel]['value'] = meas[channel]['value']

            if measurements:
                self.logger.debug(
                    "Adding measurements to influxdb: {}".format(measurements))
                add_measurements_influxdb(self.unique_id, measurements)
            else:
                self.logger.debug("No measurements to add to influxdb.")
Beispiel #30
0
    def calculate_math(self):
        measurement_dict = {}

        if self.math_type == 'average':

            device_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).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)

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                average = float(sum(measure) / float(len(measure)))

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': average
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        elif self.math_type == 'average_single':

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

            device_measurement = db_retrieve_table_daemon(
                DeviceMeasurements, unique_id=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)

            try:
                last_measurements = read_past_influxdb(
                    device_id,
                    unit,
                    measurement,
                    channel,
                    self.max_measure_age)

                if last_measurements:
                    measure_list = []
                    for each_set in last_measurements:
                        if len(each_set) == 2:
                            measure_list.append(each_set[1])
                    average = sum(measure_list) / float(len(measure_list))

                    measurement_dict = {
                        channel: {
                            'measurement': measurement,
                            'unit': unit,
                            'value': average
                        }
                    }
                else:
                    self.error_not_within_max_age()
            except Exception as msg:
                self.logger.exception("average_single Error: {err}".format(err=msg))

        elif self.math_type == 'difference':

            device_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).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)

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                if self.difference_reverse_order:
                    difference = measure[1] - measure[0]
                else:
                    difference = measure[0] - measure[1]
                if self.difference_absolute:
                    difference = abs(difference)

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': difference
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        elif self.math_type == 'equation':

            device_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).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)

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                replaced_str = self.equation.replace('x', str(measure[0]))
                equation_output = eval(replaced_str)

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': float(equation_output)
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        elif self.math_type == 'redundancy':
            list_order = self.order_of_use.split(';')
            measurement_success = False

            for each_id_measurement_id in list_order:
                device_id = each_id_measurement_id.split(',')[0]
                measurement_id = each_id_measurement_id.split(',')[1]

                device_measurement = self.device_measurements.filter(
                    DeviceMeasurements.channel == 0).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)

                try:
                    success_measure, measure = self.get_measurements_from_id(
                        device_id, measurement_id)

                    if success_measure:
                        measurement_dict = {
                            channel: {
                                'measurement': measurement,
                                'unit': unit,
                                'value': float(measure[1]),
                                'timestamp': measure[0],
                            }
                        }
                        measurement_success = True
                        break

                except Exception as msg:
                    self.logger.exception("redundancy Error: {err}".format(err=msg))

            if not measurement_success:
                self.error_not_within_max_age()

        elif self.math_type == 'statistics':

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                # Perform some math
                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():
                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=each_measurement.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        each_measurement, conversion)

                    measurement_dict[channel] = {
                            'measurement': measurement,
                            'unit': unit,
                            'value': list_measurement[channel]
                    }

            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        elif self.math_type == 'verification':

            device_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).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)

            success, measure = self.get_measurements_from_str(self.inputs)
            if (success and
                    max(measure) - min(measure) <
                    self.max_difference):
                difference = max(measure) - min(measure)

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': difference
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        elif self.math_type == 'humidity':

            pressure_pa = 101325
            critical_error = False

            if self.pressure_pa_id and self.pressure_pa_measure_id:
                success_pa, pressure = self.get_measurements_from_id(
                    self.pressure_pa_id, self.pressure_pa_measure_id)
                if success_pa:
                    pressure_pa = int(pressure[1])
                    # Pressure must be in Pa, convert if not

                    if db_retrieve_table_daemon(DeviceMeasurements, unique_id=self.pressure_pa_measure_id):
                        measurement = db_retrieve_table_daemon(DeviceMeasurements, unique_id=self.pressure_pa_measure_id)
                    else:
                        self.logger.error("Could not find pressure measurement")
                        measurement = None
                        critical_error = True

                    if measurement and measurement.unit != 'Pa':
                        for each_conv in db_retrieve_table_daemon(Conversion, entry='all'):
                            if (each_conv.convert_unit_from == measurement.unit and
                                    each_conv.convert_unit_to == 'Pa'):
                                pressure_pa = convert_units(
                                    each_conv.unique_id, pressure_pa)
                            else:
                                self.logger.error(
                                    "Could not find conversion for unit "
                                    "{unit} to Pa (Pascals)".format(
                                        unit=measurement.unit))
                                critical_error = True

            success_dbt, dry_bulb_t = self.get_measurements_from_id(
                self.dry_bulb_t_id, self.dry_bulb_t_measure_id)
            success_wbt, wet_bulb_t = self.get_measurements_from_id(
                self.wet_bulb_t_id, self.wet_bulb_t_measure_id)

            if success_dbt and success_wbt:
                dbt_kelvin = float(dry_bulb_t[1])
                wbt_kelvin = float(wet_bulb_t[1])

                if db_retrieve_table_daemon(DeviceMeasurements, unique_id=self.dry_bulb_t_measure_id):
                    measurement = db_retrieve_table_daemon(DeviceMeasurements, unique_id=self.dry_bulb_t_measure_id)
                else:
                    self.logger.error("Could not find pressure measurement")
                    measurement = None
                    critical_error = True

                if measurement and measurement.unit != 'K':
                    conversion_found = False
                    for each_conv in db_retrieve_table_daemon(Conversion, entry='all'):
                        if (each_conv.convert_unit_from == measurement.unit and
                                each_conv.convert_unit_to == 'K'):
                            dbt_kelvin = convert_units(
                                each_conv.unique_id, dbt_kelvin)
                            conversion_found = True
                    if not conversion_found:
                        self.logger.error(
                            "Could not find conversion for unit "
                            "{unit} to K (Kelvin)".format(
                                unit=measurement.unit))
                        critical_error = True

                if db_retrieve_table_daemon(DeviceMeasurements, unique_id=self.dry_bulb_t_measure_id):
                    measurement = db_retrieve_table_daemon(DeviceMeasurements, unique_id=self.dry_bulb_t_measure_id)
                else:
                    self.logger.error("Could not find pressure measurement")
                    measurement = None
                    critical_error = True

                if measurement and measurement.unit != 'K':
                    conversion_found = False
                    for each_conv in db_retrieve_table_daemon(Conversion, entry='all'):
                        if (each_conv.convert_unit_from == measurement.unit and
                                each_conv.convert_unit_to == 'K'):
                            wbt_kelvin = convert_units(
                                each_conv.unique_id, wbt_kelvin)
                            conversion_found = True
                    if not conversion_found:
                        self.logger.error(
                            "Could not find conversion for unit "
                            "{unit} to K (Kelvin)".format(
                                unit=measurement.unit))
                        critical_error = True

                # Convert temperatures to Kelvin (already done above)
                # dbt_kelvin = celsius_to_kelvin(dry_bulb_t_c)
                # wbt_kelvin = celsius_to_kelvin(wet_bulb_t_c)
                psypi = None

                try:
                    if not critical_error:
                        psypi = SI.state(
                            "DBT", dbt_kelvin, "WBT", wbt_kelvin, pressure_pa)
                    else:
                        self.logger.error(
                            "One or more critical errors prevented the "
                            "humidity from being calculated")
                except TypeError as err:
                    self.logger.error("TypeError: {msg}".format(msg=err))

                if psypi:
                    percent_relative_humidity = psypi[2] * 100

                    # Ensure percent humidity stays within 0 - 100 % range
                    if percent_relative_humidity > 100:
                        percent_relative_humidity = 100
                    elif percent_relative_humidity < 0:
                        percent_relative_humidity = 0

                    # Dry bulb temperature: psypi[0])
                    # Wet bulb temperature: psypi[5])

                    specific_enthalpy = float(psypi[1])
                    humidity = float(percent_relative_humidity)
                    specific_volume = float(psypi[3])
                    humidity_ratio = float(psypi[4])

                    list_measurement = [
                        specific_enthalpy,
                        humidity,
                        specific_volume,
                        humidity_ratio
                    ]

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

                        measurement_dict[channel] = {
                            'measurement': measurement,
                            'unit': unit,
                            'value': list_measurement[channel]
                        }
            else:
                self.error_not_within_max_age()

        elif self.math_type == 'vapor_pressure_deficit':

            vpd_pa = None
            critical_error = False

            success_dbt, temperature = self.get_measurements_from_id(
                self.unique_id_1, self.unique_measurement_id_1)
            success_wbt, humidity = self.get_measurements_from_id(
                self.unique_id_2, self.unique_measurement_id_2)

            if success_dbt and success_wbt:
                vpd_temperature_celsius = float(temperature[1])
                vpd_humidity_percent = float(humidity[1])

                if db_retrieve_table_daemon(
                        DeviceMeasurements,
                        unique_id=self.unique_measurement_id_1):
                    measurement = db_retrieve_table_daemon(
                        DeviceMeasurements,
                        unique_id=self.unique_measurement_id_1)
                else:
                    self.logger.error("Could not find temperature measurement")
                    measurement = None
                    critical_error = True

                if measurement and measurement.unit != 'C':
                    conversion_found = False
                    for each_conv in db_retrieve_table_daemon(Conversion, entry='all'):
                        if (each_conv.convert_unit_from == measurement.unit and
                                each_conv.convert_unit_to == 'C'):
                            vpd_temperature_celsius = convert_units(
                                each_conv.unique_id, vpd_temperature_celsius)
                            conversion_found = True
                    if not conversion_found:
                        self.logger.error(
                            "Could not find conversion for unit "
                            "{unit} to C (Celsius)".format(
                                unit=measurement.unit))
                        critical_error = True

                if db_retrieve_table_daemon(
                        DeviceMeasurements,
                        unique_id=self.unique_measurement_id_2):
                    measurement = db_retrieve_table_daemon(
                        DeviceMeasurements,
                        unique_id=self.unique_measurement_id_2)
                else:
                    self.logger.error("Could not find humidity measurement")
                    measurement = None
                    critical_error = True

                if measurement and measurement.unit != 'percent':
                    conversion_found = False
                    for each_conv in db_retrieve_table_daemon(Conversion, entry='all'):
                        if (each_conv.convert_unit_from == measurement.unit and
                                each_conv.convert_unit_to == 'percent'):
                            vpd_humidity_percent = convert_units(
                                each_conv.unique_id, vpd_humidity_percent)
                            conversion_found = True
                    if not conversion_found:
                        self.logger.error(
                            "Could not find conversion for unit "
                            "{unit} to percent (%)".format(
                                unit=measurement.unit))
                        critical_error = True

                try:
                    if not critical_error:
                        vpd_pa = calculate_vapor_pressure_deficit(
                            vpd_temperature_celsius, vpd_humidity_percent)
                    else:
                        self.logger.error(
                            "One or more critical errors prevented the "
                            "vapor pressure deficit from being calculated")
                except TypeError as err:
                    self.logger.error("TypeError: {msg}".format(msg=err))

                if vpd_pa:
                    measure = self.device_measurements.first()
                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=measure.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        measure, conversion)

                    measurement_dict[channel] = {
                        'measurement': measurement,
                        'unit': unit,
                        'value': vpd_pa
                    }
            else:
                self.error_not_within_max_age()

        else:
            self.logger.error("Unknown math type: {type}".format(type=self.math_type))

        # Finally, add measurements to influxdb
        add_measurements_influxdb(self.unique_id, measurement_dict)
Beispiel #31
0
 def record_dispersal(self, channel, amount, total_on_seconds):
     measure_dict = copy.deepcopy(measurements_dict)
     measure = channels_dict[channel]['measurements']
     measure_dict[measure[0]]['value'] = total_on_seconds
     measure_dict[measure[1]]['value'] = amount
     add_measurements_influxdb(self.unique_id, measure_dict)
Beispiel #32
0
    def get_new_data(self, past_seconds):
        # Basic implementation. Future development may use more complex library to access API
        endpoint = "https://{app}.data.thethingsnetwork.org/api/v2/query/{dev}?last={time}".format(
            app=self.application_id,
            dev=self.device_id,
            time="{}s".format(int(past_seconds)))
        headers = {"Authorization": "key {k}".format(k=self.app_api_key)}
        timestamp_format = '%Y-%m-%dT%H:%M:%S.%f'

        response = requests.get(endpoint, headers=headers)
        try:
            response.json()
        except ValueError:  # No data returned
            self.logger.debug(
                "Response Error. Response: {}. Likely there is no data to be retrieved on TTN"
                .format(response.content))
            return

        for each_resp in response.json():
            if not self.running:
                break

            try:
                datetime_utc = datetime.datetime.strptime(
                    each_resp['time'][:-7], timestamp_format)
            except Exception:
                # Sometimes the original timestamp is in milliseconds
                # instead of nanoseconds. Therefore, remove 3 less digits
                # past the decimal and try again to parse.
                try:
                    datetime_utc = datetime.datetime.strptime(
                        each_resp['time'][:-4], timestamp_format)
                except Exception as e:
                    self.logger.error(
                        "Could not parse timestamp '{}': {}".format(
                            each_resp['time'], e))
                    continue  # Malformed timestamp encountered. Discard measurement.

            if (not self.latest_datetime
                    or self.latest_datetime < datetime_utc):
                self.latest_datetime = datetime_utc

            measurements = {}
            for channel in self.channels_measurement:
                if (self.is_enabled(channel)
                        and self.options_channels['variable_name'][channel]
                        in each_resp
                        and each_resp[self.options_channels['variable_name']
                                      [channel]] is not None):

                    # Original value/unit
                    measurements[channel] = {}
                    measurements[channel][
                        'measurement'] = self.channels_measurement[
                            channel].measurement
                    measurements[channel]['unit'] = self.channels_measurement[
                        channel].unit
                    measurements[channel]['value'] = each_resp[
                        self.options_channels['variable_name'][channel]]
                    measurements[channel]['timestamp_utc'] = datetime_utc

                    # Convert value/unit is conversion_id present and valid
                    if self.channels_conversion[channel]:
                        conversion = db_retrieve_table_daemon(
                            Conversion,
                            unique_id=self.channels_measurement[channel].
                            conversion_id)
                        if conversion:
                            meas = parse_measurement(
                                self.channels_conversion[channel],
                                self.channels_measurement[channel],
                                measurements,
                                channel,
                                measurements[channel],
                                timestamp=datetime_utc)

                            measurements[channel]['measurement'] = meas[
                                channel]['measurement']
                            measurements[channel]['unit'] = meas[channel][
                                'unit']
                            measurements[channel]['value'] = meas[channel][
                                'value']

            if measurements:
                self.logger.debug(
                    "Adding measurements to influxdb: {}".format(measurements))
                add_measurements_influxdb(
                    self.unique_id,
                    measurements,
                    use_same_timestamp=INPUT_INFORMATION[
                        'measurements_use_same_timestamp'])
            else:
                self.logger.debug("No measurements to add to influxdb.")

        # set datetime to latest timestamp
        if self.running:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_input = new_session.query(Input).filter(
                    Input.unique_id == self.unique_id).first()
                if not mod_input.datetime or mod_input.datetime < self.latest_datetime:
                    mod_input.datetime = self.latest_datetime
                    new_session.commit()
Beispiel #33
0
    def calculate_math(self):
        measurement_dict = {}

        #
        # Average (multiple channels)
        #
        if self.math_type == 'average':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            channel, unit, measurement = return_measurement_info(
                math_dev_measurement, None)

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                average = float(sum(measure) / float(len(measure)))

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': average
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        #
        # Average (single channel)
        #
        elif self.math_type == 'average_single':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            math_conversion = db_retrieve_table_daemon(
                Conversion, unique_id=math_dev_measurement.conversion_id)
            (math_channel,
             math_unit,
             math_measurement) = return_measurement_info(
                math_dev_measurement, math_conversion)

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

            if measurement_id == 'output':
                output = db_retrieve_table_daemon(Output, unique_id=device_id)
                measure_channel = output.channel
                measure_unit = output.unit
                measure_measurement = output.measurement
            else:
                device_measurement = db_retrieve_table_daemon(
                    DeviceMeasurements, unique_id=measurement_id)
                if device_measurement:
                    measure_conversion = db_retrieve_table_daemon(
                        Conversion,
                        unique_id=device_measurement.conversion_id)
                else:
                    measure_conversion = None
                (measure_channel,
                 measure_unit,
                 measure_measurement) = return_measurement_info(
                    device_measurement, measure_conversion)

            try:
                return_value = average_past_seconds(
                    device_id,
                    measure_unit,
                    measure_channel,
                    self.max_measure_age,
                    measure=measure_measurement)

                if math_dev_measurement.conversion_id:
                    return_value = convert_units(
                        math_dev_measurement.conversion_id,
                        return_value)

                if return_value:
                    measurement_dict = {
                        math_dev_measurement.channel: {
                            'measurement': measure_measurement,
                            'unit': math_unit,
                            'value': return_value
                        }
                    }
                else:
                    self.error_not_within_max_age()
            except Exception as msg:
                self.logger.exception(
                    "average_single Error: {err}".format(err=msg))

        #
        # Sum (multiple channels)
        #
        elif self.math_type == 'sum':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            if math_dev_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=math_dev_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                math_dev_measurement, conversion)

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                sum_value = float(sum(measure))

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': sum_value
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        #
        # Sum (single channel)
        #
        elif self.math_type == 'sum_single':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            math_conversion = db_retrieve_table_daemon(
                Conversion, unique_id=math_dev_measurement.conversion_id)
            (math_channel,
             math_unit,
             math_measurement) = return_measurement_info(
                math_dev_measurement, math_conversion)

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

            if measurement_id == 'output':
                output = db_retrieve_table_daemon(Output, unique_id=device_id)
                measure_channel = output.channel
                measure_unit = output.unit
                measure_measurement = output.measurement
            else:
                device_measurement = db_retrieve_table_daemon(
                    DeviceMeasurements, unique_id=measurement_id)
                if device_measurement:
                    measure_conversion = db_retrieve_table_daemon(
                        Conversion,
                        unique_id=device_measurement.conversion_id)
                else:
                    measure_conversion = None
                (measure_channel,
                 measure_unit,
                 measure_measurement) = return_measurement_info(
                    device_measurement, measure_conversion)

            try:
                return_value = sum_past_seconds(
                    device_id,
                    measure_unit,
                    measure_channel,
                    self.max_measure_age,
                    measure=measure_measurement)

                if math_dev_measurement.conversion_id:
                    return_value = convert_units(
                        math_dev_measurement.conversion_id,
                        return_value)

                if return_value:
                    measurement_dict = {
                        math_dev_measurement.channel: {
                            'measurement': measure_measurement,
                            'unit': math_unit,
                            'value': return_value
                        }
                    }
                else:
                    self.error_not_within_max_age()
            except Exception as msg:
                self.logger.exception(
                    "sum_single Error: {err}".format(err=msg))

        #
        # Difference between two channels
        #
        elif self.math_type == 'difference':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            if math_dev_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=math_dev_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                math_dev_measurement, conversion)

            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                if self.difference_reverse_order:
                    difference = measure[1] - measure[0]
                else:
                    difference = measure[0] - measure[1]
                if self.difference_absolute:
                    difference = abs(difference)

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': difference
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        #
        # Equation (math performed on measurement)
        #
        elif self.math_type == 'equation':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            if math_dev_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=math_dev_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                math_dev_measurement, conversion)

            success, measure = self.get_measurements_from_str(
                self.equation_input)
            if success:
                if 'x' in self.equation:
                    replaced_str = self.equation.replace('x', str(measure[0]))
                else:
                    replaced_str = self.equation
                equation_output = eval(replaced_str)

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': float(equation_output)
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        #
        # Redundancy (Use next measurement if one isn't currently available)
        #
        elif self.math_type == 'redundancy':
            list_order = self.order_of_use.split(';')
            measurement_success = False

            for each_id_measurement_id in list_order:
                device_id = each_id_measurement_id.split(',')[0]
                measurement_id = each_id_measurement_id.split(',')[1]

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

                try:
                    success_measure, measure = self.get_measurements_from_id(
                        device_id, measurement_id)

                    if success_measure:
                        measurement_dict = {
                            channel: {
                                'measurement': measurement,
                                'unit': unit,
                                'value': float(measure[1]),
                                'timestamp_utc': measure[0],
                            }
                        }
                        measurement_success = True
                        break

                except Exception as msg:
                    self.logger.exception(
                        "redundancy Error: {err}".format(err=msg))

            if not measurement_success:
                self.error_not_within_max_age()

        #
        # Statistical analysis on all measurements from a period of time
        #
        elif self.math_type == 'statistics':
            success, measure = self.get_measurements_from_str(self.inputs)
            if success:
                # Perform some math
                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():
                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=each_measurement.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        each_measurement, conversion)

                    measurement_dict[channel] = {
                            'measurement': measurement,
                            'unit': unit,
                            'value': list_measurement[channel]
                    }

            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        #
        # Verification (only use measurement if it's close to another measurement)
        #
        elif self.math_type == 'verification':
            math_dev_measurement = self.device_measurements.filter(
                DeviceMeasurements.channel == 0).first()
            if math_dev_measurement:
                conversion = db_retrieve_table_daemon(
                    Conversion, unique_id=math_dev_measurement.conversion_id)
            else:
                conversion = None
            channel, unit, measurement = return_measurement_info(
                math_dev_measurement, conversion)

            success, measure = self.get_measurements_from_str(self.inputs)
            if (success and
                    max(measure) - min(measure) <
                    self.max_difference):
                difference = max(measure) - min(measure)

                measurement_dict = {
                    channel: {
                        'measurement': measurement,
                        'unit': unit,
                        'value': difference
                    }
                }
            elif measure:
                self.logger.error(measure)
            else:
                self.error_not_within_max_age()

        #
        # Calculate humidity from wet- and dry-bulb temperatures
        #
        elif self.math_type == 'humidity':
            pressure_pa = 101325
            critical_error = False

            if self.pressure_pa_id and self.pressure_pa_measure_id:

                success_pa, pressure = self.get_measurements_from_id(
                    self.pressure_pa_id, self.pressure_pa_measure_id)

                if success_pa:
                    pressure_pa = int(pressure[1])
                    # Pressure must be in Pa, convert if not

                    measurement_press = db_retrieve_table_daemon(
                        DeviceMeasurements,
                        unique_id=self.pressure_pa_measure_id)
                    if not measurement_press:
                        self.logger.error(
                            "Could not find pressure measurement")
                        measurement_press = None
                        critical_error = True

                    if measurement_press and measurement_press.unit != 'Pa':
                        pressure_pa, status = self.is_measurement_unit(
                            measurement_press.unit, 'Pa', pressure_pa)
                        if status == 'error':
                            critical_error = True

            success_dbt, dry_bulb_t = self.get_measurements_from_id(
                self.dry_bulb_t_id, self.dry_bulb_t_measure_id)
            success_wbt, wet_bulb_t = self.get_measurements_from_id(
                self.wet_bulb_t_id, self.wet_bulb_t_measure_id)

            if success_dbt and success_wbt:
                dbt_kelvin = float(dry_bulb_t[1])
                wbt_kelvin = float(wet_bulb_t[1])

                measurement_db_temp = db_retrieve_table_daemon(
                    DeviceMeasurements,
                    unique_id=self.dry_bulb_t_measure_id)
                if not measurement_db_temp:
                    self.logger.error(
                        "Could not find dry bulb temperature measurement")
                    measurement_db_temp = None
                    critical_error = True

                if measurement_db_temp and measurement_db_temp.unit != 'K':
                    dbt_kelvin, status = self.is_measurement_unit(
                        measurement_db_temp.unit, 'K', dbt_kelvin)
                    if status == 'error':
                        critical_error = True

                measurement_wb_temp = db_retrieve_table_daemon(
                    DeviceMeasurements,
                    unique_id=self.wet_bulb_t_measure_id)
                if not measurement_wb_temp:
                    self.logger.error(
                        "Could not find wet bulb temperature measurement")
                    measurement_wb_temp = None
                    critical_error = True

                if measurement_wb_temp and measurement_wb_temp.unit != 'K':
                    wbt_kelvin, status = self.is_measurement_unit(
                        measurement_wb_temp.unit, 'K', wbt_kelvin)
                    if status == 'error':
                        critical_error = True

                # Convert temperatures to Kelvin (already done above)
                # dbt_kelvin = celsius_to_kelvin(dry_bulb_t_c)
                # wbt_kelvin = celsius_to_kelvin(wet_bulb_t_c)
                psypi = None

                try:
                    if not critical_error:
                        psypi = SI.state(
                            "DBT", dbt_kelvin, "WBT", wbt_kelvin, pressure_pa)
                    else:
                        self.logger.error(
                            "One or more critical errors prevented the "
                            "humidity from being calculated")
                except TypeError as err:
                    self.logger.error("TypeError: {msg}".format(msg=err))

                if psypi:
                    percent_relative_humidity = psypi[2] * 100

                    # Ensure percent humidity stays within 0 - 100 % range
                    if percent_relative_humidity > 100:
                        percent_relative_humidity = 100
                    elif percent_relative_humidity < 0:
                        percent_relative_humidity = 0

                    # Dry bulb temperature: psypi[0])
                    # Wet bulb temperature: psypi[5])

                    specific_enthalpy = float(psypi[1])
                    humidity = float(percent_relative_humidity)
                    specific_volume = float(psypi[3])
                    humidity_ratio = float(psypi[4])

                    list_measurement = [
                        specific_enthalpy,
                        humidity,
                        specific_volume,
                        humidity_ratio
                    ]

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

                        measurement_dict[channel] = {
                            'measurement': measurement,
                            'unit': unit,
                            'value': list_measurement[channel]
                        }
            else:
                self.error_not_within_max_age()

        #
        # Calculate vapor pressure deficit from temperature and humidity
        #
        elif self.math_type == 'vapor_pressure_deficit':
            vpd_pa = None
            critical_error = False

            success_temp, temperature = self.get_measurements_from_id(
                self.unique_id_1, self.unique_measurement_id_1)
            success_hum, humidity = self.get_measurements_from_id(
                self.unique_id_2, self.unique_measurement_id_2)

            if success_temp and success_hum:
                vpd_temperature_celsius = float(temperature[1])
                vpd_humidity_percent = float(humidity[1])

                measurement_temp = db_retrieve_table_daemon(
                    DeviceMeasurements,
                    unique_id=self.unique_measurement_id_1)
                if not measurement_temp:
                    self.logger.error("Could not find temperature measurement")
                    measurement_temp = None
                    critical_error = True

                if measurement_temp and measurement_temp.unit != 'C':
                    vpd_temperature_celsius, status = self.is_measurement_unit(
                        measurement_temp.unit, 'C', vpd_temperature_celsius)
                    if status == 'error':
                        critical_error = True

                measurement_hum = db_retrieve_table_daemon(
                    DeviceMeasurements,
                    unique_id=self.unique_measurement_id_2)
                if not measurement_hum:
                    self.logger.error("Could not find humidity measurement")
                    measurement_hum = None
                    critical_error = True

                if measurement_hum and measurement_hum.unit != 'percent':
                    vpd_humidity_percent, status = self.is_measurement_unit(
                        measurement_hum.unit, 'percent', vpd_humidity_percent)
                    if status == 'error':
                        critical_error = True

                try:
                    if not critical_error:
                        vpd_pa = calculate_vapor_pressure_deficit(
                            vpd_temperature_celsius, vpd_humidity_percent)
                    else:
                        self.logger.error(
                            "One or more critical errors prevented the "
                            "vapor pressure deficit from being calculated")
                except TypeError as err:
                    self.logger.error("TypeError: {msg}".format(msg=err))

                if vpd_pa:
                    math_dev_measurement = self.device_measurements.first()
                    conversion = db_retrieve_table_daemon(
                        Conversion, unique_id=math_dev_measurement.conversion_id)
                    channel, unit, measurement = return_measurement_info(
                        math_dev_measurement, conversion)

                    measurement_dict[channel] = {
                        'measurement': measurement,
                        'unit': unit,
                        'value': vpd_pa
                    }
            else:
                self.error_not_within_max_age()

        else:
            self.logger.error("Unknown math type: {type}".format(type=self.math_type))

        # Finally, add measurements to influxdb
        if measurement_dict:
            self.logger.debug(
                "Adding measurements to InfluxDB with ID {}: {}".format(
                    self.unique_id, measurement_dict))
            add_measurements_influxdb(self.unique_id, measurement_dict)
        else:
            self.logger.debug(
                "No measurements to add to InfluxDB with ID {}".format(
                    self.unique_id))
Beispiel #34
0
    def run(self):
        try:
            self.running = True
            self.logger.info("Activated in {:.1f} ms".format(
                (timeit.default_timer() - self.thread_startup_timer) * 1000))
            self.ready.set()

            # Set up edge detection
            if self.device == 'EDGE':
                GPIO.setmode(GPIO.BCM)
                GPIO.setup(int(self.gpio_location), GPIO.IN)
                GPIO.add_event_detect(int(self.gpio_location),
                                      self.switch_edge_gpio,
                                      callback=self.edge_detected,
                                      bouncetime=self.switch_bouncetime)

            while self.running:
                # Pause loop to modify conditional statements.
                # Prevents execution of conditional while variables are
                # being modified.
                if self.pause_loop:
                    self.verify_pause_loop = True
                    while self.pause_loop:
                        time.sleep(0.1)

                if self.force_measurements_trigger:
                    self.acquire_measurements_now()
                    self.force_measurements_trigger = False

                if self.device not in ['EDGE']:
                    now = time.time()
                    # Signal that a measurement needs to be obtained
                    if now > self.next_measurement and not self.get_new_measurement:
                        self.get_new_measurement = True
                        self.trigger_cond = True
                        while self.next_measurement < now:
                            self.next_measurement += self.period

                    # if signaled and a pre output is set up correctly, turn the
                    # output on or on for the set duration
                    if (self.get_new_measurement and
                            self.pre_output_setup and
                            not self.pre_output_activated):

                        # Set up lock
                        self.input_lock = locket.lock_file(self.lock_file, timeout=30)
                        try:
                            self.input_lock.acquire()
                            self.pre_output_locked = True
                        except locket.LockError:
                            self.logger.error("Could not acquire input lock. Breaking for future locking.")
                            try:
                                os.remove(self.lock_file)
                            except OSError:
                                self.logger.error("Can't delete lock file: Lock file doesn't exist.")

                        self.pre_output_timer = time.time() + self.pre_output_duration
                        self.pre_output_activated = True

                        # Only run the pre-output before measurement
                        # Turn on for a duration, measure after it turns off
                        if not self.pre_output_during_measure:
                            output_on = threading.Thread(
                                target=self.control.output_on,
                                args=(self.pre_output_id,
                                      self.pre_output_duration,))
                            output_on.start()

                        # Run the pre-output during the measurement
                        # Just turn on, then off after the measurement
                        else:
                            output_on = threading.Thread(
                                target=self.control.output_on,
                                args=(self.pre_output_id,))
                            output_on.start()

                    # If using a pre output, wait for it to complete before
                    # querying the input for a measurement
                    if self.get_new_measurement:

                        if (self.pre_output_setup and
                                self.pre_output_activated and
                                now > self.pre_output_timer):

                            if self.pre_output_during_measure:
                                # Measure then turn off pre-output
                                self.update_measure()
                                output_off = threading.Thread(
                                    target=self.control.output_off,
                                    args=(self.pre_output_id,))
                                output_off.start()
                            else:
                                # Pre-output has turned off, now measure
                                self.update_measure()

                            self.pre_output_activated = False
                            self.get_new_measurement = False

                            # release pre-output lock
                            try:
                                if self.pre_output_locked:
                                    self.input_lock.release()
                                    self.pre_output_locked = False
                            except AttributeError:
                                self.logger.error("Can't release lock: "
                                                  "Lock file not present.")

                        elif not self.pre_output_setup:
                            # Pre-output not enabled, just measure
                            self.update_measure()
                            self.get_new_measurement = False

                        # Add measurement(s) to influxdb
                        if self.measurement_success:
                            add_measurements_influxdb(
                                self.unique_id, self.create_measurements_dict())
                            self.measurement_success = False

                self.trigger_cond = False

                time.sleep(self.sample_rate)

            self.running = False

            if self.device == 'EDGE':
                GPIO.setmode(GPIO.BCM)
                GPIO.cleanup(int(self.gpio_location))

            self.logger.info("Deactivated in {:.1f} ms".format(
                (timeit.default_timer() - self.thread_shutdown_timer) * 1000))
        except requests.ConnectionError:
            self.logger.error("Could not connect to influxdb. Check that it "
                              "is running and accepting connections")
        except Exception as except_msg:
            self.logger.exception("Error: {err}".format(
                err=except_msg))