Esempio n. 1
0
class ModbusUdpClientProtocol(protocol.DatagramProtocol, ModbusClientMixin):
    '''
    This represents the base modbus client protocol.  All the application
    layer code is deferred to a higher level wrapper.
    '''
    def __init__(self, framer=None, **kwargs):
        ''' Initializes the framer module

        :param framer: The framer to use for the protocol
        '''
        self.framer = framer or ModbusSocketFramer(ClientDecoder())
        if isinstance(self.framer, ModbusSocketFramer):
            self.transaction = DictTransactionManager(self, **kwargs)
        else:
            self.transaction = FifoTransactionManager(self, **kwargs)

    def datagramReceived(self, data, params):
        ''' Get response, check for valid message, decode result

        :param data: The data returned from the server
        :param params: The host parameters sending the datagram
        '''
        _logger.debug("Datagram from: %s:%d" % params)
        unit = self.framer.decode_data(data).get("uid", 0)
        self.framer.processIncomingPacket(data,
                                          self._handleResponse,
                                          unit=unit)

    def execute(self, request):
        ''' Starts the producer to send the next request to
        consumer.write(Frame(request))
        '''
        request.transaction_id = self.transaction.getNextTID()
        packet = self.framer.buildPacket(request)
        self.transport.write(packet)
        return self._buildResponse(request.transaction_id)

    def _handleResponse(self, reply):
        ''' Handle the processed response and link to correct deferred

        :param reply: The reply to process
        '''
        if reply is not None:
            tid = reply.transaction_id
            handler = self.transaction.getTransaction(tid)
            if handler:
                handler.callback(reply)
            else:
                _logger.debug("Unrequested message: " + str(reply))

    def _buildResponse(self, tid):
        ''' Helper method to return a deferred response
        for the current request.

        :param tid: The transaction identifier for this response
        :returns: A defer linked to the latest request
        '''
        d = defer.Deferred()
        self.transaction.addTransaction(d, tid)
        return d
Esempio n. 2
0
class ModbusUdpClientProtocol(protocol.DatagramProtocol, ModbusClientMixin):
    '''
    This represents the base modbus client protocol.  All the application
    layer code is deferred to a higher level wrapper.
    '''

    def __init__(self, framer=None, **kwargs):
        ''' Initializes the framer module

        :param framer: The framer to use for the protocol
        '''
        self.framer = framer or ModbusSocketFramer(ClientDecoder())
        if isinstance(self.framer, ModbusSocketFramer):
            self.transaction = DictTransactionManager(self, **kwargs)
        else: self.transaction = FifoTransactionManager(self, **kwargs)

    def datagramReceived(self, data, params):
        ''' Get response, check for valid message, decode result

        :param data: The data returned from the server
        :param params: The host parameters sending the datagram
        '''
        _logger.debug("Datagram from: %s:%d" % params)
        unit = self.framer.decode_data(data).get("uid", 0)
        self.framer.processIncomingPacket(data, self._handleResponse, unit=unit)

    def execute(self, request):
        ''' Starts the producer to send the next request to
        consumer.write(Frame(request))
        '''
        request.transaction_id = self.transaction.getNextTID()
        packet = self.framer.buildPacket(request)
        self.transport.write(packet)
        return self._buildResponse(request.transaction_id)

    def _handleResponse(self, reply):
        ''' Handle the processed response and link to correct deferred

        :param reply: The reply to process
        '''
        if reply is not None:
            tid = reply.transaction_id
            handler = self.transaction.getTransaction(tid)
            if handler:
                handler.callback(reply)
            else: _logger.debug("Unrequested message: " + str(reply))

    def _buildResponse(self, tid):
        ''' Helper method to return a deferred response
        for the current request.

        :param tid: The transaction identifier for this response
        :returns: A defer linked to the latest request
        '''
        d = defer.Deferred()
        self.transaction.addTransaction(d, tid)
        return d
