def _recv( self, size ): """Replicate the approximate semantics of a socket recv; return what's available. However, don't return Nothing (indicating an EOF). So, wait for up to remaining 'self.timeout' for something to show up, but return immediately with whatever is there. We'll do it simply -- just read one at a time from the serial port. We could find out how many bytes are available using the TIOCINQ ioctl, but this won't work on non-Posix systems. We can't just use the built-in Serial's read method and adjust its own _timeout to reflect our own remaining timeout -- we must only block 'til we have at least one character, and then continue reading 'til no more input is immediately available; there is no way to invoke Serial.read to indicate that. """ if not self.socket: raise ConnectionException( self.__str__() ) begun = misc.timer() request = None try: request = modbus_rtu_read( fd=self.socket.fd, decoder=self.framer.decoder, size=size, timeout=self.timeout ) except Exception as exc: logging.warning( "Receive Exception %s; %s", exc, traceback.format_exc() ) if request: return request # Nothing within timeout; potential client failure, disconnected hardware? Force a re-open self.close() logging.debug( "Receive failure in %7.3f/%7.3fs", misc.timer() - begun, self.timeout ) raise ConnectionException("Receive from (%s, %s) failed: Timeout" % ( getattr( self, 'host', '(serial)' ), self.port ))
def run(self): """ тут производится обновление """ if self._logger is not None: self._logger.info("[{n}]: Попытка запуска испытания".format(n=self.name)) try: self.reset() except ConnectionException: if self._logger is not None: self._logger.error("[{n}]: Невозможно подключиться к хосту [{h}]".format(n=self.name, h=self._host)) raise ConnectionException("Не удалось подключиться к хосту {h}".format(h=self._host)) if self._logger is not None: self._logger.info("[{n}]: Испытание запущено".format(n=self.name)) try: while not self.__exit: self._update() time.sleep(self._invfreq) except AttributeError: if self._logger is not None: self._logger.error("[{n}]: Связь была прервана, необходим перезапуск испытания".format(n=self.name)) raise ModbusIOException("Связь была оборвана") except ConnectionException: if self._logger is not None: self._logger.error("[{n}]: Невозможно подключиться к хосту [{h}]".format(n=self.name, h=self._host)) raise ConnectionException("Не удалось подключиться к хосту {h}".format(h=self._host))
def _send(self, request): """ Sends data on the underlying socket If receive buffer still holds some data then flush it. Sleep if last send finished less than 3.5 character times ago. :param request: The encoded request to send :return: The number of bytes written """ if not self.socket: raise ConnectionException(self.__str__()) if request: try: waitingbytes = self._in_waiting() if waitingbytes: result = self.socket.read(waitingbytes) if _logger.isEnabledFor(logging.WARNING): _logger.warning("Cleanup recv buffer before " "send: " + hexlify_packets(result)) except NotImplementedError: pass size = self.socket.write(request) return size return 0
async def read_voltage(self): """reads the voltage off of ADAM-6024's inputs for channels 0-5. Parameters ---------- None Returns ------- volts : List of floats the voltages on the ADAM's input channels """ try: readout = await self.client.read_input_registers(0, 8, unit=1) voltages = [self.counts_to_volts(r) for r in readout.registers] return voltages except AttributeError: # read_input_registers() *returns* (not raises) a # ModbusIOException in the event of loss of ADAM network # connectivity, which causes an AttributeError when we try # to access the registers field. But the whole thing is # really a connectivity problem, so we re-raise it as a # ConnectionException, which we know how to handle. Weird # exception handling is a known issue with pymodbus so it # may see a fix in a future version, which may require # minor code changes on our part. # https://github.com/riptideio/pymodbus/issues/298 raise ConnectionException(f"Unable to reach modbus device at " f"{self.clientip}:{self.clientport}.")
def _send(self, request): ''' Sends data on the underlying socket If receive buffer still holds some data then flush it. Sleep if last send finished less than 3.5 character times ago. :param request: The encoded request to send :return: The number of bytes written ''' if not self.socket: raise ConnectionException(self.__str__()) if request: ts = time.time() if ts < self._last_frame_end + self._silent_interval: _logger.debug("will sleep to wait for 3.5 char") time.sleep(self._last_frame_end + self._silent_interval - ts) try: waitingbytes = self.socket.inWaiting() if waitingbytes: result = self.socket.read(waitingbytes) if _logger.isEnabledFor(logging.WARNING): _logger.warning("cleanup recv buffer before send: " + " ".join([hex(ord(x)) for x in result])) except NotImplementedError: pass size = self.socket.write(request) self._last_frame_end = time.time() return size return 0
def _handle_abrupt_socket_close(self, size, data, duration): """ Handle unexpected socket close by remote end Intended to be invoked after determining that the remote end has unexpectedly closed the connection, to clean up and handle the situation appropriately. :param size: The number of bytes that was attempted to read :param data: The actual data returned :param duration: Duration from the read was first attempted until it was determined that the remote closed the socket :return: The more than zero bytes read from the remote end :raises: ConnectionException If the remote end didn't send any data at all before closing the connection. """ self.close() readsize = ("read of %s bytes" % size if size else "unbounded read") msg = ("%s: Connection unexpectedly closed " "%.6f seconds into %s" % (self, duration, readsize)) if data: result = b"".join(data) msg += " after returning %s bytes" % len(result) _logger.warning(msg) return result msg += " without response from unit before it closed connection" raise ConnectionException(msg)
def _recv(self, size): """ Reads data from the underlying descriptor :param size: The number of bytes to read :return: The bytes read if the peer sent a response, or a zero-length response if no data packets were received from the client at all. :raises: ConnectionException if the socket is not initialized, or the peer either has closed the connection before this method is invoked or closes it before sending any data before timeout. """ if not self.socket: raise ConnectionException(self.__str__()) # socket.recv(size) waits until it gets some data from the host but # not necessarily the entire response that can be fragmented in # many packets. # To avoid the splitted responses to be recognized as invalid # messages and to be discarded, loops socket.recv until full data # is received or timeout is expired. # If timeout expires returns the read data, also if its length is # less than the expected size. self.socket.setblocking(0) timeout = self.timeout # If size isn't specified read up to 4096 bytes at a time. if size is None: recv_size = 4096 else: recv_size = size data = [] data_length = 0 time_ = time.time() end = time_ + timeout while recv_size > 0: ready = select.select([self.socket], [], [], end - time_) if ready[0]: recv_data = self.socket.recv(recv_size) if recv_data == b'': return self._handle_abrupt_socket_close( size, data, time.time() - time_) data.append(recv_data) data_length += len(recv_data) time_ = time.time() # If size isn't specified continue to read until timeout expires. if size: recv_size = size - data_length # Timeout is reduced also if some data has been received in order # to avoid infinite loops when there isn't an expected response # size and the slave sends noisy data continuosly. if time_ > end: break return b"".join(data)
def test_read_slave_registers_on_ConnectionException_exits(self, MockExit): slave_id = 1 self.mock_client.read_holding_registers.side_effect = ConnectionException( ) self.reader.read_slave(slave_id) MockExit.assert_called()
def __enter__(self): """ Implement the client with enter block :returns: The current instance of the client """ if not self.connect(): raise ConnectionException("Failed to connect[%s]" % (self.__str__())) return self
def execute(self, request=None): ''' :param request: The request to process :returns: The result of the request execution ''' if self.transaction: return self.transaction.execute(request) raise ConnectionException("Client Not Connected")
def execute(self, request=None): """ :param request: The request to process :returns: The result of the request execution """ if not self.connect(): raise ConnectionException("Failed to connect[%s]" % (self.__str__())) return self.transaction.execute(request)
def _recv(self, size): """ Reads data from the underlying descriptor :param size: The number of bytes to read :return: The bytes read """ if not self.socket: raise ConnectionException(self.__str__()) return self.socket.recvfrom(size)[0]
def _recv( self, size ): """On a receive timeout, closes the socket and raises a ConnectionException. Otherwise, returns the available input.""" if not self.socket: raise ConnectionException( self.__str__() ) begun = misc.timer() timeout = self.timeout # This computes the remaining timeout available logging.debug( "Receive begins in %7.3f/%7.3fs", misc.timer() - begun, timeout ) r,w,e = select.select( [self.socket], [], [], timeout ) if r: logging.debug( "Receive reading in %7.3f/%7.3fs", misc.timer() - begun, timeout ) result = super( modbus_client_tcp, self )._recv( size ) logging.debug( "Receive success in %7.3f/%7.3fs", misc.timer() - begun, timeout ) return result self.close() logging.debug( "Receive failure in %7.3f/%7.3fs", misc.timer() - begun, timeout ) raise ConnectionException("Receive from (%s, %s) failed: Timeout" % ( getattr( self, 'host', '(serial)' ), self.port ))
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 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 _recv(self, size): ''' Reads data from the underlying descriptor :param size: The number of bytes to read :return: The bytes read ''' if not self.socket: raise ConnectionException(self.__str__()) result = self.socket.read(size) self._last_frame_end = time.time() return result
def _send(self, request): ''' Sends data on the underlying socket :param request: The encoded request to send :return: The number of bytes written ''' if not self.socket: raise ConnectionException(self.__str__()) if request: return self.socket.write(request) return 0
def test__ProductionDevice_getRegisters__NoConnection(self): plant = ProductionPlant() plant.load('test_data/modmap_testing.yaml', self.testPlantname()) for dev in plant.devices: dev.get_registers = Mock( side_effect=ConnectionException('Failed to connect')) # No Raises plant.get_registers()
def _send(self, request): """ Sends data on the underlying socket :param request: The encoded request to send :return: The number of bytes written """ if not self.socket: raise ConnectionException(self.__str__()) if request: return self.socket.sendto(request, (self.host, self.port)) return 0
async def connect(self, ip, port): self.clientip = ip self.clientport = port if self.simulation_mode: self.client = MockModbusClient(self.clientip, self.clientport) else: try: self.client = ModbusClient(self.clientip, self.clientport) except AttributeError: raise ConnectionException( "Unable to connect to modbus device at " f"{self.clientip}:{self.clientport}.")
def _buildResponse(self): ''' Helper method to return a deferred response for the current request. :returns: A defer linked to the latest request ''' if not self._connected: return defer.fail(ConnectionException('Client is not connected')) d = defer.Deferred() self._requests.append(d) return d
def _recv(self, size): """ Reads data from the underlying descriptor :param size: The number of bytes to read :return: The bytes read """ if not self.socket: raise ConnectionException(self.__str__()) if size is None: size = self._wait_for_data() result = self.socket.read(size) return result
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)
def _recv(self, size): ''' Reads data from the underlying descriptor :param size: The number of bytes to read :return: The bytes read ''' if not self.socket: raise ConnectionException(self.__str__()) rd = self.socket.read(size) if _logger.isEnabledFor(logging.DEBUG): _logger.debug(" ".join([hex(ord(x)) for x in rd])) return rd
def _build_response(self, tid): """ Prepare for a response, returns a future :param tid: :return: Future """ f = Future() if not self._connected: f.set_exception(ConnectionException("Client is not connected")) return f self.transaction.addTransaction(f, tid) return f
def _recv(self, size): """ Reads data from the underlying descriptor :param size: The number of bytes to read :return: The bytes read """ if not self.socket: raise ConnectionException(self.__str__()) if size is None: _logger.debug("Shall wait for data") size = self._wait_for_data() result = self.socket.read(size) _logger.debug("Finished reading socket....") return result
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 _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 _send(self, request): ''' Sends data on the underlying socket :param request: The encoded request to send :return: The number of bytes written ''' if not self.socket: raise ConnectionException(self.__str__()) if request: self.socket.flushInput() n = self.socket.write(request) if _logger.isEnabledFor(logging.DEBUG): _logger.debug(" written %d bytes" % n) _logger.debug(" ".join([hex(ord(x)) for x in request])) return n return 0
def _send(self, request): """ Sends data on the underlying socket :param request: The encoded request to send :return: The number of bytes written """ if not self.socket: raise ConnectionException(self.__str__()) if self.state == ModbusTransactionState.RETRYING: data = self._check_read_buffer() if data: return data if request: return self.socket.send(request) return 0