Modbus TCP slave na TCP serveru

Projekt Examples\Qenex_TcpServer_ModbusSlave_HoldingRegisters.qproj najdete ve složce Examples vedle QInsight.exe.

Co ukázka předvádí

QInsight tu není master, ale slave: driver TCP Server naslouchá na portu 502 a protokol Modbus Slave (RTU/TCP) v režimu tcp vystavuje dvě proměnné jako holding registry 0 a 1 pod unit adresou 2. Skutečné zařízení žádné není — hodnoty registrů generuje periodický Python skript, který každé 2 s posune simulovanou teplotu oleje (0–150 °C) a tlak oleje (1000–2500 kPa) o krok nahoru nebo dolů. Externí Modbus TCP master, který se k QInsight připojí, čte pilovitě rostoucí a klesající hodnoty.

Druhá polovina ukázky je zobrazení: zápis skriptu do proměnné protokolu zařízení se v controlech neprojeví (nevzniká vzorek signálu). Skript proto každou hodnotu zapisuje dvakrát — do slave proměnné pro mastera a do virtuální proměnné, kterou kreslí graf a signal controly. Na projektu je tak vidět i to, jak se TCP Server chová při výpadku klienta: odpojený kabel zjistí keepalive, server se vrátí k naslouchání a master se po obnovení znovu připojí.

Co potřebujete

  • QInsight s nastaveným Pythonem — skripty jsou jádrem ukázky, viz Nastavení Pythonu.
  • Modbus TCP master jako protějšek, například qModMaster. Připojte ho na IP adresu počítače s QInsight, port 502, unit 2, a nechte ho číst holding registry od adresy 0, počet 2. Díky respondToAnyUnit=true slave odpoví i na jinou unit adresu.
  • Master může běžet na stejném PC — pak se připojuje na 127.0.0.1.
  • Při masteru na jiném počítači musí firewall na PC s QInsight povolit příchozí spojení na port 502 (lokální strana QInsight, vzdálená master). Obnovu spojení po výpadku vždy iniciuje master (klient), server jen naslouchá.
  • Port 502 nesmí obsazovat jiný program; jinak se driver opakovaně pokouší port otevřít (viz Obnova spojení) a spustí se, jakmile se port uvolní.

Projekt

Drivery a protokoly

Projekt má dvě dvojice driver + protokol.

TCP Server (TcpServerDriver) s nastavením bindAddress=0.0.0.0;port=502;reconnectDelayMs=1000;reconnectAttempts=-1;keepAliveMs=5000:

ParametrHodnotaVýznam v ukázce
bindAddress0.0.0.0naslouchá na všech rozhraních — master se připojí přes libovolnou IP adresu PC i přes 127.0.0.1
port502standardní port Modbus TCP
reconnectDelayMs1000prodleva mezi pokusy o otevření naslouchacího portu
reconnectAttempts-1pokouší se port otevřít bez omezení počtu pokusů (obsazený port, chybějící adresa)
keepAliveMs5000keepalive na přijatém spojení: po 5 s bez provozu spojení ověřuje, odpojený klient se zjistí během několika sekund

Pod ním Modbus Slave (RTU/TCP) (ModbusSlaveProtocol) s nastavením mode=tcp;unitId=2;respondToAnyUnit=true:

ParametrHodnotaVýznam v ukázce
modetcprámování Modbus TCP (MBAP), odpovídá driveru TCP Server
unitId2vlastní adresa slave
respondToAnyUnittrueslave odpoví bez ohledu na unit adresu v dotazu — nastaveno výslovně, protože předvyplněná šablona obsahuje false

Druhá dvojice Virtual Variables Host (VirtualDataDriver) + Virtual Variables (VirtualDataProtocol) nemá žádná nastavení; nese zobrazovací kopie obou registrů.

Proměnné

ProměnnáNamespaceTypProtokolComm paramsRole
OilTemp/ModbusushortModbus SlaveregisterType="holdingRegister";address="0";wordOrder="big";direction="read"holding registr 0 čtený masterem
OilPressure/ModbusushortModbus SlaveregisterType="holdingRegister";address="1";wordOrder="big";direction="read"holding registr 1 čtený masterem
OilTempView/VirtualdoubleVirtual Variables—zobrazovací kopie OilTemp pro graf a signal control
OilPressureView/VirtualdoubleVirtual Variables—zobrazovací kopie OilPressure pro graf a signal control

Slave proměnné jsou ushort — jeden 16bitový registr, do kterého se rozsahy 0–150 i 1000–2500 vejdou bez přepočtu. Prezentace Temperature (0–150, °C) a Pressure (1000–2500, kPa) používají obě lineární konverzi Raw s multiplier=1, offset=0 a formát {0:F0}; inženýrská hodnota se rovná surové (raw) hodnotě, master tedy čte přímo stupně a kilopascaly. Stejné prezentace mají i virtuální kopie, takže graf a signal controly ukazují tytéž jednotky a rozsahy.

Žádná ze slave proměnných není navázaná na control — vzhledem k tomu, že zápis skriptu na protokolu zařízení nevyvolá událost pro UI, by v něm nic neukazovala.

