Exemple #1
0
class CouchHeartbeat(object):
    """Heartbeat object    """

    logger = None

    def __init__(self):
        """Standard constructor
        Get and hold a Python logger
        """
        self._logger = Logger('CouchHeartbeat', Logger.INFO,
                              "/home/pi/MVP/logs/heartbeat.log")

    def check(self, port):
        """Ping the database
        Should return the welcome message
        Throws an exception if cannot make a connection to the database

        Args:
            port: port the database is communicating on
        Returns:
            None
        Raises:
            None
        """
        cmd = 'curl -X GET http://localhost:5984'
        ret = os.system(cmd)
        if ret == 0:
            self._logger.info('localhost Couch is Up')
        else:
            self._logger.error('Couch is Down')
            self.restart()

    def restart(self):
        """System restart (reboot)
        Args:
            None
        Returns:
            None
        Raises:
            None
        """
        cmd = 'sudo reboot'
        self.logger.warning('System restart: %s' % (cmd))
        os.system(cmd)
Exemple #2
0
class SCD30:
    CMD_START_PERIODIC_MEASUREMENT = 0x0010
    CMD_STOP_PERIODIC_MEASUREMENT = 0x0104
    CMD_READ_MEASUREMENT = 0x0300
    CMD_GET_DATA_READY = 0x0202
    CMD_SET_MEASUREMENT_INTERVAL = 0x4600
    CMD_SET_TEMPERATURE_OFFSET = 0x5403
    CMD_SET_ALTITUDE = 0x5102

    WORD_LEN = 2
    COMMAND_LEN = 2
    MAX_BUFFER_WORDS = 24

    STATUS_FAIL = 1
    STATUS_OK = 0

    CO2_DATA_INDEX = 0
    TEMP_DATA_INDEX = 1
    HUMIDITY_DATA_INDEX = 2

    def __init__(self, logger=None):
        """Create sensor object
           Args:
               None
           Returns:
               None
           Raises:
               None
        """        
        self._addr = addr
        self._path = path
        self._logger = logger
        if logger == None:
           self._logger = Logger("SCD30", Logger.INFO)
        self._i2c = I2C(path, addr, self._logger)
        self._logger.debug("initialize SCD30")        
        
        self.start_periodic_measurement()
        

    
    def start_periodic_measurement(self):
        """Start sensor to generate data (about every 2 seconds
           Args:
               self:
               test: flag for test logic
           Returns:
               None
           Raises:
               None
        """        

        # command 0x0010, altitude 0x03eb, crc_check 0x87
        # altitude set to 1003 mb
        #msgs = [0x00,0x10, 0x03, 0xeb, 0x87]
        # Altitude is currently set to 0
        self._logger.debug("In Start Periodic Measurment")        
        
        msgs = [0x00,0x10, 0x00, 0x00, 0x81]
        self._i2c.msg_write(msgs)                

    # TESTE ADN WORKS!
    def stop_periodic_measurement(self):
        """Stop automatic data gathering
           Args:
               None
           Returns:
               None
           Raises:
               None
        """
        self._logger.debug("In Stop Periodic Measurment")        
        
        msgs = [0x01,0x14]
        self._i2c.msg_write(msgs)                
        
#        return self._i2c.msg_write(self.CMD_STOP_PERIODIC_MEASUREMENT
        pass


    def read_measurement(self):
        """Read data
           Args:
               self:
               test:
           Returns:
               CO2
               Temp
               Relative Humidity
           Raises:
               None
        """
        self._logger.debug("In Read Measurment")        
        
        self._i2c.msg_write([0x03, 0x00])
        msgs = self._i2c.msg_read(18)
        
        data3 = self.bytes_to_value(msgs[0].data)                    
        return data3[self.CO2_DATA_INDEX], data3[self.TEMP_DATA_INDEX], data3[self.HUMIDITY_DATA_INDEX]

    def bytes_to_value(self, byte_array):
        """Convert array of byte values into three float values
           Args:
               self
               byte_array: array of data from I2C sensor
               test
           Returns:
               data: array of float values (CO2, Temp, RH)
           Raises:
               None
        """        
        self._logger.debug("In Bytes To Value")        
        
        # Array for value bytes (exclude crc check byte)
        bytes_buf = [0]*12 # 2 words for each co2, temperature, humidity
        l = len(bytes_buf)
        ld = len(byte_array)
        # array for word conversion - two words per value
        word_buf = [0]*int(l/2)
        # final data structure - one place per value
        data = [0]*int(len(word_buf)/2)

        # Load bytes_buffer, strip crc bytes
        y = 0
        for x in range(0, ld, 6):