Esempio n. 3
0
class ModbusClientProtocol():
    '''
    This represents the base modbus modbusclient_rs485 protocol.  All the application
    layer code is deferred to a higher level wrapper.
    '''
    def __init__(self, framer=None):
        ''' Initializes the framer module

        :param framer: The framer to use for the protocol
        '''
        self.connected = False
        self.framer = framer or ModbusSocketFramer(ClientDecoder())
        if isinstance(self.framer, ModbusSocketFramer):
            self.transaction = DictTransactionManager(self)
        else:
            self.transaction = FifoTransactionManager(self)

    def setTransport(self, stream):

        # clear frame buffer
        self.framer.advanceFrame()
        # clear all transaction with exception
        for tid in self.transaction:
            future = self.transaction.getTransaction(tid)
            future.set_exception(ConnectionException("Slave closed"))
        self.transport = stream
        self.connected = stream != None

    def dataReceived(self, data):
        ''' Get response, check for valid message, decode result
            To be used as streaming_callback to IOStream.read_until_close 

        :param data: The data returned from the server
        '''
        self.framer.processIncomingPacket(data, self._handleResponse)

    def _handleResponse(self, reply):
        ''' Handle the processed response and link to correct deferred

        :param reply: The reply to process
        '''
        if reply is not None:
            tid = reply.transaction_id
            future = self.transaction.getTransaction(tid)
            if future:
                future.set_result(reply)
                #handler.callback(reply)
            else:
                logger.debug("Unrequested message: %s", str(reply))

    @gen.coroutine
    def execute(self, request):
        ''' Starts the producer to send the next request to
        consumer.write(Frame(request))
        '''
        request.transaction_id = self.transaction.getNextTID()
        packet = self.framer.buildPacket(request)
        if not self.transport:
            raise ConnectionException("Slave not connected")
        yield self.transport.write(packet)
        future = TracebackFuture()
        self.transaction.addTransaction(future, request.transaction_id)
        res = yield future
        raise gen.Return(res)

    @gen.coroutine
    def read_input_registers(self, address, count=1, **kwargs):
        request = ReadInputRegistersRequest(address, count, **kwargs)
        res = yield self.execute(request)
        raise gen.Return(res)

    @gen.coroutine
    def write_coil(self, address, value, **kwargs):
        request = WriteSingleCoilRequest(address, value, **kwargs)
        res = yield self.execute(request)
        raise gen.Return(res)

    @gen.coroutine
    def write_register(self, address, value, **kwargs):
        request = WriteSingleRegisterRequest(address, value, **kwargs)
        res = yield self.execute(request)
        raise gen.Return(res)
Esempio n. 4
0
class ModbusClientProtocol(protocol.Protocol, ModbusClientMixin):
    '''
    This represents the base modbus client protocol.  All the application
    layer code is deferred to a higher level wrapper.
    '''

    def __init__(self, framer=None):
        ''' Initializes the framer module

        :param framer: The framer to use for the protocol
        '''
        self._connected = False
        self.framer = framer or ModbusSocketFramer(ClientDecoder())
        if isinstance(framer, ModbusSocketFramer):
            self.transaction = DictTransactionManager(self)
        else: self.transaction = FifoTransactionManager(self)

    def connectionMade(self):
        ''' Called upon a successful client connection.
        '''
        _logger.debug("Client connected to modbus server")
        self._connected = True

    def connectionLost(self, reason):
        ''' Called upon a client disconnect

        :param reason: The reason for the disconnect
        '''
        _logger.debug("Client disconnected from modbus server: %s" % reason)
        self._connected = False
        for tid in self.transaction:
            self.transaction.getTransaction(tid).errback(Failure(
                ConnectionException('Connection lost during request')))

    def dataReceived(self, data):
        ''' Get response, check for valid message, decode result

        :param data: The data returned from the server
        '''
        self.framer.processIncomingPacket(data, self._handleResponse)

    def execute(self, request):
        ''' Starts the producer to send the next request to
        consumer.write(Frame(request))
        '''
        request.transaction_id = self.transaction.getNextTID()
        packet = self.framer.buildPacket(request)
        self.transport.write(packet)
        return self._buildResponse(request.transaction_id)

    def _handleResponse(self, reply):
        ''' Handle the processed response and link to correct deferred

        :param reply: The reply to process
        '''
        if reply is not None:
            tid = reply.transaction_id
            handler = self.transaction.getTransaction(tid)
            if handler:
                handler.callback(reply)
            else: _logger.debug("Unrequested message: " + str(reply))

    def _buildResponse(self, tid):
        ''' Helper method to return a deferred response
        for the current request.

        :param tid: The transaction identifier for this response
        :returns: A defer linked to the latest request
        '''
        if not self._connected:
            return defer.fail(Failure(
                ConnectionException('Client is not connected')))

        d = defer.Deferred()
        self.transaction.addTransaction(d, tid)
        return d
