Минимальный цикл опроса по ModBUS

Добры день.

  1. Собственно интересует вопрос, какое паспортное значение времени ответа на запрос по ModBUS ?
    Ка к показали эксперименты с WB-MR14 реальное время round trip зависит от нагрузки, что несколько странно. При паузе между опросами 0.1 секунда round trip порядка 25мс. При уменьшении паузы до 5 мс раундрип увеличивается до 40мс. Проводил аналогичный эксперимент с китайскими Wellapro, у них round trip стабильный 25мс.

  2. Можно ли как-то уменьшить round trip ?

Round trip - время между отправкой запроса на слэйв и получением ответа от него. Если пренебречь временем передачи, то это время за которое слэйв обрабатывает запрос сервера.

Добрый день!

А что вы за странное слово используете?
Опишите максимально подробно методику эксперимента пожалуйста.

Эксперимент простой: Выставил таймаут ожидания ответа в 0.1с и запускал цикл опроса слэйва с различными интервалами между опросами. Замерял время ответа от слэйва. При увеличении частоты опроса, время ответа растет с 25мс до 40мс.

плохое описание. Какой софт, чем измеряете, где код, который измеряет, какой baudrate выставлен?

Прошу прощения.

Baudrate 9600
для работы с модулями использую переходник с USB на RS486, скорость работы переходника 9600
К модулю обращаюсь из питона с помощью библиотеки Minimalmodbus .

Время замеряю двумя способами: 1. Minimalmodbus выдает время roundtrip в консоль в режиме отладки.
2. Замеряю времена перед и после вызова функции чтения из питона.

Код на питоне ниже

#!/usr/bin/env python
import minimalmodbus
import time

instrument = minimalmodbus.Instrument('/dev/USB0', 30) # port name, slave address (in decimal)

instrument.serial.baudrate = 9600   # Baud
instrument.serial.bytesize = 8
instrument.serial.stopbits = 1
instrument.serial.timeout  = 0.1   # seconds
instrument.debug = True

print instrument
swprev=0
counter = 0
tprev=time.time()

while True:
        time.sleep(0.004)

        counter = counter+1

        t=time.time()
        if t-tprev > 1:
                print "------------ ",counter
                counter=0
                tprev=t

        try:
                t0=time.time()
                sw1 = instrument.read_bit(0)
                t1=time.time()
                print int((t1-t0)*1000)

                if(swprev!=sw1):
                        print (" ", swprev, "->", sw1)
                        swprev=sw1
        except IOError:
                print("Failed to read from instrument")

А время ответа, которое вы упоминаете, это вывод

print int((t1-t0)*1000)

?
Оно же зависит от того, какой sleep у вас в цикле

Вообще у наших устройств ограничено минимальное время ответа: пауза между концом пакета запроса и началом пакета с ответом не должна быть меньше длительности 3.5 байт (на 9600бодах это ~4мс) и не должна быть меньше 8мс.
Первое ограничение - требование стандарта Modbus RTU, второе - обеспечивает совместимость с некоторыми мастерами, у которых медленно переключается направление передачи.