#            print("x: " + str(x) + " y: " + str(y))
            bytes_buf[y] = byte_array[x]
            bytes_buf[y+1] = byte_array[x+1]
            bytes_buf[y+2] = byte_array[x+3]
            bytes_buf[y+3] = byte_array[x+4]            
            y += 4
        self._logger.detail("bytes_buf: " + str(bytes_buf))        
        
        # Convert sensor data reads to physical value per Sensirion specification
        # Load buffer with values
        # Cast 4 bytes to one unsigned 32 bit integer
        # Cast unsigned 32 bit integer to 32 bit float

        # Convert bytes to words
        for i in range(len(word_buf)):
            word_buf[i] = (bytes_buf[i*2] << 8) | bytes_buf[i*2+1]
            self._logger.detail(str(hex(bytes_buf[i])) + " " + str(hex(bytes_buf[i+1])))
            self._logger.detail(hex(word_buf[i]))

        #convert words to int32
        data[self.CO2_DATA_INDEX] = (word_buf[0] << 16) | word_buf[1]
        data[self.TEMP_DATA_INDEX] = (word_buf[2] << 16) | word_buf[3]
        data[self.HUMIDITY_DATA_INDEX] = (word_buf[4] << 16) | word_buf[5]

        #Convert int32 data to float32
        floatData = numpy.array(data, dtype=numpy.int32)
        data = floatData.view('float32')

        self._logger.detail("CO2: " + str(data[self.CO2_DATA_INDEX]))
        self._logger.detail("Temp: " + str(data[self.TEMP_DATA_INDEX]))
        self._logger.detail("RH: " + str(data[self.HUMIDITY_DATA_INDEX]))

        return data
        
        

    # interval : (u16 integer)
    def set_measurement_interval(self, interval_sec):
        """Set frequency of automatic data collectoin
           Args:
               self
               interval_sec: time in seconds
               test
           Returns:
               None
           Raises:
               None
        """
        self._logger.debug("In Set Measurment Interval")
        if interval_sec < 2 or interval_sec > 1800:
            return self.STATUS_FAIL
# Need to finish this so value is 32 word split to two 16 words
# Calculate crc value for last word
        # [Cmd MSB, Cmd LSB, Interval MSB, Interval LSB, CRC]
        # msb, lsb = convert_word(interval_sec, test)
        # crc = calc_crc(msb, lsb, test)
        # msg = [0x46,0x10, msb, lsb, crc]
        msgs = [0x46,0x10, 0x00, 0x02, 0xE3]
        self._i2c.msg_write(msgs)                


    def get_data_ready(self):
        """Check if have fresh data from periodic update
           Args:
               self
               test
           Returns:
               ready flag
           Raises:
               None
        """          
        self._logger.debug("In Get Data Ready")        
        self._i2c.msg_write([0x02, 0x02])
        msgs = self._i2c.msg_read(3)        
        #for msg in msgs:
        #    self._logger.detail("Msg: " + str(msg))
        #    for d in msg.data:
        #        self._logger.detail("Data: " + str(hex(d)))
        #self._logger.detail("Data: " + str(msgs[0].data[1]))
        return msgs[0].data[1]
    
    # Strange behaviour
    def set_temperature_offset(self, temperature_offset):
        """Temperature compensation offset
           Args:
               self
               temperature offset
               test
           Returns:
               None
           Raises:
               None
        """          
        self._logger.debug("In Set Temperature Offset")                
# Need to finish this so value is 32 word split to two 16 words
# Calculate crc value for last word
        # [Cmd MSB, Cmd LSB, Interval MSB, Interval LSB, CRC]
        # msb, lsb = convert_word(interval_sec, test)
        # crc = calc_crc(msb, lsb, test)
        # msg = [0x54,0x03, msb, lsb, crc]
        msgs = [0x54,0x03, 0x00, 0x02, 0xE3]
        self._i2c.msg_write(msgs)                 


    # TESTE ADN WORKS!
    def set_altitude(self, altitude):
        """Altitude compensation 
           Args:
               self
               altitude: uint16, height over sea level in [m] above 0
               test
           Returns:
               None
           Raises:
               None
        """          
        self._logger.debug("In Set Altitude")                
# Need to finish this so value is 32 word split to two 16 words
# Calculate crc value for last word
        # [Cmd MSB, Cmd LSB, Interval MSB, Interval LSB, CRC]
        # msb, lsb = convert_word(interval_sec, test)
        # crc = calc_crc(msb, lsb, test)
        # msg = [0x51,0x02, msb, lsb, crc]
        msgs = [0x51,0x02, 0x00, 0x02, 0xE3]
        self._i2c.msg_write(msgs)                 

    # TESTE ADN WORKS!
    def get_configured_address(self, test=False):
        """Altitude compensation 
           Args:
               self
               test
           Returns:
               self._addr: address of the sensor
           Raises:
               None
        """                  
        self._logger.debug("In Get Configured Address")        
        return self._addr
    
    def get_data(self):
        """High level logic to simply get data
           Args:
               self
               test
           Returns:
               co2: co2 value
               temp: temperature value
               rh: relative humidity value
           Raises:
               NameError: if error in logic (I2C Problem)
        """
        self._logger.debug("In Get Data")                
        for x in range(0, 4): # Only give four reste tries before giving up
            try:
                while True:
                    # Test if data is ready
                    if self.get_data_ready():
                       # fetch data 
                       co2, temp, rh = self.read_measurement()
                       self._logger.detail("CO2: " + str(co2))
                       self._logger.detail("Temp: " + str(temp))
                       self._logger.detail("RH: " + str(rh))
                       return co2, temp, rh
                    time.sleep(1)
            # try reset to see if can recover from errors
            except Exception as e:
                self._logger.error("{}, {}".format("Data Ready Err: ", e))
                self.__init__()
                time.sleep(2)
        # Give up if cannot fix the problems
        raise NameError("Too Many Failures")                        