Esempio n. 5
0
class ModbusClientProtocol(protocol.Protocol, AsyncModbusClientMixin):
    """
    This represents the base modbus client protocol.  All the application
    layer code is deferred to a higher level wrapper.
    """
    framer = None

    def __init__(self, framer=None, **kwargs):
        self._connected = False
        self.framer = framer or ModbusSocketFramer(ClientDecoder())
        if isinstance(self.framer, type):
            # Framer class not instance
            self.framer = self.framer(ClientDecoder(), client=None)
        if isinstance(self.framer, ModbusSocketFramer):
            self.transaction = DictTransactionManager(self, **kwargs)
        else:
            self.transaction = FifoTransactionManager(self, **kwargs)

    def connectionMade(self):
        """ 
        Called upon a successful client connection.
        """
        _logger.debug("Client connected to modbus server")
        self._connected = True

    def connectionLost(self, reason=None):
        """ 
        Called upon a client disconnect

        :param reason: The reason for the disconnect
        """
        _logger.debug("Client disconnected from modbus server: %s" % reason)
        self._connected = False
        for tid in list(self.transaction):
            self.transaction.getTransaction(tid).errback(
                Failure(ConnectionException('Connection lost during request')))

    def dataReceived(self, data):
        """ 
        Get response, check for valid message, decode result

        :param data: The data returned from the server
        """
        unit = self.framer.decode_data(data).get("unit", 0)
        self.framer.processIncomingPacket(data,
                                          self._handleResponse,
                                          unit=unit)

    def execute(self, request):
        """ 
        Starts the producer to send the next request to
        consumer.write(Frame(request))
        """
        request.transaction_id = self.transaction.getNextTID()
        packet = self.framer.buildPacket(request)
        _logger.debug("send: " + " ".join([hex(byte2int(x)) for x in packet]))
        self.transport.write(packet)
        return self._buildResponse(request.transaction_id)

    def _handleResponse(self, reply, **kwargs):
        """ 
        Handle the processed response and link to correct deferred

        :param reply: The reply to process
        """
        if reply is not None:
            tid = reply.transaction_id
            handler = self.transaction.getTransaction(tid)
            if handler:
                handler.callback(reply)
            else:
                _logger.debug("Unrequested message: " + str(reply))

    def _buildResponse(self, tid):
        """ 
        Helper method to return a deferred response
        for the current request.

        :param tid: The transaction identifier for this response
        :returns: A defer linked to the latest request
        """
        if not self._connected:
            return defer.fail(
                Failure(ConnectionException('Client is not connected')))

        d = defer.Deferred()
        self.transaction.addTransaction(d, tid)
        return d

    def close(self):
        """
        Closes underlying transport layer ,essentially closing the client
        :return: 
        """
        if self.transport and hasattr(self.transport, "close"):
            self.transport.close()
        self._connected = False
