def __init__(self, input_dev, testing=False): super(InputModule, self).__init__(input_dev, testing=testing, name=__name__) self.atlas_sensor_ftdi = None self.atlas_sensor_uart = None self.atlas_sensor_i2c = None self.ftdi_location = None self.uart_location = None self.i2c_address = None self.i2c_bus = None # Initialize custom options self.temperature_comp_meas_device_id = None self.temperature_comp_meas_measurement_id = None self.max_age = None # Set custom options self.setup_custom_options(INPUT_INFORMATION['custom_options'], input_dev) if not testing: self.input_dev = input_dev self.interface = input_dev.interface if self.temperature_comp_meas_measurement_id: self.atlas_command = AtlasScientificCommand(self.input_dev) try: self.initialize_sensor() except Exception: self.logger.exception("Exception while initializing sensor") # Throw out first measurement of Atlas Scientific sensor, as it may be prone to error self.get_measurement()
def dual_commands_to_sensor(input_sel, first_cmd, amount, second_cmd, current_stage): """ Handles the Atlas Scientific pH sensor calibration: Sends two consecutive commands to the sensor board Denies advancement to the next stage if any commands fail Permits advancement to the next stage if all commands succeed Prints any errors or successes """ return_error = None set_temp = None if first_cmd == 'temperature': unit = 'C' set_temp = amount else: unit = 'pH' atlas_command = AtlasScientificCommand(input_sel) first_status, first_return_str = atlas_command.calibrate( first_cmd, temperature=set_temp) info_str = "{act}: {lvl} ({amt} {unit}): {resp}".format( act=gettext('Calibration'), lvl=first_cmd, amt=amount, unit=unit, resp=first_return_str) if first_status: flash(info_str, "error") return_error = first_return_str return_stage = current_stage else: flash(info_str, "success") second_status, second_return_str = atlas_command.calibrate(second_cmd) second_info_str = "{act}: {cmd}: {resp}".format(act=gettext('Command'), cmd=second_cmd, resp=second_return_str) if second_status: flash(second_info_str, "error") return_error = second_return_str return_stage = current_stage else: flash(second_info_str, "success") # Advance to the next stage return_stage = current_stage + 1 return return_stage, return_error
def setup_atlas_flow(): """ Step-by-step tool for calibrating the Atlas Scientific RGB sensor """ if not utils_general.user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) form_flow_calibrate = forms_calibration.CalibrationAtlasFlow() input_dev = Input.query.filter(Input.device == 'ATLAS_FLOW').all() ui_stage = 'start' selected_input = None input_device_name = None complete_with_error = None # Begin calibration from Selected input if form_flow_calibrate.clear_calibration.data: selected_input = Input.query.filter_by( unique_id=form_flow_calibrate.selected_input_id.data).first() dict_inputs = parse_input_information() list_inputs_sorted = generate_form_input_list(dict_inputs) if not selected_input: flash( 'Input not found: {}'.format( form_flow_calibrate.selected_input_id.data), 'error') else: for each_input in list_inputs_sorted: if selected_input.device == each_input[0]: input_device_name = each_input[1] if selected_input.is_activated: control = DaemonControl() return_status, return_string = control.input_atlas_flow_clear_total_volume( selected_input.unique_id) else: atlas_command = AtlasScientificCommand(selected_input) return_status, return_string = atlas_command.send_command('Clear') if return_status: complete_with_error = return_string ui_stage = 'complete' return render_template('tools/calibration_options/atlas_flow.html', complete_with_error=complete_with_error, form_flow_calibrate=form_flow_calibrate, input=input_dev, input_device_name=input_device_name, selected_input=selected_input, ui_stage=ui_stage)
class InputModule(AbstractInput): """A sensor support class that monitors the Atlas Scientific sensor ORP""" def __init__(self, input_dev, testing=False): super(InputModule, self).__init__(input_dev, testing=testing, name=__name__) self.atlas_sensor_ftdi = None self.atlas_sensor_uart = None self.atlas_sensor_i2c = None self.ftdi_location = None self.uart_location = None self.i2c_address = None self.i2c_bus = None # Initialize custom options self.temperature_comp_meas_device_id = None self.temperature_comp_meas_measurement_id = None self.max_age = None # Set custom options self.setup_custom_options(INPUT_INFORMATION['custom_options'], input_dev) if not testing: self.input_dev = input_dev self.interface = input_dev.interface if self.temperature_comp_meas_measurement_id: self.atlas_command = AtlasScientificCommand(self.input_dev) try: self.initialize_sensor() except Exception: self.logger.exception("Exception while initializing sensor") # Throw out first measurement of Atlas Scientific sensor, as it may be prone to error self.get_measurement() def initialize_sensor(self): from mycodo.devices.atlas_scientific_ftdi import AtlasScientificFTDI from mycodo.devices.atlas_scientific_i2c import AtlasScientificI2C from mycodo.devices.atlas_scientific_uart import AtlasScientificUART if self.interface == 'FTDI': self.ftdi_location = self.input_dev.ftdi_location self.atlas_sensor_ftdi = AtlasScientificFTDI(self.ftdi_location) elif self.interface == 'UART': self.uart_location = self.input_dev.uart_location self.atlas_sensor_uart = AtlasScientificUART(self.uart_location) elif self.interface == 'I2C': self.i2c_address = int(str(self.input_dev.i2c_location), 16) self.i2c_bus = self.input_dev.i2c_bus self.atlas_sensor_i2c = AtlasScientificI2C( i2c_address=self.i2c_address, i2c_bus=self.i2c_bus) def get_measurement(self): """ Gets the sensor's ORP measurement via UART/I2C """ orp = None self.return_dict = measurements_dict.copy() # Compensate measurement based on a temperature measurement if self.temperature_comp_meas_measurement_id and self.atlas_command: self.logger.debug("pH sensor set to calibrate temperature") last_measurement = self.get_last_measurement( self.temperature_comp_meas_device_id, self.temperature_comp_meas_measurement_id, max_age=self.max_age) if last_measurement: self.logger.debug( "Latest temperature used to calibrate: {temp}".format( temp=last_measurement[1])) ret_value, ret_msg = self.atlas_command.calibrate( 'temperature', set_amount=last_measurement[1]) time.sleep(0.5) self.logger.debug("Calibration returned: {val}, {msg}".format( val=ret_value, msg=ret_msg)) else: self.logger.error( "Calibration measurement not found within the past " "{} seconds".format(self.max_age)) # Read sensor via UART if self.interface == 'FTDI': if self.atlas_sensor_ftdi.setup: lines = self.atlas_sensor_ftdi.query('R') if lines: self.logger.debug("All Lines: {lines}".format(lines=lines)) # 'check probe' indicates an error reading the sensor if 'check probe' in lines: self.logger.error('"check probe" returned from sensor') # if a string resembling a float value is returned, this # is out measurement value elif str_is_float(lines[0]): orp = float(lines[0]) self.logger.debug( 'Value[0] is float: {val}'.format(val=orp)) else: # During calibration, the sensor is put into # continuous mode, which causes a return of several # values in one string. If the return value does # not represent a float value, it is likely to be a # string of several values. This parses and returns # the first value. if str_is_float(lines[0].split(b'\r')[0]): orp = lines[0].split(b'\r')[0] # Lastly, this is called if the return value cannot # be determined. Watchthe output in the GUI to see # what it is. else: orp = lines[0] self.logger.error( 'Value[0] is not float or "check probe": ' '{val}'.format(val=orp)) else: self.logger.error('FTDI device is not set up.' 'Check the log for errors.') # Read sensor via UART elif self.interface == 'UART': if self.atlas_sensor_uart.setup: lines = self.atlas_sensor_uart.query('R') if lines: self.logger.debug("All Lines: {lines}".format(lines=lines)) # 'check probe' indicates an error reading the sensor if 'check probe' in lines: self.logger.error('"check probe" returned from sensor') # if a string resembling a float value is returned, this # is out measurement value elif str_is_float(lines[0]): orp = float(lines[0]) self.logger.debug( 'Value[0] is float: {val}'.format(val=orp)) else: # During calibration, the sensor is put into # continuous mode, which causes a return of several # values in one string. If the return value does # not represent a float value, it is likely to be a # string of several values. This parses and returns # the first value. if str_is_float(lines[0].split(b'\r')[0]): orp = lines[0].split(b'\r')[0] # Lastly, this is called if the return value cannot # be determined. Watchthe output in the GUI to see # what it is. else: orp = lines[0] self.logger.error( 'Value[0] is not float or "check probe": ' '{val}'.format(val=orp)) else: self.logger.error('UART device is not set up.' 'Check the log for errors.') # Read sensor via I2C elif self.interface == 'I2C': if self.atlas_sensor_i2c.setup: ec_status, ec_str = self.atlas_sensor_i2c.query('R') if ec_status == 'error': self.logger.error( "Sensor read unsuccessful: {err}".format(err=ec_str)) elif ec_status == 'success': orp = float(ec_str) else: self.logger.error( 'I2C device is not set up. Check the log for errors.') self.value_set(0, orp) return self.return_dict
def setup_atlas_ph(): """ Step-by-step tool for calibrating the Atlas Scientific pH sensor """ if not utils_general.user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) form_ph_calibrate = forms_calibration.CalibrationAtlasph() input_dev = Input.query.filter( or_(Input.device == 'ATLAS_PH_UART', Input.device == 'ATLAS_PH_I2C')).all() stage = 0 next_stage = None selected_input = None input_device_name = None complete_with_error = None if form_ph_calibrate.hidden_next_stage.data is not None: next_stage = int(form_ph_calibrate.hidden_next_stage.data) # Clear Calibration memory if form_ph_calibrate.clear_calibration.data: selected_input = Input.query.filter_by( unique_id=form_ph_calibrate.selected_sensor_id.data).first() atlas_command = AtlasScientificCommand(selected_input) status, message = atlas_command.calibrate('clear_calibration') if status: flash(message, "error") else: flash(message, "success") # Begin calibration from Selected input elif form_ph_calibrate.go_from_first_stage.data: stage = 1 selected_input = Input.query.filter_by( unique_id=form_ph_calibrate.selected_sensor_id.data).first() if not selected_input: flash('Input not found: {}'.format( form_ph_calibrate.selected_sensor_id.data), 'error') else: for each_input in DEVICES: if selected_input.device == each_input[0]: input_device_name = each_input[1] # Continue calibration from selected input elif (form_ph_calibrate.go_to_next_stage.data or form_ph_calibrate.go_to_last_stage.data or (next_stage is not None and next_stage > 1)): selected_input = Input.query.filter_by( unique_id=form_ph_calibrate.hidden_sensor_id.data).first() for each_input in DEVICES: if selected_input.device == each_input[0]: input_device_name = each_input[1] if next_stage == 2: if form_ph_calibrate.temperature.data is None: flash(gettext("A valid temperature is required: %(temp)s is invalid.", temp=form_ph_calibrate.temperature.data), "error") stage = 1 else: temp = '{temp:.2f}'.format( temp=form_ph_calibrate.temperature.data) stage, complete_with_error = dual_commands_to_sensor( selected_input, 'temperature', temp, 'continuous', 1) elif next_stage == 3: stage, complete_with_error = dual_commands_to_sensor( selected_input, 'mid', '7.0', 'continuous', 2) elif next_stage == 4: stage, complete_with_error = dual_commands_to_sensor( selected_input, 'low', '4.0', 'continuous', 3) elif next_stage == 5: stage, complete_with_error = dual_commands_to_sensor( selected_input, 'high', '10.0', 'end', 4) return render_template('tools/calibration_options/atlas_ph.html', complete_with_error=complete_with_error, form_ph_calibrate=form_ph_calibrate, sensor=input_dev, sensor_device_name=input_device_name, selected_sensor=selected_input, stage=stage)
def dual_commands_to_sensor(input_sel, first_cmd, amount, second_cmd, current_stage, next_stage=None): """ Handles the Atlas Scientific pH sensor calibration: Sends two consecutive commands to the sensor board Denies advancement to the next stage if any commands fail Permits advancement to the next stage if all commands succeed Prints any errors or successes """ return_error = None set_amount = None if first_cmd == 'temperature': unit = '°C' set_amount = amount elif first_cmd in ['ec_dry', 'ec_low', 'ec_high']: unit = 'μS' set_amount = amount else: unit = 'pH' atlas_command = AtlasScientificCommand(input_sel) sensor_measurement = atlas_command.get_sensor_measurement() first_status, first_return_str = atlas_command.calibrate( first_cmd, set_amount=set_amount) if isinstance(first_return_str, tuple): message_status = first_return_str[0] message_info = first_return_str[1] first_return_message = "{}".format(message_status) if message_info: first_return_message += ": {}".format(message_info) else: first_return_message = first_return_str first_info_str = "{act}: {lvl} ({amt} {unit}): {resp}".format( act=TRANSLATIONS['calibration']['title'], lvl=first_cmd, amt=amount, unit=unit, resp=first_return_message) if sensor_measurement != 'NA': first_info_str = "{} {}".format(sensor_measurement, first_info_str) if first_status: flash(first_info_str, "error") return_error = first_return_str return_stage = current_stage else: flash(first_info_str, "success") time.sleep(0.1) # Add space between commands second_status, second_return_str = atlas_command.calibrate(second_cmd) if isinstance(second_return_str, tuple): message_status = second_return_str[0] message_info = second_return_str[1] second_return_message = "{}".format(message_status) if message_info: second_return_message += ": {}".format(message_info) else: second_return_message = second_return_str second_info_str = "{act}: {cmd}: {resp}".format( act=gettext('Command'), cmd=second_cmd, resp=second_return_message) if sensor_measurement != 'NA': second_info_str = "{} {}".format(sensor_measurement, second_info_str) if second_status: flash(second_info_str, "error") return_error = second_return_str return_stage = current_stage else: flash(second_info_str, "success") # Advance to the next stage return_stage = next_stage return return_stage, return_error
def setup_atlas_ph(): """ Step-by-step tool for calibrating the Atlas Scientific pH sensor """ if not utils_general.user_has_permission('edit_controllers'): return redirect(url_for('routes_general.home')) form_ph_calibrate = forms_calibration.CalibrationAtlasph() input_dev = Input.query.filter(Input.device == 'ATLAS_PH').all() ui_stage = 'start' backend_stage = None next_stage = None selected_input = None selected_point_calibration = None input_device_name = None complete_with_error = None if form_ph_calibrate.hidden_current_stage.data: backend_stage = form_ph_calibrate.hidden_current_stage.data if form_ph_calibrate.hidden_selected_point_calibration.data: selected_point_calibration = form_ph_calibrate.hidden_selected_point_calibration.data elif form_ph_calibrate.point_calibration.data: selected_point_calibration = form_ph_calibrate.point_calibration.data if selected_point_calibration: list_point_calibrations = selected_point_calibration.split(',') else: list_point_calibrations = [] # Clear Calibration memory if form_ph_calibrate.clear_calibration.data: selected_input = Input.query.filter_by( unique_id=form_ph_calibrate.selected_input_id.data).first() atlas_command = AtlasScientificCommand(selected_input) status, message = atlas_command.calibrate('clear_calibration') sensor_measurement = atlas_command.get_sensor_measurement() if isinstance(message, tuple): message_status = message[0] message_info = message[1] message = "Calibration command returned from sensor: {}".format( message_status) if message_info: message += ": {}".format(message_info) else: message = "Calibration command returned from sensor: {}".format( message) if sensor_measurement != 'NA': message = "{} {}".format(sensor_measurement, message) if status: flash(message, "error") else: flash(message, "success") # Begin calibration from Selected input elif form_ph_calibrate.start_calibration.data: ui_stage = 'temperature' selected_input = Input.query.filter_by( unique_id=form_ph_calibrate.selected_input_id.data).first() dict_inputs = parse_input_information() list_inputs_sorted = generate_form_input_list(dict_inputs) if not selected_input: flash( 'Input not found: {}'.format( form_ph_calibrate.selected_input_id.data), 'error') else: for each_input in list_inputs_sorted: if selected_input.device == each_input[0]: input_device_name = each_input[1] # Continue calibration from selected input elif (form_ph_calibrate.go_to_next_stage.data or form_ph_calibrate.go_to_last_stage.data or (backend_stage is not None and backend_stage not in ['start', 'temperature'])): selected_input = Input.query.filter_by( unique_id=form_ph_calibrate.hidden_input_id.data).first() dict_inputs = parse_input_information() list_inputs_sorted = generate_form_input_list(dict_inputs) for each_input in list_inputs_sorted: if selected_input.device == each_input[0]: input_device_name = each_input[1] if backend_stage in ['temperature', 'low', 'mid', 'high']: time.sleep(2) # Sleep makes querying sensor more stable # Determine next ui_stage if backend_stage == 'temperature': next_stage = list_point_calibrations[0] logger.error("next_stage: {}".format(next_stage)) else: current_stage_index = list_point_calibrations.index(backend_stage) if current_stage_index == len(list_point_calibrations) - 1: next_stage = 'complete' else: next_stage = list_point_calibrations[current_stage_index + 1] if form_ph_calibrate.clear_calibration.data: pass elif backend_stage == 'temperature': if form_ph_calibrate.temperature.data is None: flash( gettext( "A valid temperature is required: %(temp)s is invalid.", temp=form_ph_calibrate.temperature.data), "error") ui_stage = 'start' else: temp = '{temp:.2f}'.format( temp=float(form_ph_calibrate.temperature.data)) ui_stage, complete_with_error = dual_commands_to_sensor( selected_input, 'temperature', temp, 'continuous', current_stage='temperature', next_stage=next_stage) elif backend_stage == 'low': ui_stage, complete_with_error = dual_commands_to_sensor( selected_input, 'low', '4.0', 'continuous', current_stage='low', next_stage=next_stage) elif backend_stage == 'mid': ui_stage, complete_with_error = dual_commands_to_sensor( selected_input, 'mid', '7.0', 'continuous', current_stage='mid', next_stage=next_stage) elif backend_stage == 'high': ui_stage, complete_with_error = dual_commands_to_sensor( selected_input, 'high', '10.0', 'end', current_stage='high', next_stage=next_stage) return render_template( 'tools/calibration_options/atlas_ph.html', complete_with_error=complete_with_error, form_ph_calibrate=form_ph_calibrate, input=input_dev, input_device_name=input_device_name, selected_input=selected_input, selected_point_calibration=selected_point_calibration, ui_stage=ui_stage)
def get_measurement(self): """ Gets the sensor's pH measurement via UART/I2C """ ph = None return_dict = measurements_dict.copy() # Calibrate the pH measurement based on a temperature measurement if (self.calibrate_sensor_measure and ',' in self.calibrate_sensor_measure): self.logger.debug("pH sensor set to calibrate temperature") device_id = self.calibrate_sensor_measure.split(',')[0] measurement_id = self.calibrate_sensor_measure.split(',')[1] device_measurement = self.device_measurements.filter( DeviceMeasurements.unique_id == measurement_id).first() if device_measurement: conversion = db_retrieve_table_daemon( Conversion, unique_id=device_measurement.conversion_id) else: conversion = None channel, unit, measurement = return_measurement_info( device_measurement, conversion) last_measurement = read_last_influxdb( device_id, measurement.unit, measurement.measurement, measurement.channel, self.max_age) if last_measurement: self.logger.debug( "Latest temperature used to calibrate: {temp}".format( temp=last_measurement[1])) atlas_command = AtlasScientificCommand(self.input_dev) ret_value, ret_msg = atlas_command.calibrate( 'temperature', temperature=last_measurement[1]) time.sleep(0.5) self.logger.debug( "Calibration returned: {val}, {msg}".format( val=ret_value, msg=ret_msg)) else: self.logger.error( "Calibration measurement not found within the past " "{} seconds".format(self.max_age)) # Read sensor via FTDI if self.interface == 'FTDI': if self.atlas_sensor_ftdi.setup: lines = self.atlas_sensor_ftdi.query('R') if lines: self.logger.debug( "All Lines: {lines}".format(lines=lines)) # 'check probe' indicates an error reading the sensor if 'check probe' in lines: self.logger.error( '"check probe" returned from sensor') # if a string resembling a float value is returned, this # is out measurement value elif str_is_float(lines[0]): ph = float(lines[0]) self.logger.debug( 'Value[0] is float: {val}'.format(val=ph)) else: # During calibration, the sensor is put into # continuous mode, which causes a return of several # values in one string. If the return value does # not represent a float value, it is likely to be a # string of several values. This parses and returns # the first value. if str_is_float(lines[0].split(b'\r')[0]): ph = lines[0].split(b'\r')[0] # Lastly, this is called if the return value cannot # be determined. Watchthe output in the GUI to see # what it is. else: ph = lines[0] self.logger.error( 'Value[0] is not float or "check probe": ' '{val}'.format(val=ph)) else: self.logger.error('FTDI device is not set up.' 'Check the log for errors.') # Read sensor via UART elif self.interface == 'UART': if self.atlas_sensor_uart.setup: lines = self.atlas_sensor_uart.query('R') if lines: self.logger.debug( "All Lines: {lines}".format(lines=lines)) # 'check probe' indicates an error reading the sensor if 'check probe' in lines: self.logger.error( '"check probe" returned from sensor') # if a string resembling a float value is returned, this # is out measurement value elif str_is_float(lines[0]): ph = float(lines[0]) self.logger.debug( 'Value[0] is float: {val}'.format(val=ph)) else: # During calibration, the sensor is put into # continuous mode, which causes a return of several # values in one string. If the return value does # not represent a float value, it is likely to be a # string of several values. This parses and returns # the first value. if str_is_float(lines[0].split(b'\r')[0]): ph = lines[0].split(b'\r')[0] # Lastly, this is called if the return value cannot # be determined. Watchthe output in the GUI to see # what it is. else: ph = lines[0] self.logger.error( 'Value[0] is not float or "check probe": ' '{val}'.format(val=ph)) else: self.logger.error('UART device is not set up.' 'Check the log for errors.') # Read sensor via I2C elif self.interface == 'I2C': if self.atlas_sensor_i2c.setup: ph_status, ph_str = self.atlas_sensor_i2c.query('R') if ph_status == 'error': self.logger.error( "Sensor read unsuccessful: {err}".format( err=ph_str)) elif ph_status == 'success': ph = float(ph_str) else: self.logger.error( 'I2C device is not set up. Check the log for errors.') return_dict[0]['value'] = ph return return_dict
def get_measurement(self): """ Gets the sensor's pH measurement via UART/I2C """ self._ph = None ph = None # Calibrate the pH measurement based on a temperature measurement if (self.calibrate_sensor_measure and ',' in self.calibrate_sensor_measure): self.logger.debug("pH sensor set to calibrate temperature") device_id = self.calibrate_sensor_measure.split(',')[0] measurement = self.calibrate_sensor_measure.split(',')[1] last_measurement = read_last_influxdb(device_id, measurement, duration_sec=300) if last_measurement: self.logger.debug( "Latest temperature used to calibrate: {temp}".format( temp=last_measurement[1])) atlas_command = AtlasScientificCommand(self.input_dev) ret_value, ret_msg = atlas_command.calibrate( 'temperature', temperature=last_measurement[1]) time.sleep(0.5) self.logger.debug("Calibration returned: {val}, {msg}".format( val=ret_value, msg=ret_msg)) # Read sensor via UART if self.interface == 'UART': if self.atlas_sensor_uart.setup: lines = self.atlas_sensor_uart.query('R') if lines: self.logger.debug("All Lines: {lines}".format(lines=lines)) # 'check probe' indicates an error reading the sensor if 'check probe' in lines: self.logger.error('"check probe" returned from sensor') # if a string resembling a float value is returned, this # is out measurement value elif str_is_float(lines[0]): ph = float(lines[0]) self.logger.debug( 'Value[0] is float: {val}'.format(val=ph)) else: # During calibration, the sensor is put into # continuous mode, which causes a return of several # values in one string. If the return value does # not represent a float value, it is likely to be a # string of several values. This parses and returns # the first value. if str_is_float(lines[0].split(b'\r')[0]): ph = lines[0].split(b'\r')[0] # Lastly, this is called if the return value cannot # be determined. Watchthe output in the GUI to see # what it is. else: ph = lines[0] self.logger.error( 'Value[0] is not float or "check probe": ' '{val}'.format(val=ph)) else: self.logger.error('UART device is not set up.' 'Check the log for errors.') # Read sensor via I2C elif self.interface == 'I2C': if self.atlas_sensor_i2c.setup: ph_status, ph_str = self.atlas_sensor_i2c.query('R') if ph_status == 'error': self.logger.error( "Sensor read unsuccessful: {err}".format(err=ph_str)) elif ph_status == 'success': ph = float(ph_str) else: self.logger.error( 'I2C device is not set up. Check the log for errors.') return ph