# Database Datastore Example

    import sqlalchemy
    import sqlalchemy.types as sqltypes
    from sqlalchemy.sql import and_
    from sqlalchemy.schema import UniqueConstraint
    from sqlalchemy.sql.expression import bindparam

    from pymodbus.exceptions import NotImplementedException
    from pymodbus.interfaces import IModbusSlaveContext

    #---------------------------------------------------------------------------#
    # Logging
    #---------------------------------------------------------------------------#
    import logging;
    _logger = logging.getLogger(__name__)

    #---------------------------------------------------------------------------#
    # Context
    #---------------------------------------------------------------------------#
    class DatabaseSlaveContext(IModbusSlaveContext):
        '''
        This creates a modbus data model with each data access
        stored in its own personal block
        '''

        def __init__(self, *args, **kwargs):
            ''' Initializes the datastores

            :param kwargs: Each element is a ModbusDataBlock
            '''
            self.table = kwargs.get('table', 'pymodbus')
            self.database = kwargs.get('database', 'sqlite:///pymodbus.db')
            self.__db_create(self.table, self.database)

        def __str__(self):
            ''' Returns a string representation of the context

            :returns: A string representation of the context
            '''
            return "Modbus Slave Context"

        def reset(self):
            ''' Resets all the datastores to their default values '''
            self._metadata.drop_all()
            self.__db_create(self.table, self.database)
            raise NotImplementedException()  # TODO drop table?

        def validate(self, fx, address, count=1):
            ''' Validates the request to make sure it is in range

            :param fx: The function we are working with
            :param address: The starting address
            :param count: The number of values to test
            :returns: True if the request in within range, False otherwise
            '''
            address = address + 1  # section 4.4 of specification
            _logger.debug("validate[%d] %d:%d" % (fx, address, count))
            return self.__validate(self.decode(fx), address, count)

        def getValues(self, fx, address, count=1):
            ''' Validates the request to make sure it is in range

            :param fx: The function we are working with
            :param address: The starting address
            :param count: The number of values to retrieve
            :returns: The requested values from a:a+c
            '''
            address = address + 1  # section 4.4 of specification
            _logger.debug("get-values[%d] %d:%d" % (fx, address, count))
            return self.__get(self.decode(fx), address, count)

        def setValues(self, fx, address, values):
            ''' Sets the datastore with the supplied values

            :param fx: The function we are working with
            :param address: The starting address
            :param values: The new values to be set
            '''
            address = address + 1  # section 4.4 of specification
            _logger.debug("set-values[%d] %d:%d" % (fx, address, len(values)))
            self.__set(self.decode(fx), address, values)

        #--------------------------------------------------------------------------#
        # Sqlite Helper Methods
        #--------------------------------------------------------------------------#
        def __db_create(self, table, database):
            ''' A helper method to initialize the database and handles

            :param table: The table name to create
            :param database: The database uri to use
            '''
            self._engine = sqlalchemy.create_engine(database, echo=False)
            self._metadata = sqlalchemy.MetaData(self._engine)
            self._table = sqlalchemy.Table(table, self._metadata,
                sqlalchemy.Column('type', sqltypes.String(1)),
                sqlalchemy.Column('index', sqltypes.Integer),
                sqlalchemy.Column('value', sqltypes.Integer),
                UniqueConstraint('type', 'index', name='key'))
            self._table.create(checkfirst=True)
            self._connection = self._engine.connect()

        def __get(self, type, offset, count):
            '''

            :param type: The key prefix to use
            :param offset: The address offset to start at
            :param count: The number of bits to read
            :returns: The resulting values
            '''
            query  = self._table.select(and_(
                self._table.c.type == type,
                self._table.c.index >= offset,
                self._table.c.index <= offset + count))
            query = query.order_by(self._table.c.index.asc())
            result = self._connection.execute(query).fetchall()
            return [row.value for row in result]

        def __build_set(self, type, offset, values, p=''):
            ''' A helper method to generate the sql update context

            :param type: The key prefix to use
            :param offset: The address offset to start at
            :param values: The values to set
            '''
            result = []
            for index, value in enumerate(values):
                result.append({
                    p + 'type'  : type,
                    p + 'index' : offset + index,
                        'value' : value
                })
            return result

        def __set(self, type, offset, values):
            '''

            :param key: The type prefix to use
            :param offset: The address offset to start at
            :param values: The values to set
            '''
            context = self.__build_set(type, offset, values)
            query   = self._table.insert()
            result  = self._connection.execute(query, context)
            return result.rowcount == len(values)

        def __update(self, type, offset, values):
            '''

            :param type: The type prefix to use
            :param offset: The address offset to start at
            :param values: The values to set
            '''
            context = self.__build_set(type, offset, values, p='x_')
            query   = self._table.update().values(name='value')
            query   = query.where(and_(
                self._table.c.type  == bindparam('x_type'),
                self._table.c.index == bindparam('x_index')))
            result  = self._connection.execute(query, context)
            return result.rowcount == len(values)

        def __validate(self, key, offset, count):
            '''
            :param key: The key prefix to use
            :param offset: The address offset to start at
            :param count: The number of bits to read
            :returns: The result of the validation
            '''
            query  = self._table.select(and_(
                self._table.c.type == type,
                self._table.c.index >= offset,
                self._table.c.index <= offset + count))
            result = self._connection.execute(query)
            return result.rowcount == count