Esempio n. 6
0
class ModbusClientProtocol(protocol.Protocol, ModbusClientMixin):
    '''
    This represents the base modbus client protocol.  All the application
    layer code is deferred to a higher level wrapper.
    '''
    def __init__(self, framer=None):
        ''' Initializes the framer module

        :param framer: The framer to use for the protocol
        '''
        self._connected = False
        self.framer = framer or ModbusSocketFramer(ClientDecoder())
        if isinstance(self.framer, ModbusSocketFramer):
            self.transaction = DictTransactionManager(self)
        else:
            self.transaction = FifoTransactionManager(self)

    def connectionMade(self):
        ''' Called upon a successful client connection.
        '''
        _logger.debug("Client connected to modbus server")
        self._connected = True

    def connectionLost(self, reason):
        ''' Called upon a client disconnect

        :param reason: The reason for the disconnect
        '''
        _logger.debug("Client disconnected from modbus server: %s" % reason)
        self._connected = False
        for tid in list(self.transaction):
            self.transaction.getTransaction(tid).errback(
                Failure(ConnectionException('Connection lost during request')))

    def dataReceived(self, data):
        ''' Get response, check for valid message, decode result

        :param data: The data returned from the server
        '''
        self.framer.processIncomingPacket(data, self._handleResponse)

    def execute(self, request):
        ''' Starts the producer to send the next request to
        consumer.write(Frame(request))
        '''
        request.transaction_id = self.transaction.getNextTID()
        packet = self.framer.buildPacket(request)
        self.transport.write(packet)
        return self._buildResponse(request.transaction_id)

    def _handleResponse(self, reply):
        ''' Handle the processed response and link to correct deferred

        :param reply: The reply to process
        '''
        if reply is not None:
            tid = reply.transaction_id
            handler = self.transaction.getTransaction(tid)
            if handler:
                handler.callback(reply)
            else:
                _logger.debug("Unrequested message: " + str(reply))

    def _buildResponse(self, tid):
        ''' Helper method to return a deferred response
        for the current request.

        :param tid: The transaction identifier for this response
        :returns: A defer linked to the latest request
        '''
        if not self._connected:
            return defer.fail(
                Failure(ConnectionException('Client is not connected')))

        d = defer.Deferred()
        self.transaction.addTransaction(d, tid)
        return d