Exemple #3
0
class StartUp(object):
    def __init__(self, logger=None):
        if logger == None:
            self._logger = Logger("StartUp", Logger.INFO)
        else:
            self._logger = logger
        self._logger.debug("StartUp Initialized")

    def check(self, test=False):
        """Main function to run what needs to be done at restart
        Args:
            test: flag for testing system
        Returns:
            None:
        Raises:
            None
        """
        light_state = "Unknown"
        try:
            light_state = self.checkLight()
        except Exception as e:
            self._logger.error(e)
        self.checkPump()

    def checkPump(self):
        """Make sure the pump is turned off
            Don't check, just turn off
            If no pump in installed, this will just flip the GPIO
        Args:
            test: flag for testing system
        Returns:
            state: (should be Off)
        Raises:
            None
        """

        self._logger.debug("In checkPump")
        pump_state = "Unknown"
        try:
            pump_state = self.pumpOff()
            self._logger.debug("{}: {}".format("Pump State", pump_state))
        except Exception as e:
            self._logger.error(e)

    def pumpOff(self):
        """Make sure the pump is turned off
        Args:
            test: flag for testing system
        Returns:
            state: (should be Off)
        Raises:
            None
        """
        from Pump import Pump
        pump = Pump()

        self._logger.debug("In pumpOff")
        p = Pump(self._logger)
        p.off()
        msg = "Pump is turned off"
        self._logger.debug(msg)
        return "Off"

    def checkLight(self):
        """Check if lights should be on or off
        Args:
            test: flag for testing system
        Returns:
            None
        Raises:
            None
        """
        # Move import here so can trap if fails
        # couchdb library does not seem to load when initially starting
        self._logger.debug("In checkLight")
        import Light
        # Get times from env and split into components
        s = env['lights']['On']
        e = env['lights']['Off']
        state = self.determineState(s, e)
        l = Light.Light(self._logger)
        if state:
            l.set_on(test)
            return "On"
        else:
            l.set_off(test)
            return "Off"

    def determineState(self, start, end):
        ''' Determine if lights should be on or off'''
        s = start.split(':')
        e = end.split(':')
        # Munge date into times
        t = datetime.now()
        st = t.replace(hour=int(s[0]), minute=int(s[1]), second=int(s[2]))
        et = t.replace(hour=int(e[0]), minute=int(e[1]), second=int(e[2]))

        if st > et:
            # Night Light - roll to next day when lights go off
            et += timedelta(days=1)

        msg = "{} {} {} {}".format("Start Time: ", st, "End Time: ", et)
        self._logger.debug(msg)

        if (st < datetime.now()) and (et > datetime.now()):
            msg = "Lights should be On"
            self._logger.debug(msg)
            return True
        else:
            msg = "Lights should be Off"
            self._logger.debug(msg)
            return False
Exemple #4
0
class SI7021(object):
    def __init__(self, logger=None):

        self._addr = addr
        self._path = path
        self._logger = logger
        if logger == None:
            self._logger = Logger("SI7021", Logger.INFO,
                                  "/home/pi/MVP/logs/obsv.log")
        self._i2c = I2C(path, addr, self._logger)

    def calc_humidity(self, read):
        """Calculate relative humidity from sensor reading
           Args:
               read: the sensor value
           Returns:
               rh: calculated relative humidity
           Raises:
               None
      """
        rh = ((125.0 * read) / 65536.0) - 6.0
        return rh

    def calc_temp(self, read):
        """Calculate relative humidity from sensor reading
           Args:
               read: the sensor value
           Returns:
               tempC: calculated temperature in Centigrade
           Raises:
               None
      """
        tempC = ((175.72 * read) / 65536.0) - 46.85
        return tempC

    def get_tempC_prior(self):
        """Get the temperature from the prior humidity reading
           Args:
               None
           Returns:
               tempC: calculated temperature in Centigrade
           Raises:
               None
       """

        msgs = self._i2c.get_data([previous_temp], 0.03, 3)
        if msgs == None:
            return None
        else:
            value = self._i2c.bytesToWord(msgs[0].data[0], msgs[0].data[1])
            tempC = self.calc_temp(value)
            return tempC

    def get_humidity(self):
        """Get the humidity
           Args:
               None
           Returns:
               rh: calculated relative humidity
           Raises:
                None
       """
        msgs = self._i2c.get_data([rh_no_hold], 0.03, 2)
        value = self._i2c.bytesToWord(msgs[0].data[0], msgs[0].data[1])
        if value == None:
            return None
        else:
            rh = self.calc_humidity(value)
            return rh

    def get_tempC(self):
        """Get the temperature (new reading)
           Args:
               None
           Returns:
               tempC: calculated temperature in Centigrade
           Raises:
               None
       """
        msgs = self._i2c.get_data([temp_no_hold], 0.03, 3)
        value = self._i2c.bytesToWord(msgs[0].data[0], msgs[0].data[1])
        if value == None:
            return None
        else:
            return self.calc_temp(value)

    def get_rev(self):
        """Get the firmware revision number
           Args:
               None
           Returns:
               rev: coded revision number
           Raises:
               None
       """
        self._logger.info("\nGet Revision")
        #       msgs = self._i2c.get_msg([firm_rev_1_1, firm_rev_1_2], 3)
        msgs = self._i2c.get_data([firm_rev_1_1, firm_rev_1_2], 0.03, 3)
        # Need to test, may error out on some conditions
        if not ((msgs is None) or (msgs[0].data is None)):
            rev = msgs[0].data[0]
            if rev == 0xFF:
                self._logger.info("version 1.0")
            elif rev == 0x20:
                self._logger.info("version 2.0")
            else:
                self._logger.error("Unknown")
        else:
            self._logger.error("No Revision Data Available")
            return rev

    def get_id1(self):
        """Print the first part of the chips unique id
           Args:
               None
           Returns:
               None
           Raises:
                None
       """
        self._logger.info("\nGet ID 1")
        try:
            msgs = self._i2c.get_data([read_id_1_1, read_id_1_2], 0.05, 4)
            ret = msgs[0].data
            for data in ret:
                self._logger.info("ID: " + str(hex(data)))
        except Exception as e:
            self._logger.error("Error getting msgs " + str(e))

    def get_id2(self):
        """Print the second part of the chips unique id
           The device version is in SNA_3
           Args:
               None
           Returns:
               None
           Raises:
               None
       """

        self._logger.info("\nGet ID 2")
        msgs = self._i2c.get_data([read_id_2_1, read_id_2_2], 0.05, 4)
        ret = msgs[0].data
        for data in ret:
            self._logger.info("ID" + str(hex(data)))
        sna3 = msgs[0].data[0]
        if sna3 == 0x00:
            self._logger.info("Device: Engineering Sample")
        elif sna3 == 0xFF:
            self._logger.info("Device: Engineering Sample")
        elif sna3 == 0x14:
            self._logger.info("Device: SI7020")
        elif sna3 == 0x15:
            self._logger.info("Device: SI7021")
        else:
            self._logger.error("Unknown")

    def reset(self):
        """Reset the device
           Args:
               None
           Returns:
               None
           Raises:
               None
       """

        self._logger.info("\nReset")
        rev_1 = self._i2c.msg_write([reset_cmd])
        self._logger.info("Reset: " + str(rev_1))
