Ejemplo n.º 1
0
    def _command(self, cmd, ip=None):
        if debug: 
            print 'TcpIpClient command: ', cmd, self.ip
            print cmd.buffer
        if self.ip is None: raise EnvalidValue('Modbus IP: there must be an ip address')
        b = cmd.buffer.tostring()
        b = b[:-2] #trim off the crc
        t = cmd.timeout(self.timeout)
        port = None
        self._lock.acquire()
        try:
            try:
                if self._port is None:
                    if debug: print 'TcpIpClient open new tcp connection'
                    self._port = TcpClientConnection(self.ip, self.udp_port, .750) #.75 second connect timeout
                    if debug: print 'TcpIpClient port is: ', self._port
                    self._port.open_connection()
                    if debug: print 'TcpIpClient connection has opened'
                mbapw = MBAP(len(b), cmd.slave_address, 0)
                if debug: print 'MBAP encoding is: ', mbapw, b
                self._port.write(mbapw.encoding + b)
                if debug: print 'TcpIpClient command sent, wait for response'
                header = buffer()
                #todo timeout faster than tcp/ip does
                mbapr = self._port.read_MBAP()
                if debug:
                    print 'TcpIpClient MBAP of response received'
                    print mbapr

                if mbapr is None:
                    raise EInvalidResponse('Modbus: no response to client command', mbapw)
                if mbapw.transaction_id != mbapr.transaction_id: raise EInvlaidValue("Modbus: transaction id mismatch", mbapr)
                if mbapr.length > 256: raise EInvalidValue('Modbus: length too long', mbapr.length)
                #if mbapw.unit_id != mbapr.unit_id: raise EInvalidValue('Modbus: unit id mismatch')

                header.fromstring(self._port.read(mbapr.length)) #get the whole thing at once
                if debug: print 'TcpIpClient complete response: ', header
                if header[1] & 0x80:
                    e = cmd.exception(exception.factory)(self, header)
                    raise e
                resp = cmd.response(response.factory)(self, header, t)
                return resp
                #when response calls crc, return 0 (happy code)
                #when response uses port calls, intercept them
                #an alternative would be to append the crc to the header...
            except Exception, e:
                if self._port:
                    try:
                        if debug:
                            print 'modbus tcp: close connection', self._port
                        self._port.close_connection()
                    except:
                        pass
                self._port = None
                raise e
        finally:
            self._lock.release()
        raise EUnreachableCode()
Ejemplo n.º 2
0
class TcpIpClient(CompositeNode):
    def __init__(self):
        CompositeNode.__init__(self)
        self._lock = threading.RLock()
        self._port = None
        self.last_error = None
        self.retries = 3 #used by register_cache.RegisterCache._refresh
        self.report_timeouts = 1 #0 == try but give back last good val
        return
    def configure(self,config):
        self.timeout = 1.0
        CompositeNode.configure(self,config)
        set_attribute(self, 'ip', REQUIRED, config)
        set_attribute(self, 'port', 502, config, int)
        self.udp_port = int(self.port)
        set_attribute(self, 'debug', 0, config, int)
        set_attribute(self, 'retries', 3, config, int)
        set_attribute(self, 'report_timeouts', 1, config, int)
        self.port = self #intercept any calls to the port
        return
    def configuration(self):
        config = CompositeNode.configuration(self)
        get_attribute(self, 'ip', config, str)
        config['port'] = str(self.udp_port)
        get_attribute(self, 'debug', config, str)
        get_attribute(self, 'last_error', config, str)
        get_attribute(self, 'retries', config, str)
        get_attribute(self, 'report_timeouts', config, str)
        return config

    def buffer(self, initializer=None):
        return base.buffer(initializer)

    def crc(self, byte_array):
        return 0 #always answer that crc is good

    def read(self, header, n , timeout): #pretend to be a serial port but do nothing
        pass
    ##
    # Prevent other threads from reading or writing to the connection until
    # it's unlocked().
    #
    # Used to synchronize access to the connection.
    #
    def lock(self):
        self._lock.acquire()
        return
    ##
    # Release the inner most lock on the connection by the current thread.
    #
    # Used to synchronize access to the connection.
    #
    def unlock(self):
        self._lock.release()
        return

    def _command(self, cmd, ip=None):
        if debug: 
            print 'TcpIpClient command: ', cmd, self.ip
            print cmd.buffer
        if self.ip is None: raise EnvalidValue('Modbus IP: there must be an ip address')
        b = cmd.buffer.tostring()
        b = b[:-2] #trim off the crc
        t = cmd.timeout(self.timeout)
        port = None
        self._lock.acquire()
        try:
            try:
                if self._port is None:
                    if debug: print 'TcpIpClient open new tcp connection'
                    self._port = TcpClientConnection(self.ip, self.udp_port, .750) #.75 second connect timeout
                    if debug: print 'TcpIpClient port is: ', self._port
                    self._port.open_connection()
                    if debug: print 'TcpIpClient connection has opened'
                mbapw = MBAP(len(b), cmd.slave_address, 0)
                if debug: print 'MBAP encoding is: ', mbapw, b
                self._port.write(mbapw.encoding + b)
                if debug: print 'TcpIpClient command sent, wait for response'
                header = buffer()
                #todo timeout faster than tcp/ip does
                mbapr = self._port.read_MBAP()
                if debug:
                    print 'TcpIpClient MBAP of response received'
                    print mbapr

                if mbapr is None:
                    raise EInvalidResponse('Modbus: no response to client command', mbapw)
                if mbapw.transaction_id != mbapr.transaction_id: raise EInvlaidValue("Modbus: transaction id mismatch", mbapr)
                if mbapr.length > 256: raise EInvalidValue('Modbus: length too long', mbapr.length)
                #if mbapw.unit_id != mbapr.unit_id: raise EInvalidValue('Modbus: unit id mismatch')

                header.fromstring(self._port.read(mbapr.length)) #get the whole thing at once
                if debug: print 'TcpIpClient complete response: ', header
                if header[1] & 0x80:
                    e = cmd.exception(exception.factory)(self, header)
                    raise e
                resp = cmd.response(response.factory)(self, header, t)
                return resp
                #when response calls crc, return 0 (happy code)
                #when response uses port calls, intercept them
                #an alternative would be to append the crc to the header...
            except Exception, e:
                if self._port:
                    try:
                        if debug:
                            print 'modbus tcp: close connection', self._port
                        self._port.close_connection()
                    except:
                        pass
                self._port = None
                raise e
        finally:
            self._lock.release()
        raise EUnreachableCode()

    def command(self, cmd, ip=None):
        e = None
        try:
            for i in range(3):
                try:
                    return self._command(cmd, ip)
                except Exception, e:
                    if debug:
                        print str(e)
                    sleep(1)
        finally:
            self.last_error = str(e)
        raise