Esempio n. 7
0
class UsrProtocol(Protocol):
    """
    有人云协议
    """
    def __init__(self):
        self._is_valid = False
        self._addr = None
        self._token = None
        self._connected = False
        self._timeout = 10
        self._functions = {
            1: ReadCoilsRequest,
            2: ReadDiscreteInputsRequest,
            3: ReadHoldingRegistersRequest,
            4: ReadInputRegistersRequest,
            5: WriteSingleCoilRequest,
            6: WriteSingleRegisterRequest,
            15: WriteMultipleCoilsRequest,
            16: WriteMultipleRegistersRequest
        }
        self._timeoutDeffer = defer.Deferred()
        self._requests = []
        self.framer = None
        self.transaction = None

    def dataReceived(self, data):
        log.debug("Received data: " +
                  " ".join([hex(byte2int(x)) for x in data]))
        self._timeoutDeffer.callback('dataReceived')
        if self._is_valid:
            # 验证成功
            unit = self.framer.decode_data(data).get("uid", 0)
            self.framer.processIncomingPacket(data,
                                              self._handleResponse,
                                              unit=unit)
        else:
            # 首次获取数据,验证注册码
            data_str = str(data, encoding='ascii')
            if data_str is not None and len(data_str) > 0:
                self._handlerToken(data_str)

    def connectionMade(self):
        # 初始化Framer
        self.framer = self.factory.framer
        # 初始化TransactionT
        if isinstance(self.framer, ModbusSocketFramer):
            self.transaction = DictTransactionManager(self)
        else:
            self.transaction = FifoTransactionManager(self)

        self._addr = '%s:%d' % (self.transport.getPeer().host,
                                self.transport.getPeer().port)
        self.factory.addClient(self._addr, self, None)
        log.debug("Client Connected [%s]" % self._addr)
        self._connected = True

    def connectionLost(self, reason=connectionDone):
        log.debug("Client Disconnected: %s" % reason)
        self.factory.delClient(self._addr, self._token)

    def _handlerToken(self, data):
        if self.factory.hasToken(data):
            self._is_valid = True
            self._token = data
            self.factory.updateClient(self._addr, self, data)
        else:
            log.error("This connections token[%s] is not valid." % data)
            self.transport.loseConnection()

    def _handlerModbus(self, data):
        if not self.factory.control.ListenOnly:
            units = self.factory.store.slaves()
            single = self.factory.store.single
            self.framer.processIncomingPacket(data,
                                              self._dataHandler,
                                              single=single,
                                              unit=units)

    def _dataHandler(self, request):
        device = self.factory.getDevice(self._addr, request.unit_id)
        if device is not None:
            log.debug("From modbus device %s, \n%s", device["deviceName"],
                      device)
            log.debug("With result %s", request.dncode())
        else:
            log.error("Modbus device is not exist")

    # Send to device
    def _function_to_device(self,
                            config,
                            unit_id,
                            callback=None,
                            errback=None):
        function_code = config.get('functionCode')
        request = None
        if function_code in (1, 2, 3, 4):
            registerCount = config.get("registerCount", 1)
            request = self._functions[function_code](unit_id, registerCount)
        elif function_code in (5, 6, 15, 16):
            payload = config["payload"]
            request = self._functions[function_code](unit_id, payload)

        if request is not None:
            log.debug("To modbus device %s, \n%s", config["deviceName"],
                      config)
            return self._execute(request, callback, errback)
        else:
            log.error("Unknown Modbus function with code: %i", function_code)
            return None

    def poll_period_to_device(self,
                              config,
                              unit_id,
                              poll_type,
                              callback=None,
                              errback=None):
        current_time = time.time()
        device = config['config']
        try:
            if device.get(type) is not None:
                if config["next_" + poll_type + "_check"] < current_time:
                    #  Reading data from device
                    for interested_data in range(len(device[poll_type])):
                        current_data = device[poll_type][interested_data]
                        current_data["deviceName"] = device.get('deviceName')

                        def __internalCallback(response):
                            device_responses = {
                                "timeseries": {},
                                "attributes": {},
                            }
                            if not isinstance(response,
                                              ReadRegistersResponseBase
                                              ) and response.isError():
                                log.exception(response)
                            device_responses[poll_type][device["tag"]] = {
                                "data_sent": current_data,
                                "input_data": response
                            }
                            if callback is not None:
                                callback(device_responses)

                        _ = self._function_to_device(
                            current_data,
                            unit_id,
                            callback=__internalCallback,
                            errback=errback)
        except Exception as e:
            log.exception(e)

    def server_side_rpc_handler(self,
                                config,
                                content,
                                callback=None,
                                errback=None):
        rpc_command_config = config["config"]["rpc"].get(
            content["data"].get("method"))
        if rpc_command_config.get('bit') is not None:
            rpc_command_config["functionCode"] = 6
            rpc_command_config["unitId"] = config["config"]["unitId"]

        if rpc_command_config is not None:
            rpc_command_config["payload"] = self._devices[
                content["device"]]["downlink_converter"].convert(
                    rpc_command_config, content)
            return self._function_to_device(rpc_command_config,
                                            rpc_command_config['unit_id'],
                                            callback=callback,
                                            errback=errback)
        else:
            log.error(
                "Received rpc request, but method %s not found in config for %s.",
                content["data"].get("method"), config["config"]['deviceName'])
            return None

    @threadsafe_function
    def _execute(self, request, config, callback, errback):
        """
        Starts the producer to send the next request to
        consumer.write(Frame(request))
        """
        request.transaction_id = self.transaction.getNextTID()
        d = self._buildResponse(request.transaction_id)
        if callback is not None:
            d.addCallback(callback)
        if errback is not None:
            d.addErrback(errback)

        if len(self._requests) > 0:
            # 加入队列
            self._requests.append({'request': request, 'config': config})
        else:
            # 立即请求
            self._send()
        return d

    def _buildResponse(self, tid):
        """
        Helper method to return a deferred response
        for the current request.

        :param tid: The transaction identifier for this response
        :returns: A defer linked to the latest request
        """
        if not self._connected:
            return defer.fail(
                Failure(ConnectionException('Client is not connected')))

        d = defer.Deferred()
        self.transaction.addTransaction(d, tid)
        return d

    def _handleResponse(self, reply, **kwargs):
        """
        Handle the processed response and link to correct deferred
        :param reply: The reply to process
        """
        if reply is not None:
            tid = reply.transaction_id
            handler = self.transaction.getTransaction(tid)
            if handler:
                handler.callback(reply)
            else:
                log.debug("Unrequested message: " + str(reply))

    def _timeoutHandler(self, res=None):
        if res is None:
            log.debug("Wait Response time out!")
        else:
            log.debug("Error: %s", res)

        if len(self._requests) > 0:
            request = self._requests.pop(0)
            handler = self.transaction.getTransaction(
                request['request'].transaction_id)
            handler.errback(request['config'])
        self._nextHandler('Next')

    def _cancelHandler(self, res):
        log.debug("timeoutDeffer Cancel")

    def _nextHandler(self, res):
        if len(self._requests) > 0:
            log.debug("Next Request")
            self._send()
        else:
            log.debug("Request Queue is empty! Waiting new request")

    def _send(self):
        request = self._requests[0]
        packet = self.framer.buildPacket(request['request'])
        log.debug("send: " + " ".join([hex(byte2int(x)) for x in packet]))
        self.transport.write(packet)
        self._createRequestDeffer()

    def _createRequestDeffer(self):
        self._timeoutDeffer = None
        self._timeoutDeffer = defer.Deferred(canceller=self._cancelHandler)
        self._timeoutDeffer.addErrback(self._timeoutHandler)
        self._timeoutDeffer.addCallback(self._nextHandler)
        self._timeoutDeffer.addTimeout(timeout=self._timeout)

    def close(self):
        """
        Closes underlying transport layer ,essentially closing the client
        :return:
        """
        if self.transport and hasattr(self.transport, "close"):
            self.transport.close()
        self._timeoutDeffer.cancel()
        self._connected = False