Exemple #5
0
class Reservoir:

    FULL = 0
    EMPTY = 1
    OK = 2

    vol_per_mm = 0.3785  # estimate 1 gal per 10 mm, this is reservoir specific
    vol_per_sec = 0.3785  # estimate 100 ml per sec, this is reservoir specific

    def __init__(self):
        '''Get distances for determining reservoir levels'''
        self.res = {'full': full_ec, 'empty': empty_ec, 'timeout': timeout}
        self._activity_type = 'Agronomic_Activity'
        self._logger = Logger('LogReservoir', Logger.INFO)
        self._persist = Persistence(self._logger)
        self._logger.detail("Reservoir Initialized")
        # flag for testing
        self._test = False

    def getStatus(self):
        "Logic for calling the reservoir full"
        self._logger.detail("In getStatus")
        ec = self.getEC()
        if ec <= full_ec:
            self._logger.debug("{}, {}, {:10.1f}".format(
                "Reservoir Full", EC, ec))
            return Reservoir.FULL, ec
        elif ec >= empty_ec:
            self._logger.debug("{}, {}, {:10.1f}".format(
                "Reservoir Empty", EC, ec))
            return Reservoir.EMPTY, ec
        else:
            self._logger.debug("{}, {}, {:10.1f}".format(
                "Reservoir not Empty", EC, ec))
            return Reservoir.OK, ec

    def isFull(self):
        self._logger.detail("In isFull")
        status, ec = self.getStatus()
        if status == Reservoir.FULL:
            return True
        else:
            return False

    def getEC(self):
        '''Get EC reading'''
        self._logger.detail("In getEC")
        snsr = EC(self._logger)
        return snsr.getEC()

    def fill(self, test=False):
        ''' Routine to control re-filling of the reservoir'''
        self._logger.detail("{}".format("In Fill"))
        start_ec = self.getEC()
        start_t = time.time()
        pump = Pump()
        pump.on()
        # Loop till filled or times out
        while (not self.isFull()) and self.isFilling(start_t):
            self._logger.detail("{}".format("In Fill Loop"))
        self._logger.detail("{}".format("Exit Fill Loop, close solenoid"))
        # Close valve
        pump.off()
        # Calculate amount filled
        stop_t = time.time()
        stop_ec = self.getEC()
        dif_t = stop_t - start_t
        volume = dif_t * self.vol_per_sec
        self._logger.detail("{}".format("Exit Fill"))

        return volume

    def isFilling(self, start_time, test=False):
        '''Check that actually filling: the distance is actually changing'''
        start_ec = self.getEC()
        self._logger.detail("{} {}".format("Filling, Start EC:", start_ec))
        time.sleep(fill_time)

        # Check for level change first, else will never get to this logic till timeout
        end_ec = self.getEC()
        change = start_ec - end_ec
        if end_ec < start_ec:  # need to see at least a 5mm change
            self._logger.detail("{} {} {} {} {} {}".format(
                "Still Filling, change:", change, "Start", start_ec, "End",
                end_ec))
            return True
        else:
            self._logger.detail("{} {} {} {} {} {}".format(
                "Not Filling, no change:", change, "Start", start_ec, "End",
                end_ec))
            return False