Исправил код. Просто разные эксперименты ставил, скинул вам не последнюю версию.
Естественно я замерял время одного обращения к слэйву, без всяких слипов.
Как я писал выше я замеряю время двумя способами:

  1. Отладочная инфа библиоткеи
    Timing::

                                               Request from master (Master is writing)
                                               |
                                               |       Response from slave (Master is reading)
                                               |       |
         ----W----R----------------------------W-------R----------------------------------------
                  |                            |       |
                  |<----- Silent period ------>|       |
                                               |       |
                          Roundtrip time  ---->|-------|<--`
         _checkString(request, minlength=1, description='request')
     _checkInt(number_of_bytes_to_read)
    
     if self.debug:
         _print_out('\nMinimalModbus debug mode. Writing to instrument (expecting {} bytes back): {!r} ({})'. \
             format(number_of_bytes_to_read, request, _hexlify(request)))
    
     if self.close_port_after_each_call:
         self.serial.open()
    
     #self.serial.flushInput() TODO
    
     if sys.version_info[0] > 2:
         request = bytes(request, encoding='latin1')  # Convert types to make it Python3 compatible
    
     # Sleep to make sure 3.5 character times have passed
     minimum_silent_period   = _calculate_minimum_silent_period(self.serial.baudrate)
     time_since_read         = time.time() - _LATEST_READ_TIMES.get(self.serial.port, 0)
    
     if time_since_read < minimum_silent_period:
         sleep_time = minimum_silent_period - time_since_read
    
         if self.debug:
             template = 'MinimalModbus debug mode. Sleeping for {:.1f} ms. ' + \
                     'Minimum silent period: {:.1f} ms, time since read: {:.1f} ms.'
             text = template.format(
                 sleep_time * _SECONDS_TO_MILLISECONDS,
                 minimum_silent_period * _SECONDS_TO_MILLISECONDS,
                 time_since_read * _SECONDS_TO_MILLISECONDS)
             _print_out(text)
    
         time.sleep(sleep_time)
    
     elif self.debug:
         template = 'MinimalModbus debug mode. No sleep required before write. ' + \
             'Time since previous read: {:.1f} ms, minimum silent period: {:.2f} ms.'
         text = template.format(
             time_since_read * _SECONDS_TO_MILLISECONDS,
             minimum_silent_period * _SECONDS_TO_MILLISECONDS)
         _print_out(text)
    
     # Write request
     latest_write_time = time.time()
     
     self.serial.write(request)
    
     # Read and discard local echo
     if self.handle_local_echo:
         localEchoToDiscard = self.serial.read(len(request))
         if self.debug:
             template = 'MinimalModbus debug mode. Discarding this local echo: {!r} ({} bytes).' 
             text = template.format(localEchoToDiscard, len(localEchoToDiscard))
             _print_out(text)
         if localEchoToDiscard != request:
             template = 'Local echo handling is enabled, but the local echo does not match the sent request. ' + \
                 'Request: {!r} ({} bytes), local echo: {!r} ({} bytes).' 
             text = template.format(request, len(request), localEchoToDiscard, len(localEchoToDiscard))
             raise IOError(text)
    
     # Read response
     answer = self.serial.read(number_of_bytes_to_read)
     _LATEST_READ_TIMES[self.serial.port] = time.time()
    
     if self.close_port_after_each_call:
         self.serial.close()
    
     if sys.version_info[0] > 2:
         answer = str(answer, encoding='latin1')  # Convert types to make it Python3 compatible
    
     if self.debug:
         template = 'MinimalModbus debug mode. Response from instrument: {!r} ({}) ({} bytes), ' + \
             'roundtrip time: {:.1f} ms. Timeout setting: {:.1f} ms.\n'
         text = template.format(
             answer,
             _hexlify(answer),
             len(answer),
             (_LATEST_READ_TIMES.get(self.serial.port, 0) - latest_write_time) * _SECONDS_TO_MILLISECONDS,
             self.serial.timeout * _SECONDS_TO_MILLISECONDS)
         _print_out(text)
    
     if len(answer) == 0:
         raise IOError('No communication with the instrument (no answer)')
    
     return answer
    
  2. Второе значение print int((t1-t0)*1000), только при единичном обращении и безо всяких слипов.

Пауза между опросами 8мс - понятна, но я говорю не о ней, а о времени между отправкой запроса мастером и получением ответа от слэйва.

я как раз про это и пишу.

У вас на картинке нарисовано неправильно, сами “запросы” и “ответы” занимают довольно большое время. В вашем случае (если вы читаете 1 coil) - по 8мс каждый.

Вы имеете ввиду, что “время тишины” лежит внутри Round trip ?
Даже если это так, то все равно не понятно, почему Round trip растет с увеличением частоты опроса устройства.

Опять же 8+8 никак не 40.

Евгений, я вас правильно понял, что минимально возможное время опроса входов командой чтения сразу 14и входов = 16 мс ?