class ModbusClientProtocol():
    '''
    This represents the base modbus client protocol.  All the application
    layer code is deferred to a higher level wrapper.
    '''

    def __init__(self, framer=None):
        ''' Initializes the framer module

        :param framer: The framer to use for the protocol
        '''
        self.connected = False
        self.framer = framer or ModbusSocketFramer(ClientDecoder())
        if isinstance(self.framer, ModbusSocketFramer):
            self.transaction = DictTransactionManager(self)
        else: self.transaction = FifoTransactionManager(self)

    def setTransport(self, stream):

        # clear frame buffer
        self.framer.advanceFrame()
        # clear all transaction with exception
        for tid in self.transaction:
            future = self.transaction.getTransaction(tid)
            future.set_exception(ConnectionException("Slave closed"))
        self.transport = stream
        self.connected = stream != None


    def dataReceived(self, data):
        ''' Get response, check for valid message, decode result
            To be used as streaming_callback to IOStream.read_until_close 

        :param data: The data returned from the server
        '''
        self.framer.processIncomingPacket(data, self._handleResponse)

    def _handleResponse(self, reply):
        ''' Handle the processed response and link to correct deferred

        :param reply: The reply to process
        '''
        if reply is not None:
            tid = reply.transaction_id
            future = self.transaction.getTransaction(tid)
            if future:
                future.set_result(reply)
                #handler.callback(reply)
            else:
                logger.debug("Unrequested message: %s", str(reply))


    @gen.coroutine
    def execute(self, request):
        ''' Starts the producer to send the next request to
        consumer.write(Frame(request))
        '''
        request.transaction_id = self.transaction.getNextTID()
        packet = self.framer.buildPacket(request)
        if not self.transport:
            raise ConnectionException("Slave not connected")
        yield self.transport.write(packet)
        future = TracebackFuture()
        self.transaction.addTransaction(future, request.transaction_id)
        res = yield future
        raise gen.Return(res)


    @gen.coroutine
    def read_input_registers(self, address, count=1, **kwargs):
        request = ReadInputRegistersRequest(address, count, **kwargs)
        res = yield self.execute(request)
        raise gen.Return(res)
    
    @gen.coroutine
    def read_holding_registers(self, address, count=1, **kwargs):
        request = ReadHoldingRegistersRequest(address, count, **kwargs)
        res = yield self.execute(request)
        raise gen.Return(res)

    @gen.coroutine
    def write_coil(self, address, value, **kwargs):
        request = WriteSingleCoilRequest(address, value, **kwargs)
        res = yield self.execute(request)
        raise gen.Return(res)

    @gen.coroutine
    def read_coils(self, address, count=1, **kwargs):
        request = ReadCoilsRequest(address, count=1, **kwargs)
        res = yield self.execute(request)
        raise gen.Return(res)

    @gen.coroutine
    def write_register(self, address, value, **kwargs):
        request = WriteSingleRegisterRequest(address, value, **kwargs)
        res = yield self.execute(request)
        raise gen.Return(res)