# Check for timeout
        stop_time = time.time()
        if stop_time - start_time > self.res['timeout']:
            self._logger.detail("{}".format("Timeout"))
            return False
        else:
            return True

    def checkReservoir(self):
        '''Check condition of reservoir and fill if necessary'''
        self._logger.detail("{}".format("Check Reservoir"))
        status, ec = self.getStatus()
        self._logger.debug("{} {} {} {}".format("Status:", status, "EC", ec))

        # Is full, log state
        if status == Reservoir.FULL:
            self._logger.detail("{}".format("Status: Full"))
            self._logger.info("{} {} {} {}".format("EC:", ec, "Full level:",
                                                   self.res['full'], "Empty:",
                                                   self.res['empty']))
            return True
        else:
            # Needs filling
            self._logger.debug("{}".format("Status: Filling"))
            volume = self.fill()
            if volume > 0:
                # Filled, log volume
                self.logState(volume, 'Success')
                return True
            else:
                # Failure
                self._logger.debug("{}".format("Status: Failure"))
                level = 'Empty'
                if status == '2':
                    level = 'Ok'
                self._logger.error("{}".format("Failure to fill Reservoir"))
                self._logger.error("{} {} {} {}".format(
                    "EC:", ec, "Full level:", self.res['full'], "Empty:",
                    self.res['empty']))
                self.logState(volume, 'Failure')
                return False

    def logState(self, value, status_qualifier):

        if self._test:
            status_qualifier = 'Test'
        txt = {
            'Volume': value,
            'full_level': self.res['full'],
            'empty_level': self.res['empty'],
            'status': 'Full'
        }
        self._persist.save([
            'State_Change', '', 'Nutrient', 'Reservoir', 'Volume', value,
            'Liter', 'Solenoid', status_qualifier, ''
        ])
        self._logger.info(txt)
