Эта статья ни о чём, и немного о Modbus
Modbus - это низкоуровневый протокол позволяющий получить информацию от устройств. Протокол не предусматривает прерывания и ведомое устройство должно быть опрошено с какой-то периодичностью ведущим устройством. Информация может инкапсулирована во фрейм TCP/IP и информация может передаваться по сетям передачи данных - данная реализация Modbus TCP. В спецификации Modbus определено два типа данных, один бит и 16 битное слово. Данные организованы в четыре таблицы с 16 битной адресацией ячеек, адресация в таблицах начинается с 0.
- Discrete Inputs (1 бит, только чтение)
- Coils (1 бит, чтение и запись)
- Input Registers (16 бит, только чтение)
- Holding Registers (16 бит, чтение и запись)
Стандарт Modbus предусматривает ряд команд для получение от устройства регистров:
- 0x01 — чтение значений из нескольких регистров флагов (Read Coil Status)
- 0x02 — чтение значений из нескольких дискретных входов (Read Discrete Inputs)
- 0x03 — чтение значений из нескольких регистров хранения (Read Holding Registers)
- 0x04 — чтение значений из нескольких регистров ввода (Read Input Registers)
У каждого разработчика может быть своя реализация частей этого протокола, которые не регаламентированы стандартом. Запрос к ведомому устройству не гарантирует, то что устройство поддерживает какую-то команду и вернет данные. Так же необходимо помнить, что некоторые реализации подразумевают возможность записи, только после чтения. Или для простоты реализации возвращают состояние всех регистров сразу. В некоторых случаях, из возможных 65535 адресов, используется часть адресов, при этом начальный адрес имеет смещение например 30000 или 40000, то есть первый адрес в этом случае будет 30001 и 40001 соответственно.
Протокол modbus и python
Для работы с modbus в python есть несколько библиотек:
В интеренте я нашел сравнение библиотек. Однако для моих целей по опросу одного устройства, скорость работы и время декодирования регистров вообще не имели значние и я остановился на тех, у которых была хорошая документация. Я проверял 2 библитеки pymodbus и pymodbustcp.
Изучение устройства при помощи wireshark
Необходимо получать данные с MOXA Ethernet I/O Server E1210. После изучения запросов и ответов текущей системы мониторинга, я пришел к выводу, что всё здесь просто элементарно.
Запрос:
1515 12.686205000 192.168.100.3 192.168.100.2
Modbus/TCP 66 Query: Trans: 27967;
Unit: 1, Func: 2: Read Discrete Inputs
Modbus/TCP
Transaction Identifier: 27967
Protocol Identifier: 0
Length: 6
Unit Identifier: 1
Modbus
Function Code: Read Discrete Inputs (2)
Reference Number: 0
Bit Count: 1
Ответ:
1517 12.699150000 192.168.100.2 192.168.100.3
Modbus/TCP 64 Response: Trans: 27967;
Unit: 1, Func: 2: Read Discrete Inputs
Modbus/TCP
Transaction Identifier: 27967
Protocol Identifier: 0
Length: 4
Unit Identifier: 1
Modbus
Function Code: Read Discrete Inputs (2)
Byte Count: 1
Data: 00
После изучения текущей реализации стало понятно, что по modbus с I/O Server берутся данные о состоянии 15 входов, посредставом чтения нескольких дискретных входов, а именно 1 и 2 (0x00 и 0x01) значения которых соответствуют True или False. Смещения нет.
Установка pymodbustcp
Устанавливаем виртуальное окржение и модуль pymodbustcp.
$ mkdir project && cd project
$ virtualenv -ppython2.7 env
$ source env/bin/activate
(env)$ pip install pymodbustcp
...
Successfully installed pymodbustcp-0.1.1
Программа для опроса
Самый простой вариант, в моем случае, подключиться посмотреть данные и отключиться. Но правильно получать данные непрерывно при поддержке постоянного подключения.
$ nano modbus_test.py
# coding=utf8
from pyModbusTCP.client import ModbusClient
import time
import sys
def main():
ip_address='192.168.100.2'
c = ModbusClient()
if not c.host(ip_address):
print("host error")
if not c.port(502):
print("port error")
while True:
if c.is_open():
rr = c.read_discrete_inputs(0, 2)
# [False, True]
if not rr[0] and rr[1]:
print 'work'
sys.exit(0)
# [True, True]
elif rr[0] and rr[1]:
print 'not work'
sys.exit(1)
print '-'
sys.exit(-1)
else:
c.open()
if __name__ == "__main__":
main()
Я же запускаю этот скрипт по cron, проверяю пингуется или нет, затем статус по modbus.
$ nano modbus_test.sh
#!/bin/bash
IP='192.168.100.2'
if ping -q -c 2 -n $IP >/dev/null; then
echo $IP'; up' >> /var/log/messages;
if /project/env/bin/python /project/modbus_test.py; then
echo $IP'; work' >> /var/log/messages
else echo $IP'; not work' >> /var/log/messages; fi
else echo $IP'; down' >> /var/log/messages; fi
Для поддержки постоянного подключения мне кажется правильным было бы использование twisted с бесконечным циклом и проверкой этих данных.
Для прочтения по теме modbus
Очень неплохой ресурс с информацией о протоколе modbus
P.S. Надеюсь кому-то будет полезно.