Esempio n. 9
0
class AsyncModbusClientMixin(ModbusClientMixin):
    """Abstract asynchronous protocol running high level modbus logic on top
    of asynchronous loop.

    Behavior specific to an asynchronous framework like Twisted or asyncio is
    implemented in a derived class.
    """

    transport = None

    def __init__(self, framer=None, **kwargs):
        ''' Initializes the framer module

        :param framer: The framer to use for the protocol.
        '''
        self._connected = False
        self.framer = framer or ModbusSocketFramer(ClientDecoder())

        if isinstance(self.framer, ModbusSocketFramer):
            self.transaction = DictTransactionManager(self, **kwargs)
        else:
            self.transaction = FifoTransactionManager(self, **kwargs)

    def _connectionMade(self):
        ''' Called upon a successful client connection.
        '''
        _logger.debug("Client connected to modbus server")
        self._connected = True

    def _connectionLost(self, reason):
        ''' Called upon a client disconnect

        :param reason: The reason for the disconnect
        '''
        _logger.debug("Client disconnected from modbus server: %s" % reason)
        self._connected = False
        for tid in list(self.transaction):
            self.raise_future(self.transaction.getTransaction(tid), ConnectionException('Connection lost during request'))

    def _dataReceived(self, data):
        ''' Get response, check for valid message, decode result

        :param data: The data returned from the server
        '''
        self.framer.processIncomingPacket(data, self._handleResponse)

    def execute(self, request):
        ''' Starts the producer to send the next request to
        consumer.write(Frame(request))
        '''
        request.transaction_id = self.transaction.getNextTID()
        packet = self.framer.buildPacket(request)
        self.transport.write(packet)
        return self._buildResponse(request.transaction_id)

    def _handleResponse(self, reply):
        ''' Handle the processed response and link to correct deferred

        :param reply: The reply to process
        '''
        if reply is not None:
            tid = reply.transaction_id
            handler = self.transaction.getTransaction(tid)
            if handler:
                self.resolve_future(handler, reply)
            else:
                _logger.debug("Unrequested message: " + str(reply))

    def _buildResponse(self, tid):
        ''' Helper method to return a deferred response
        for the current request.

        :param tid: The transaction identifier for this response
        :returns: A defer linked to the latest request
        '''
        f = self.create_future()
        if not self._connected:
            self.raise_future(f, ConnectionException('Client is not connected'))
        else:
            self.transaction.addTransaction(f, tid)
        return f

    def create_future(self):
        raise NotImplementedError()

    def resolve_future(self, f, result):
        raise NotImplementedError()

    def raise_future(self, f, exc):
        raise NotImplementedError()