При изучении протокола Modbus (да и вообще, в принципе, наверное, любого другого протокола), чтобы понять структуру посылаемых данных, очень полезно самому составить эту структуру и отослать ее устройству которое будет принимать эти данные. И для этих целей очень удобно пользоваться терминалом - где ты можешь посылать запросы и принимать ответы, и видеть все это на своем экране. Если почитать про протокол Modbus RTU, то там написано что структура посылки выглядит следующим образом:
{MbAddr} {CmdCode} {AddrH} {AddrL} {QntH} {QntL} {CRCL} {CRCH}
То есть внутри каждой фигурной скобки находится 1 байт, который может принимать значение от 0 до 255 или в шестнадцатеричном формате FF
Таким образом чтобы сформировать запрос к ведомому устройству, нужно через терминал отправить что-то вроде этого 01 03 00 14 00 02 84 0F и если запрос сформирован верно то в ответ должно прийти что-то тоже вполне осмысленное, хотя и состоящее также из наборов байт
В этом наборе байт по порядку 01 - адрес ведомого устройства, 03 - код команды на чтение регистров хранения (Holding Register или Analog Output), 00 14 - адрес регистра с которого начинается чтение, 00 02 - количество читаемых регистров, 84 0F - это контрольная сумма
Как видим в ответ пришла телеграмма, тоже состоящая из байтов, и в ней выделенное значение при переводе в формат числа с плавающей точкой равно ожидаемому 0,1121 - это значение тока которое измеряет наше устройство. В конце есть так же контрольная сумма и она совпадает с ожидаемой. В данном случае для проверки использовался терминал Termite, который предназначен для тестирования устройств с Modbus RTU. Эта программа автоматически считает контрольную сумму, что очень удобно.
Сейчас же мы будем делать свой терминал для Modbus TCP, конечно же не такую навороченную и красивую программу, но зато вполне функциональную и при этом еще лучше поймем принцип этого протокола. Очень удобно и классно в этом случае, что в протоколе Modbus TCP не предусмотрена контрольная сумма, и все дело в том что работает он поверх TCP, в котором уже есть своя контрольная сумма и потому нет смысла дублировать. И очень удобно для нас и то, что мастер сети хоть и является мастером, но по отношению к ведомому устройству он все же клиент, а ведомое устройство по отношению к мастеру это сервер. Соответственно чтобы посылать Modbus запросы нам нужно написать обычный TCP клиент, который будет посылать нужную структуру байтов, и выводить в терминал полученную от сервера (ведомого устройства) структуру байт в окно. Проще всего это сделать в виде консольного приложения и в гугле находится очень много примеров для таких приложений, например у Microsoft.
Я скопировал данный код в блокнот, немного подправил и скомпилировал его через командную строку, в итоге увидел что запросы отправляются и принимаются
На этом скрине видно, что данные отправляются и принимаются уже в шестнадцатеричном формате. Это стало возможным путем преобразования отправляемых данных из hex string to byte array. А принимаемые данные наоборот из byte array to hex string. Первые ответы со stackoverflow отлично заработали. На скрине также видно, что строка дополняется нулями, и происходит это потому, что для получения ответа выделяется буфер с конкретным размером, и незаполненная данными часть массива остается заполненной нулями.
Теперь самое интересное - нужно дополнить программу так чтобы отправлять на устройство телеграмму в соответствии с протоколом ModbusTCP, а делается это тоже очень просто. В описании протокола сказано, что он состоит из 2 частей: заголовок и данные. Собственно заголовок не меняется, а данные нужно вводить с клавиатуры, потом все это сложить вместе и отправить ведомому устройству. В результате у меня появился вот такой код (постарался расписать комментарии):
Static void Main(string[] args) {
Console.WriteLine("Введите IP адрес ведомого устройства. Порт по умолчанию 502");
string server = Console.ReadLine();
int port = 502;
while (true) {
// Modbus TCP frame format = MBAP Header + PDU (Modbus Application Header + Protocol Data Unit)
Console.WriteLine("Введите запрос для ведомого устройства");
Console.WriteLine("Формат такой же как ModBus RTU, но без контрольной суммы, т.е. {MbAddr} {CmdCode} {AddrH} {AddrL} {QntH} {QntL}");
string MBAP = "0001 0000 0006";
// 0001 - Идентификатор транзакции Transaction Identifier
// 0000 - Идентификатор протокола Protocol Identifier
// 0006 - Длина (6 байтов идут следом) Message Length
string PDU = Console.ReadLine();
// Считываем ввод PDU с клавиатуры. Формат такой же как ModBus RTU, но без контрольной суммы:
// {MbAddr} {CmdCode} {AddrH} {AddrL} {QntH} {QntL}
// например - 01 03 00 01 00 02, где 01- адрес ведомого устройства
// 03 - код команды чтение регистров хранения (Holding Register или Analog Output),
// 00 01 - адрес регистра с которого начинается чтение
// 00 02 - количество читаемых регистров
string SendToDevice = MBAP + PDU;
Connect(server, port, SendToDevice.Replace(" ", ""));
}
}
Для проверки работоспособности использовалась программа эмулятор Modbus-Slave и на первом скрине хорошо видно, что запрашиваемые данные которые лежат в регистрах Slave устройства соответствуют тем что мы получаем в терминале. Конечно же у этого терминала очень много недостатков - нет валидации ввода, и при любой внештатной ситуации он будет сразу же ломаться, но тем не менее он все же работает и выполняет возложенные на него функции. Полнай код программы лежит в моем гитхабе: ModbusTCP-terminal
Ну и еще кое-что. Эту программу можно довольно легко изменить и перекомпиллировать у себя на компьтере без установки тяжелой Visual Studio, нужет только .NET Framework и блокнот (кторое скорее всего уже установлены у вас). Сохранить весь код программы в текстовый фал с расширением .cs и запустить команду компиляции. Для удобства я добавил в контекстное меню .cs файлов команду для компиляции при помощи реестра Windows:
Содержимое файла compiler.reg
Windows Registry Editor Version 5.00Чтобы сделать так же, нужно сохранить текст выше в текстовый файл, переименовать его в compiler.reg и запустить. После этого у вас должно появится такое же контекстное меню что и у меня, и нажав на него, можно быстро скомпилировать программу Modbus терминала, а редактировать ее можно при помощи блокнота.
[HKEY_CLASSES_ROOT\cs_auto_file\shell\Compile]
"MUIVerb"="Скомпилировать C#"
"Icon"="shell32.dll,80"
[HKEY_CLASSES_ROOT\cs_auto_file\shell\Compile\command]
@="cmd /k C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe -nologo \"%1\""
Windows Registry Editor Version 5.00
ОтветитьУдалить[HKEY_CLASSES_ROOT\.cs\shell\Compile]
@="Compile C#"
"Icon"="shell32.dll,80"
[HKEY_CLASSES_ROOT\.cs\shell\Compile\command]
@="cmd /k C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe -nologo \"%1\""
[HKEY_CLASSES_ROOT\SystemFileAssociations\.cs\shell\Compile]
@="Compile C#"
"Icon"="shell32.dll,80"
[HKEY_CLASSES_ROOT\SystemFileAssociations\.cs\shell\Compile\command]
@="cmd /k C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe -nologo \"%1\""