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()
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