Exemple #6
0
class LogSensorsExtra(object):
    def __init__(self, lvl=Logger.INFO):
        """Record optional sensor data
        Args:
            lvl: Logging level
        Returns:
            None
        Raises:
            None
        """
        self._logger = Logger("LogSensor-Extra",
                              lvl,
                              file="/home/pi/MVP/logs/obsv.log")
        self._activity_type = "Environment_Observation"
        self._test = False
        self._persist = Persistence(self._logger)

    def getOneWire(self, test=False):
        """Loop OneWire temperature sensors
            Assumes there are four
        Args:
            test: flag for testing
        Returns:
            None
        Raises:
            None
        """
        self._logger.debug("In getOneWire")
        from OneWireTemp import OneWireTemp
        for sensor in OneWireTemp.one_temp:
            self.logOneWire(sensor, OneWireTemp.one_temp[sensor])

    def logOneWire(self, sensor, name, test=False):
        """Record OneWire temperature sensor
        Args:
            sensor: number of the sensor
            name: name of the sensor
            test: flag for testing
        Returns:
            None
        Raises:
            None
        """
        self._logger.debug("In logOneWire")
        from OneWireTemp import OneWireTemp
        try:
            ow = OneWireTemp()
            temp = ow.getTempC(sensor)

            status_qualifier = 'Success'
            if self._test:
                status_qualifier = 'Test'
            rec = [
                self._activity_type, '', name, 'Air', 'Temperature',
                "{:10.1f}".format(temp), 'Centigrade',
                'DS18B20-' + str(sensor), status_qualifier, ''
            ]
            self._persist.save(rec)
            self._logger.info("{}, {}, {:10.1f}".format(
                name, status_qualifier, temp))
        except Exception as e:
            status_qualifier = 'Failure'
            if test:
                status_qualifier = 'Test'
            rec = [
                self._activity_type, '', name, 'Air', 'Temperature', '',
                'Centigrade', 'DS18B20-' + str(sensor), status_qualifier,
                str(e)
            ]
            self._persist.save(rec)
            self._logger.error("{}, {}, {}".format(name, status_qualifier, e))

    def getLux(self, test=False):
        """Record LUX sensor (TSL2561)
        Args:
            test: flag for testing
        Returns:
            None
        Raises:
            None
        """
        from TSL2561 import TSL2561
        lx = TSL2561()
        self._logger.info("TSL2561 - LUX")

        try:
            lux = lx.getLux()
            status_qualifier = 'Success'
            if test:
                status_qualifier = 'Test'
            rec = [
                self._activity_type, '', 'Canopy', 'Light', 'LUX',
                "{:3.1f}".format(lux), 'lux', 'TSL2561', status_qualifier, ''
            ]
            self._persist.save(rec)
            self._logger.info("{}, {}, {:10.1f}".format(
                "LUX", status_qualifier, lux))
        except Exception as e:
            status_qualifier = 'Failure'
            if test:
                status_qualifier = 'Test'
            rec = [
                self._activity_type, '', 'Canopy', 'Light', 'LUX', '', 'lux',
                'TSL2561', status_qualifier,
                str(e)
            ]
            self._persist.save(rec)
            self._logger.error("{}, {}, {}".format("LUX", status_qualifier, e))

    def getEC(self, test=False):
        """Record EC sensor (EC - ADC reading)
        Args:
            test: flag for testing
        Returns:
            None
        Raises:
            None
        """

        from EC import EC
        self._logger.info("EC")

        try:
            s = EC()
            ec = s.getEC()

            status_qualifier = 'Success'
            if test:
                status_qualifier = 'Test'
                print("{}, {}, {:10.1f}".format("EC", status_qualifier, ec))
            rec = [
                self._activity_type, '', 'Reservoir', 'Nutrient', 'EC',
                "{:3.1f}".format(ec), 'EC', 'EC', status_qualifier, ''
            ]
            self._persist.save(rec)
            self._logger.info("{}, {}, {:10.1f}".format(
                "EC", status_qualifier, ec))
        except Exception as e:
            status_qualifier = 'Failure'
            if test:
                status_qualifier = 'Test'
                print("{}, {}, {:10.1f}".format("EC", status_qualifier, ec))
            rec = [
                self._activity_type, '', 'Reservoir', 'Nutrient', 'EC', '',
                'EC', 'EC', status_qualifier,
                str(e)
            ]
            self._persist.save(rec)
            self._logger.error("{}, {}, {}".format("EC CCS811",
                                                   status_qualifier, e))

    def getCO2_NDIR(self, test=False):
        """Record CO2 sensor (NDIR)
        Args:
            test: flag for testing
        Returns:
            None
        Raises:
            None
        """
        from NDIR import Sensor
        self._logger.info("CO2 - NDIR")
        try:
            sensor = Sensor()
            sensor.begin()
            co2 = sensor.getCO2()

            status_qualifier = 'Success'
            if test:
                status_qualifier = 'Test'
                print("{}, {}, {:10.1f}".format("CO2 Canopy", status_qualifier,
                                                co2))
            rec = [
                self._activity_type, '', 'Canopy', 'Air', 'CO2',
                "{:3.1f}".format(co2), 'ppm', 'MH-Z16-NDIR', status_qualifier,
                ''
            ]
            self._persist.save(rec)
            self._logger.debug("{}, {}, {:10.1f}".format(
                "CO2", status_qualifier, co2))
        except Exception as e:
            status_qualifier = 'Failure'
            if test:
                status_qualifier = 'Test'
                print("{}, {}, {:10.1f}".format("CO2 Canopy", status_qualifier,
                                                co2))
            rec = [
                self._activity_type, '', 'Canopy', 'Air', 'CO2', '', 'ppm',
                'MH-Z16-NDIR', status_qualifier,
                str(e)
            ]
            self._persist.save(rec)
            self._logger.error("{}, {}, {}".format("CO2 NDIR",
                                                   status_qualifier, e))

    def getCO2_CCS811(self, test=False):
        """Record CO2 sensor (CCS811)
        Args:
            test: flag for testing
        Returns:
            None
        Raises:
            None
        """
        from CCS811 import CCS811
        self._logger.info("CO2 CCS811")
        try:
            sensor = CCS811(SLAVE)
            co2 = sensor.get_co2()

            status_qualifier = 'Success'
            if test:
                status_qualifier = 'Test'
                print("{}, {}, {:10.1f}".format("CO2 Canopy", status_qualifier,
                                                co2))
            rec = [
                self._activity_type, '', 'Canopy', 'Air', 'CO2',
                "{:3.1f}".format(co2), 'ppm', 'CCS811',
                str(e)
            ]
            self._persist.save(rec)
            self._logger.debug("{}, {}, {:10.1f}".format(
                "CCS811 - CO2", status_qualifier, co2))
        except Exception as e:
            status_qualifier = 'Failure'
            if test:
                status_qualifier = 'Test'
                print("{}, {}, {:10.1f}".format("CO2 Canopy", status_qualifier,
                                                co2))
            rec = [
                self._activity_type, '', 'Canopy', 'Air', 'CO2', '', 'ppm',
                'CCS811', status_qualifier,
                str(e)
            ]
            self._persist.save(rec)
            self._logger.error("{}, {}, {}".format("CO2 CCS811",
                                                   status_qualifier, e))

    def getSCD(self):
        """Record CO2 sensor (scd30)
            Generates co2, temperature and relative humidity
        Args:
            None
        Returns:
            None
        Raises:
            None
        """

        from scd30 import SCD30
        self._scd = SCD30(self._logger)
        self._logger.debug("In SCD30")
        try:
            co2, temp, rh = self._scd.get_data()

            status = 'Success'
            if self._test:
                status = 'Test'
            c_rec = [
                'Environment_Observation', '', 'Top', 'Air', 'CO2',
                "{:10.1f}".format(co2), 'ppm', 'scd30', status, ''
            ]
            t_rec = [
                'Environment_Observation', '', 'Top', 'Air', 'Temperature',
                "{:10.1f}".format(temp), 'Centigrade', 'scd30', status, ''
            ]
            h_rec = [
                'Environment_Observation', '', 'Top', 'Air', 'Humidity',
                "{:10.1f}".format(rh), 'Percent', 'scd30', status, ''
            ]
            self._persist.save(c_rec)
            self._persist.save(t_rec)
            self._persist.save(h_rec)
            self._logger.info("{} {:6.1f}, {} {:3.1f}, {} {:3.1f}".format(
                "EnvObsv-CO2:", co2, "Temp", temp, "Humidity:", rh))
        except Exception as e:
            status = 'Failure'
            if self._test:
                status = 'Test'
            c_rec = [
                'Environment_Observation', '', 'Top', 'Air', 'CO2', '', 'ppm',
                'scd30', status,
                str(e)
            ]
            t_rec = [
                'Environment_Observation', '', 'Top', 'Air', 'Temperature', '',
                'Centigrde', 'scd30', status, ''
            ]
            h_rec = [
                'Environment_Observation', '', 'Top', 'Air', 'Humidity', '',
                'Percent', 'scd30', status, ''
            ]
            self._persist.save(c_rec)
            self._persist.save(t_rec)
            self._persist.save(h_rec)
            self._logger.debug("{} {}".format("EnvObsv-SCD30 Error:", e))

    def log(self):
        '''Log extra sensors
            Uncomment desired sensors
            Imports are in the function to avoid loading unnecessary code
        '''

        #self.getOneWire()

        self.getLux()

        self.getEC()

        self.getCO2_NDIR()

        #self.getCO2_CCS811()

        self.getSCD()
