Трябва да управлявам modbus устройство със сериен интерфейс. Нямам опит с modbus. Но краткото ми проучване разкри няколко modbus библиотеки
Какви са предимствата/недостатъците, има ли още по-добри алтернативи?
Трябва да управлявам modbus устройство със сериен интерфейс. Нямам опит с modbus. Но краткото ми проучване разкри няколко modbus библиотеки
Какви са предимствата/недостатъците, има ли още по-добри алтернативи?
Горе-долу по същото време се сблъсках със същия проблем - коя библиотека да избера за реализация на python modbus master, но в моя случай за серийна комуникация (modbus RTU), така че моите наблюдения са валидни само за modbus RTU.
При проверката си не обърнах много внимание на документацията, но примерите за сериен RTU master бяха най-лесни за намиране за modbus-tk, но все още в източника, а не в wiki и т.н.
отличителна черта: разчита на сериен поток (публикация от автора) и серийното изчакване трябва да бъде динамично зададено, в противен случай производителността ще бъде ниска (серийното изчакване трябва да се коригира за възможно най-дълъг отговор)
отличителна черта: сондира сериен буфер за данни, сглобява и връща отговор бързо.
Повече от 6 месеца използвах pymodbus поради най-доброто съотношение производителност/натоварване на процесора, но ненадеждните отговори се превърнаха в сериозен проблем при по-високи честоти на заявки и в крайна сметка преминах към по-бърза вградена система и добавих поддръжка за modbus-tk, която работи най-добре за мен.
Целта ми беше да постигна минимално време за реакция.
код:
import time
import traceback
import serial
import modbus_tk.defines as tkCst
import modbus_tk.modbus_rtu as tkRtu
import minimalmodbus as mmRtu
from pymodbus.client.sync import ModbusSerialClient as pyRtu
slavesArr = [2]
iterSp = 100
regsSp = 10
portNbr = 21
portName = 'com22'
baudrate = 153600
timeoutSp=0.018 + regsSp*0
print "timeout: %s [s]" % timeoutSp
mmc=mmRtu.Instrument(portName, 2) # port name, slave address
mmc.serial.baudrate=baudrate
mmc.serial.timeout=timeoutSp
tb = None
errCnt = 0
startTs = time.time()
for i in range(iterSp):
for slaveId in slavesArr:
mmc.address = slaveId
try:
mmc.read_registers(0,regsSp)
except:
tb = traceback.format_exc()
errCnt += 1
stopTs = time.time()
timeDiff = stopTs - startTs
mmc.serial.close()
print mmc.serial
print "mimalmodbus:\ttime to read %s x %s (x %s regs): %.3f [s] / %.3f [s/req]" % (len(slavesArr),iterSp, regsSp, timeDiff, timeDiff/iterSp)
if errCnt >0:
print " !mimalmodbus:\terrCnt: %s; last tb: %s" % (errCnt, tb)
pymc = pyRtu(method='rtu', port=portNbr, baudrate=baudrate, timeout=timeoutSp)
errCnt = 0
startTs = time.time()
for i in range(iterSp):
for slaveId in slavesArr:
try:
pymc.read_holding_registers(0,regsSp,unit=slaveId)
except:
errCnt += 1
tb = traceback.format_exc()
stopTs = time.time()
timeDiff = stopTs - startTs
print "pymodbus:\ttime to read %s x %s (x %s regs): %.3f [s] / %.3f [s/req]" % (len(slavesArr),iterSp, regsSp, timeDiff, timeDiff/iterSp)
if errCnt >0:
print " !pymodbus:\terrCnt: %s; last tb: %s" % (errCnt, tb)
pymc.close()
tkmc = tkRtu.RtuMaster(serial.Serial(port=portNbr, baudrate=baudrate))
tkmc.set_timeout(timeoutSp)
errCnt = 0
startTs = time.time()
for i in range(iterSp):
for slaveId in slavesArr:
try:
tkmc.execute(slaveId, tkCst.READ_HOLDING_REGISTERS, 0,regsSp)
except:
errCnt += 1
tb = traceback.format_exc()
stopTs = time.time()
timeDiff = stopTs - startTs
print "modbus-tk:\ttime to read %s x %s (x %s regs): %.3f [s] / %.3f [s/req]" % (len(slavesArr),iterSp, regsSp, timeDiff, timeDiff/iterSp)
if errCnt >0:
print " !modbus-tk:\terrCnt: %s; last tb: %s" % (errCnt, tb)
tkmc.close()
Резултати:
platform:
P8700 @2.53GHz
WinXP sp3 32bit
Python 2.7.1
FTDI FT232R series 1220-0
FTDI driver 2.08.26 (watch out for possible issues with 2.08.30 version on Windows)
pymodbus version 1.2.0
MinimalModbus version 0.4
modbus-tk version 0.4.2
четене на 100 x 64 регистъра:
без пестене на енергия
timeout: 0.05 [s]
Serial<id=0xd57330, open=False>(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, rtscts=False, dsrdtr=False)
mimalmodbus: time to read 1 x 100 (x 64 regs): 9.135 [s] / 0.091 [s/req]
pymodbus: time to read 1 x 100 (x 64 regs): 6.151 [s] / 0.062 [s/req]
modbus-tk: time to read 1 x 100 (x 64 regs): 2.280 [s] / 0.023 [s/req]
timeout: 0.03 [s]
Serial<id=0xd57330, open=False>(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.03, xonxoff=False, rtscts=False, dsrdtr=False)
mimalmodbus: time to read 1 x 100 (x 64 regs): 7.292 [s] / 0.073 [s/req]
pymodbus: time to read 1 x 100 (x 64 regs): 3.170 [s] / 0.032 [s/req]
modbus-tk: time to read 1 x 100 (x 64 regs): 2.342 [s] / 0.023 [s/req]
timeout: 0.018 [s]
Serial<id=0xd57330, open=False>(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.018, xonxoff=False, rtscts=False, dsrdtr=False)
mimalmodbus: time to read 1 x 100 (x 64 regs): 4.481 - 7.198 [s] / 0.045 - 0.072 [s/req]
pymodbus: time to read 1 x 100 (x 64 regs): 3.045 [s] / 0.030 [s/req]
modbus-tk: time to read 1 x 100 (x 64 regs): 2.342 [s] / 0.023 [s/req]
максимална икономия на енергия
timeout: 0.05 [s]
Serial<id=0xd57330, open=False>(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, rtscts=False, dsrdtr=False)
mimalmodbus: time to read 1 x 100 (x 64 regs): 10.289 [s] / 0.103 [s/req]
pymodbus: time to read 1 x 100 (x 64 regs): 6.074 [s] / 0.061 [s/req]
modbus-tk: time to read 1 x 100 (x 64 regs): 2.358 [s] / 0.024 [s/req]
timeout: 0.03 [s]
Serial<id=0xd57330, open=False>(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.03, xonxoff=False, rtscts=False, dsrdtr=False)
mimalmodbus: time to read 1 x 100 (x 64 regs): 8.166 [s] / 0.082 [s/req]
pymodbus: time to read 1 x 100 (x 64 regs): 4.138 [s] / 0.041 [s/req]
modbus-tk: time to read 1 x 100 (x 64 regs): 2.327 [s] / 0.023 [s/req]
timeout: 0.018 [s]
Serial<id=0xd57330, open=False>(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.018, xonxoff=False, rtscts=False, dsrdtr=False)
mimalmodbus: time to read 1 x 100 (x 64 regs): 7.776 [s] / 0.078 [s/req]
pymodbus: time to read 1 x 100 (x 64 regs): 3.169 [s] / 0.032 [s/req]
modbus-tk: time to read 1 x 100 (x 64 regs): 2.342 [s] / 0.023 [s/req]
четене на 100 x 10 регистри:
без пестене на енергия
timeout: 0.05 [s]
Serial<id=0xd56350, open=False>(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, rtscts=False, dsrdtr=False)
mimalmodbus: time to read 1 x 100 (x 10 regs): 6.246 [s] / 0.062 [s/req]
pymodbus: time to read 1 x 100 (x 10 regs): 6.199 [s] / 0.062 [s/req]
modbus-tk: time to read 1 x 100 (x 10 regs): 1.577 [s] / 0.016 [s/req]
timeout: 0.03 [s]
Serial<id=0xd56350, open=False>(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.03, xonxoff=False, rtscts=False, dsrdtr=False)
mimalmodbus: time to read 1 x 100 (x 10 regs): 3.088 [s] / 0.031 [s/req]
pymodbus: time to read 1 x 100 (x 10 regs): 3.143 [s] / 0.031 [s/req]
modbus-tk: time to read 1 x 100 (x 10 regs): 1.533 [s] / 0.015 [s/req]
timeout: 0.018 [s]
Serial<id=0xd56350, open=False>(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.018, xonxoff=False, rtscts=False, dsrdtr=False)
mimalmodbus: time to read 1 x 100 (x 10 regs): 3.066 [s] / 0.031 [s/req]
pymodbus: time to read 1 x 100 (x 10 regs): 3.006 [s] / 0.030 [s/req]
modbus-tk: time to read 1 x 100 (x 10 regs): 1.533 [s] / 0.015 [s/req]
максимална икономия на енергия
timeout: 0.05 [s]
Serial<id=0xd56350, open=False>(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, rtscts=False, dsrdtr=False)
mimalmodbus: time to read 1 x 100 (x 10 regs): 6.386 [s] / 0.064 [s/req]
pymodbus: time to read 1 x 100 (x 10 regs): 5.934 [s] / 0.059 [s/req]
modbus-tk: time to read 1 x 100 (x 10 regs): 1.499 [s] / 0.015 [s/req]
timeout: 0.03 [s]
Serial<id=0xd56350, open=False>(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.03, xonxoff=False, rtscts=False, dsrdtr=False)
mimalmodbus: time to read 1 x 100 (x 10 regs): 3.139 [s] / 0.031 [s/req]
pymodbus: time to read 1 x 100 (x 10 regs): 3.170 [s] / 0.032 [s/req]
modbus-tk: time to read 1 x 100 (x 10 regs): 1.562 [s] / 0.016 [s/req]
timeout: 0.018 [s]
Serial<id=0xd56350, open=False>(port='com22', baudrate=153600, bytesize=8, parity='N', stopbits=1, timeout=0.018, xonxoff=False, rtscts=False, dsrdtr=False)
mimalmodbus: time to read 1 x 100 (x 10 regs): 3.123 [s] / 0.031 [s/req]
pymodbus: time to read 1 x 100 (x 10 regs): 3.060 [s] / 0.031 [s/req]
modbus-tk: time to read 1 x 100 (x 10 regs): 1.561 [s] / 0.016 [s/req]
Пример за зареждане за modbus-rpc мост (~3% е причинено от RPC сървърна част)
5 x 64 регистрират синхронни четения за секунда и едновременно
асинхронен достъп с таймаут на сериен порт, зададен на 0,018 s
modbus-tk
pymodbus:
РЕДАКТИРАНЕ: библиотеката modbus-tk може лесно да бъде подобрена, за да се намали използването на процесора. В оригиналната версия след изпращане на заявка и преминаване на T3.5 заспиване главният асемблира отговор един байт наведнъж. Профилирането доказа, че по-голямата част от времето се изразходва за достъп до сериен порт. Това може да се подобри, като се опитате да прочетете очакваната дължина на данните от серийния буфер. Според документацията на pySerial трябва да е безопасно (без увисване нагоре, когато отговорът липсва или е твърде кратък), ако е зададено време за изчакване:
read(size=1)
Parameters: size – Number of bytes to read.
Returns: Bytes read from the port.
Read size bytes from the serial port. If a timeout is set it may return less characters as
requested. With no timeout it will block until the requested number of bytes is read.
след модифициране на `modbus_rtu.py' по следния начин:
def _recv(self, expected_length=-1):
"""Receive the response from the slave"""
response = ""
read_bytes = "dummy"
iterCnt = 0
while read_bytes:
if iterCnt == 0:
read_bytes = self._serial.read(expected_length) # reduces CPU load for longer frames; serial port timeout is used anyway
else:
read_bytes = self._serial.read(1)
response += read_bytes
if len(response) >= expected_length >= 0:
#if the expected number of byte is received consider that the response is done
#improve performance by avoiding end-of-response detection by timeout
break
iterCnt += 1
След модификация на modbus-tk, натоварването на процесора в приложението в реалния живот спадна значително без значително увреждане на производителността (все още по-добро от pymodbus):
Актуализиран пример за натоварване за modbus-rpc мост (~3% е причинено от RPC сървърна част)
5 x 64 регистрират синхронни четения за секунда и едновременно
асинхронен достъп с таймаут на сериен порт, зададен на 0,018 s
modbus-tk
pymodbus:
tkmc.execute(slaveId, tkCst.READ_HOLDING_REGISTERS, 0,regsSp)
, как да прочетем конкретен холдинг регистър или няколко регистъра? И как да отпечатате отговора?
- person mrid; 21.05.2018
Току-що открих uModbus и за внедряване в нещо като Raspberry PI (или друг малък SBC), това е мечта. Това е прост пакет с единична възможност, който не включва 10+ зависимости, както прави pymodbus.
Наистина зависи какво приложение използвате и какво се опитвате да постигнете.
pymodbus е много стабилна библиотека. Работи и ви дава много инструменти, с които да работите. Но може да се окаже малко смущаващо, когато се опитате да го използвате. Трудно ми беше да работя лично. Той ви предлага възможността да използвате както RTU, така и TCP/IP, което е страхотно!
MinimalModbus е много проста библиотека. В крайна сметка използвах това за моето приложение, защото направи точно това, което трябваше да направя. Прави само RTU комуникации и го прави добре, доколкото знам. Никога не съм имал проблеми с него.
Никога не съм разглеждал Modbus-tk, така че не знам къде стои.
В крайна сметка обаче зависи от това какво е вашето приложение. В крайна сметка открих, че Python не е най-добрият избор за мен.