Skripty

Projekt má dva skripty, oba jen pro runtime (ne pro replay), blokující, bez timeoutu.

init.py v režimu Startup nastaví výchozí stav simulace a hned zapíše počáteční hodnoty. Pythonovské globální proměnné oil_temp, oil_pressure a direction jsou uloženy ve sdíleném prostoru skriptů, takže jsou periodickému skriptu dostupné:

# Startup script: initial state of the simulated sensors.
# The periodic script (simulate.py) ramps the values from here; the globals persist
# between script runs inside one runtime session.
oil_temp = 0.0          # degC, ramps 0 .. 150
oil_pressure = 1000.0   # kPa, ramps 1000 .. 2500
direction = 1           # +1 ramping up, -1 ramping down

# Publish the initial values right away, both to the slave registers and to the display copies.
OilTemp.EngValue = oil_temp
OilPressure.EngValue = oil_pressure
OilTempView.EngValue = oil_temp
OilPressureView.EngValue = oil_pressure

simulate.py v režimu Periodic s parametry period=2;unit=s posune každé 2 s teplotu o 1 °C a tlak o 10 kPa; na horní nebo dolní mezi obě veličiny nastaví na krajní hodnotu a otočí směr. Oba rozsahy mají 150 kroků, jeden průběh nahoru trvá 5 minut:

# Periodic script (period=2;unit=s): saw-tooth ramp of the simulated sensors.
# Temperature moves 1 degC per step between 0 and 150, pressure 10 kPa per step between
# 1000 and 2500, so one sweep up takes 5 minutes; at either end the direction flips.
#
# Every value is written twice on purpose:
#  - OilTemp / OilPressure are the Modbus slave variables. The slave serves their current
#    value to the master (holding registers 0 and 1). A script write never raises a UI event
#    on a device protocol, so these two are not shown in any control.
#  - OilTempView / OilPressureView are virtual variables. The Virtual Data protocol publishes
#    every script write to the UI, so the graph and signal controls follow them.
oil_temp += direction * 1.0
oil_pressure += direction * 10.0

if oil_temp >= 150.0 or oil_pressure >= 2500.0:
    oil_temp, oil_pressure, direction = 150.0, 2500.0, -1
elif oil_temp <= 0.0 or oil_pressure <= 1000.0:
    oil_temp, oil_pressure, direction = 0.0, 1000.0, 1

OilTemp.EngValue = oil_temp
OilPressure.EngValue = oil_pressure
OilTempView.EngValue = oil_temp
OilPressureView.EngValue = oil_pressure

Dvojí zápis je záměrný a představuje podstatu celé ukázky. Zápis do OilTemp a OilPressure pouze uloží hodnotu, kterou Modbus Slave při dalším dotazu předá masterovi — protokol zařízení z tohoto zápisu žádný vzorek pro UI nevytvoří. Zápis do OilTempView a OilPressureView naopak protokol Virtual Variables publikuje jako vzorek s časovou značkou zápisu, a právě ten vidí graf a signal controly. Kdyby skript zapisoval jen do slave proměnných, master by data četl správně, ale workspace by zůstal prázdný; kdyby zapisoval jen do virtuálních, ukazovalo by se všechno, ale master by četl nuly.

Workspace

Workspace Simulated sensors obsahuje tři controly, všechny navázané na virtuální kopie:

  • Graph „Simulated sensors (virtual copies of the slave registers)“ s časovým oknem 300 s (přesně jeden průběh rampy) a dvěma svislými osami s pevným rozsahem: vlevo 0–150 pro OilTempView (červená), vpravo 1000–2500 pro OilPressureView (modrá).
  • Dva Signal controly (obnova 250 ms) pro OilTempView a OilPressureView, jen pro čtení.

Co uvidíte a co vyzkoušet

  1. Spusťte runtime. Graf začne kreslit dvě pily: teplota od 0 °C nahoru, tlak od 1000 kPa nahoru; po 5 minutách se obě otočí.
  2. Připojte master na port 502, unit 2, a čtěte holding registry 0 a 1. Master ukazuje tytéž hodnoty jako signal controly a každé 2 s se posune o 1 a o 10. Slave hodnoty pouze poskytuje; mezi dotazy master nic neposílá a driver nečinnost na spojení za chybu nepovažuje.
  3. Odpojte síťový kabel na straně masteru (nebo masteru odpojte síť). Po 5 s bez provozu začne keepalive spojení ověřovat a za několik dalších sekund se v okně Logs objeví varování client … lost. Server se vrátí k naslouchání, driver zůstává spuštěný; korektní odpojení masteru by se zapsalo jako client … disconnected. Rampa běží dál — skript ani virtuální proměnné na klientovi nezávisí.
  4. Kabel znovu připojte. Master se automaticky znovu připojí a čte hodnoty, které mezitím dále rostly; na straně QInsight není třeba nic dělat.
  5. Připojte druhý master, zatímco první ještě drží spojení: nové příchozí spojení nahradí to stávající — TCP Server obsluhuje jednoho klienta. Odpojeného mastera tak nahradí nový bez čekání na keepalive.

Související kapitoly