Добры день.
-
Собственно интересует вопрос, какое паспортное значение времени ответа на запрос по ModBUS ?
Ка к показали эксперименты с WB-MR14 реальное время round trip зависит от нагрузки, что несколько странно. При паузе между опросами 0.1 секунда round trip порядка 25мс. При уменьшении паузы до 5 мс раундрип увеличивается до 40мс. Проводил аналогичный эксперимент с китайскими Wellapro, у них round trip стабильный 25мс.
-
Можно ли как-то уменьшить 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, второе - обеспечивает совместимость с некоторыми мастерами, у которых медленно переключается направление передачи.
Исправил код. Просто разные эксперименты ставил, скинул вам не последнюю версию.
Естественно я замерял время одного обращения к слэйву, без всяких слипов.
Как я писал выше я замеряю время двумя способами:
-
Отладочная инфа библиоткеи
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
-
Второе значение print int((t1-t0)*1000), только при единичном обращении и безо всяких слипов.
Пауза между опросами 8мс - понятна, но я говорю не о ней, а о времени между отправкой запроса мастером и получением ответа от слэйва.
я как раз про это и пишу.
У вас на картинке нарисовано неправильно, сами “запросы” и “ответы” занимают довольно большое время. В вашем случае (если вы читаете 1 coil) - по 8мс каждый.
Вы имеете ввиду, что “время тишины” лежит внутри Round trip ?
Даже если это так, то все равно не понятно, почему Round trip растет с увеличением частоты опроса устройства.
Опять же 8+8 никак не 40.
Евгений, я вас правильно понял, что минимально возможное время опроса входов командой чтения сразу 14и входов = 16 мс ?