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)
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))
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))
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)
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))
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)
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)
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)
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))
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))
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
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"
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"
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'])
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))
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)
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)
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)
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)
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)
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))
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)
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)
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.")
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)
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)
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()
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))
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))