Exemple #7
0
class CouchUtil(object):
    def __init__(self, logger=None):
        if logger == None:
            self._logger = Logger("LogSensor", Logger.DETAIL)
        else:
            self._logger = logger
        self._activity_type = "Environment_Observation"
        self._logger.detail("CouchUtil")
        self._test = False
        self._server = Server()
        self._db = self._server[db_name]

    def processEnv(self, row):
        '''
        Environment specific processing
        Args:
            doc: list of attributes, should be of format:
                 [timestamp, field, activity_name, trial, plot, subject, attribute, value, units, participant, status_qualifier, comment]
                 participant may be a device string, or a list: ['person':'hmw']
        Returns:
            rec: json formatted record ready for the database
        Throws:
            None

        '''

        rec = self.buildCore(row)
        rec['activity_type'] = row[ACTIVITY]
        rec['subject'] = {
            'name': row[SUBJECT],
            'attribute': {
                'name': row[ATTRIBUTE],
                'units': row[UNITS],
                'value': row[VALUE]
            },
            'location': row[PLOT]
        }
        rec['location'] = {'field': row[FIELD]}
        return rec

    def processState(self, row):
        '''
        State specific processing
        Args:
            doc: list of attributes, should be of format:
                 [timestamp, field, activity_name, trial, plot, subject, attribute, value, units, participant, status_qualifier, comment]
                 participant may be a device string, or a list: ['person':'hmw']
        Returns:
            rec: json formatted record ready for the database
        Throws:
            None

        '''
        self._logger.detail("In State_Change")
        rec = self.buildCore(row)
        rec['activity_type'] = row[ACTIVITY]
        self._logger.detail("Activity: " + str(rec['activity_type']))
        rec['subject'] = {
            'name': row[SUBJECT],
            'attribute': {
                'name': row[ATTRIBUTE],
                'units': row[UNITS],
                'value': row[VALUE]
            },
            'location': row[PLOT]
        }
        self._logger.detail("Subject: " + str(rec['subject']))
        rec['participant'] = {'type': 'device', 'name': row[PARTICIPANT]}
        rec['location'] = {'field': row[FIELD]}
        self._logger.detail(str(rec))
        return rec

    def processAgro(self, row):
        '''
        Agronomic specific processing
        Args:
            doc: list of attributes, should be of format:
                 [timestamp, field, activity_name, trial, plot, subject, attribute, value, units, participant, status_qualifier, comment]
                 participant may be a device string, or a list: ['person':'hmw']
        Returns:
            rec: json formatted record ready for the database
        Throws:
            None

        '''
        self._logger.detail("In Process Agro")
        rec = self.buildCore(row)
        rec['activity_type'] = row[ACTIVITY]
        rec['sub-activity'] = row[PLOT]
        if len(row[SUBJECT]) > 0:
            rec['subject'] = {
                'name': row[SUBJECT],
                'attribute': {
                    'name': row[ATTRIBUTE],
                    'units': row[UNITS],
                    'value': row[VALUE]
                }
            }
        rec['location'] = {'field': row[FIELD], 'trial': row[TRIAL]}
        return rec

    def processPheno(self, row):
        '''
        Phenotype specific processing
        Args:
            doc: list of attributes, should be of format:
                 [timestamp, field, activity_name, trial, plot, subject, attribute, value, units, participant, status_qualifier, comment]
                 participant may be a device string, or a list: ['person':'hmw']
        Returns:
            rec: json formatted record ready for the database
        Throws:
            None

        '''
        self._logger.detail("In Process Pheno")
        rec = self.buildCore(row)
        rec['activity_type'] = row[ACTIVITY]
        rec['subject'] = {
            'name': row[SUBJECT],
            'attribute': {
                'name': row[ATTRIBUTE],
                'units': row[UNITS],
                'value': row[VALUE]
            }
        }
        rec['location'] = {
            'field': row[FIELD],
            'trial': row[TRIAL],
            'plot': row[PLOT]
        }
        return rec

    def buildCore(self, row):
        '''
        Build the core of the json structure, common elements
        Args:
            row: list of activity 
                 [timestamp, field, activity_name, trial, plot, subject, attribute, value, units, participant, status_qualifier, comment]
                 participant may be a device string, or a list: ['person':'hmw']
        Returns:
            rec: json formatted record ready for the database
        Throws:
            None

        '''
        self._logger.detail("In buildCore")
        rec = {}
        rec['start_date'] = {'timestamp': row[TS]}
        if isinstance(row[PARTICIPANT], list):
            rec['participant'] = {
                'type': row[PARTICIPANT][0],
                'name': row[PARTICIPANT][1]
            }
        else:
            rec['participant'] = {'type': 'device', 'name': row[PARTICIPANT]}
        if len(row[COMMENT]) == 0:
            rec['status'] = {
                'status': 'Complete',
                'status_qualifier': row[STATUS]
            }
        else:
            rec['status'] = {
                'status': 'Complete',
                'status_qualifier': row[STATUS],
                'comment': row[COMMENT]
            }
        if row[STATUS] == "Success":
            self._logger.detail(rec)
        elif row[STATUS] == "Failure":
            self._logger.error("Failure" + str(rec))
        elif row[STATUS] == "Test":
            self._logger.detail(rec)
        else:
            self._logger.error("Unknown Status" + str(rec))

        return rec

    def save(self, doc, test=False):
        '''
        Convert activity list to json structure and save to database
        This is the entry point for all other functions
        Args:
            doc: list of attributes, should be of format:
                 [activity_name, trial, plot, subject, attribute, value, units, participant, status_qualifier, comment]
                 participant may be a device string, or a list: ['person':'hmw']
        Returns:
            rec: json formatted record ready for the database
        Throws:
            None

        '''
        self._logger.detail("In saveList")
        # dictionary of activity types and specific processing functions
        proc = {
            'Environment_Observation': self.processEnv,
            'State_Change': self.processState,
            'Agronomic_Activity': self.processAgro,
            'Phenotype_Observation': self.processPheno
        }
        # add timestamp and field_id
        timestamp = datetime.utcnow().isoformat()[:19]
        doc.insert(0, env['field']['field_id'])
        doc.insert(0, timestamp)
        # Use activity type to route processing
        rec = proc[doc[2]](doc)
        self.saveRec(rec, test)

    def saveRec(self, rec, test=False):
        '''
        Persist json structure to a database
        Args:
            rec: json structure
        Returns:
            id: document id
            rev: revision number of document
        Throws:
            None

        '''

        #    print rec
        id, rev = self._db.save(rec)
Exemple #8
0
class I2C(object):
    def __init__(self, path, addr, logger=None):
        self._path = path
        self._addr = addr
        self._i2c = pI2C(self._path)
        self._logger = logger
        if logger == None:
            self._logger = Logger("SCD30", Logger.INFO)
        self._logger.debug("initialize I2C object")

    def __exit__(self, exc_type, exc_value, traceback):
        self._i2c.close()

    def msg_write(self, cmds):
        """Write to sensor
           Args:
               cmds: commands to send
           Returns:
               msgs: basically returns the cmds, since nothing altered
           Raises:
               None
      """
        self._logger.debug("In Msg Write")
        for cmd in cmds:
            self._logger.detail("{}, {}".format("Cmd: ", cmd))

        msgs = [self._i2c.Message(cmds)]
        try:
            self._logger.detail("Transfer")
            self._i2c.transfer(self._addr, msgs)
            msb = msgs[0].data[0]
            self._logger.detail("{}, {}".format("MSB: ", hex(msb)))
            return msgs

        except Exception as e:
            self._logger.error(str(e))
            return None

    def msg_read(self, size, cmds=None):
        """Read existing data
           Args:
               cmds: addresses to read from - optional for some sensors (SI7021)
               size: size of byte array for receiving data
           Returns:
               msgs: message package, last one should hold data
           Raises:
               None
      """

        self._logger.detail("{}, {}, {}, {}".format("Msg Read - size: ", size,
                                                    " cmds: ", cmds))
        sz = self._i2c.Message(bytearray([0x00 for x in range(size)]),
                               read=True)
        msgs = [sz]
        #       print("C " + str(type(cmds)))
        if cmds is not None:
            #           print("Cmds " + str(cmds))
            rd = self._i2c.Message(cmds)
            msgs = [rd, sz]
        try:
            self._i2c.transfer(self._addr, msgs)
            return msgs

        except Exception as e:
            self._logger.error(str(e))
            return None

    def get_data(self, cmd, sleep, size, read=None):
        '''Combine sending of command and reading
        Some sensors default the read to the prior command and don't specify a read address
       '''
        self._logger.debug("{}, {}, {}, {}, {}, {}".format(
            "In Get Data-cmd: ", cmd, " sleep: ", sleep, " size: ", size))
        self.msg_write(cmd)
        time.sleep(sleep)
        msgs = self.msg_read(size, read)
        if msgs == None:
            return None
        else:
            for msg in msgs:
                self._logger.detail("-")
                for dt in msg.data:
                    self._logger.detail("Dt " + str(dt))
            self._logger.debug("Data " + str(msgs[0].data[0]) + " " +
                               str(msgs[0].data[1]))
            value = self.bytesToWord(msgs[0].data[0], msgs[0].data[1])
            return msgs

    def bytesToWord(self, high, low):
        """Convert two byte buffers into a single word value
           shift the first byte into the work high position
           then add the low byte
            Args:
                high: byte to move to high position of word
                low: byte to place in low position of word
            Returns:
                word: the final value
            Raises:
                None
       """
        self._logger.debug("{}, {}, {}, {}".format("In Bytes To Word-high: ",
                                                   high, " Low: ", low))
        word = (high << 8) + low
        return word