PDA

Просмотр полной версии : C# NModbus4+ПЛК63



fillpackart
15.01.2016, 16:05
Здравствуйте. Мне понадобилось разработать приложение на C# для опроса ПЛК63 и сохранения данных в БД. С ПЛК я до этого не работал, да и вообще занимался только веб-разработкой до сего момента. Соответственно действовать приходиться наугад, и, по сути я вообще не понимаю, как с этим работать. Решил попробовать использовать библиотеку NModbus4.
Однако следующий код не работает:
private void button1_Click(object sender, EventArgs e)
{

var mbus = ModbusSerialMaster.CreateRtu(port);
MessageBox.Show((mbus.ReadHoldingRegisters(1, 0, 1)[0]).ToString()); - на этой строке выдает: Необработанное исключение типа "System.IO.IOException" в NModbus4.dll
Дополнительные сведения: Checksums failed to match 1, 3, 0 != 1, 3, 0, 0, 0
}
Нужна помощь либо в использовании NModbus4 либо описание и помощь по какому-нибудь альтернативному способу опроса ПЛК. Заранее спасибо.

Yegor
15.01.2016, 17:49
Какие-то левые нули от ПЛК идут что ли... Попробуйте другими готовыми мастерами поопрашивать тот же регистр той же функцией. Будет хотя бы понятно, куда двигаться.

fillpackart
18.01.2016, 12:36
Какими например? Я не совсем понимаю, ПЛК вообще у меня принял запрос или нет.

fillpackart
18.01.2016, 13:09
Попробовал использовать функцию при выключенном ПЛК, та же самая ошибка

Yegor
18.01.2016, 13:14
Код — в студию. Весь.

fillpackart
18.01.2016, 18:45
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Modbus.Device;
using System.Net.Sockets;
namespace DispetchTry0
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
port.Open();
if (!port.IsOpen)
MessageBox.Show("Порт не открыт");


}

private void button1_Click(object sender, EventArgs e)
{

var mbus = ModbusSerialMaster.CreateRtu(port);
MessageBox.Show((mbus.ReadHoldingRegisters(00, 3, 1)[1]).ToString());
}
}
}

Yegor
19.01.2016, 04:19
Откуда берётся port?

fillpackart
19.01.2016, 17:52
private System.IO.Ports.SerialPort port; Объект стандартного класса SerialPort.

alexx751
19.01.2016, 19:35
port настроить надо, насколько помню. В VS в справке все это есть.

Yegor
19.01.2016, 20:26
Пхххх.... Ну проект тогда сюда целиком что ли. И настройки используемого последовательного порта ПЛК из кодесиса тоже.

Kostennikov
20.01.2016, 13:34
в общем я на шарпе чуток работал. я собственно работал с tcp modbus. писал сам без библиотек.







//Сообщение для инициализации чтения чтение регистра функция 3 или 4 (4 функция работает только на чтение проверялось с целыми числами)

ushort startAddress = 0;
short length = 6;
short id = 1;

byte[] msg = new byte[12];

byte[] _id = BitConverter.GetBytes((ushort)id);
msg[0] = _id[1]; // я так понял
msg[1] = _id[0]; // это идентификатор сообщения слейв присылает его обратно в том же виде
msg[2] = 0; // на данный момент не используется по описанию протокола
msg[3] = 0; // на данный момент не используется по описанию протокола
msg[4] = 0; // длинна сообщения протокола модбас т.е то что следует после этого байта
msg[5] = 6; // длинна сообщения протокола модбас т.е то что следует после этого байта
msg[6] = 1; // адрес слейв как в модбас рту
msg[7] = 4; // код функции 3 или 4 для чтения. 6 код для записи
byte[] _adr = BitConverter.GetBytes((short)startAddress);
msg[8] = _adr[1]; // адрес регистра
msg[9] = _adr[0]; // для чтения
byte[] _length = BitConverter.GetBytes(length);
msg[10] = _length[1]; // количество регистров
msg[11] = _length[0]; // для чтения


это справедливо для 3, 4 и 6 функций и только для данных word. для данных dword и float нужно удвоить количество считываемых регистров и записываемых соответственно. 6 функция это запись одного регистра хранения. вместо количества считываемых регистров пишем в регистр

msg[10] = _length[1]; // данные которые надо записать
msg[11] = _length[0]; // данные которые надо записать


Функции множественного чтения записи и чтение записи битов не делал тк не нужно. в ближайшем плане это. писал сам с 0 для своего развития и ради интереса.

для сериал порта это будет выглядеть практически также

msg[0] // адрес слейв
msg[1] // код функции

byte[] _adr = BitConverter.GetBytes((short)startAddress);
msg[2] = _adr[1]; // адрес регистра
msg[3] = _adr[0];
byte[] _RegisterNum = BitConverter.GetBytes(RegisterNum);
msg[4] = _RegisterNum[1]; // количество регистров
msg[5] = _RegisterNum[0]; // для чтения
byte[] _length = BitConverter.GetBytes(length);
msg[6] = _length[1]; // длинна сообщения (кажется всего)
msg[7] = _length[0]; // длинна сообщения
byte[] _CRC = BitConverter.GetBytes(CRC);
msg[8] = _CRC[1]; // CRC16
msg[9] = _CRC[0]; // CRC16 контрольная сумма

Могу ошибаться написал по памяти))) Дa и еще важный момент старший и младший байт... мой пример работает на плк 100 если не читается нужно поставить native mode или tracert mode. для сериал порта

И еще. Библиотеки библиотеками но если это не понять то библиотеки эти бесполезны
Примеров по работе с сериал портом и сокетами валом.

Если нужно подробнее пишите в личку (на почту сообщение хоть придет. а то я форум не часто читаю)
За код во вложении сильно не бейте написано за 10 минут но 100% рабочий.

Это проще чем кажется..

fillpackart
20.01.2016, 17:01
порт настроен. Вот нагенеренный студией код
namespace DispetchTry0
{
partial class Form1
{
/// <summary>
/// Обязательная переменная конструктора.
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// Освободить все используемые ресурсы.
/// </summary>
/// <param name="disposing">истинно, если управляемый ресурс должен быть удален; иначе ложно.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Код, автоматически созданный конструктором форм Windows

/// <summary>
/// Требуемый метод для поддержки конструктора — не изменяйте
/// содержимое этого метода с помощью редактора кода.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.port = new System.IO.Ports.SerialPort(this.components);
this.button1 = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// port
//
this.port.BaudRate = 19200;
//
// button1
//
this.button1.Location = new System.Drawing.Point(13, 13);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(13, 43);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(21, 13);
this.label1.TabIndex = 1;
this.label1.Text = "log";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(406, 367);
this.Controls.Add(this.label1);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
this.PerformLayout();

}

#endregion

private System.IO.Ports.SerialPort port;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Label label1;
}
}

Kostennikov
20.01.2016, 17:05
Приложи проект полностью в архиве... посмотрю сегодня , завтра... и проект для плк

fillpackart
20.01.2016, 18:08
Ссыль на проект - https://onedrive.live.com/redir?resid=57F6DAF633928C5E!908&authkey=!AB6jpUg6tp_xHR4&ithint=file%2c7z. К проекту на плк у меня доступа нет, знаю только, что там добавлен RS-485(FIX). Настройка порта задается с ЖКИ

Kostennikov
21.01.2016, 11:14
Как говорил ув. Yegor. А откуда берется port? А порт у вас не инициализирован собственно. у вас только установлена скорость обмена. несомненно порт откроется. а остальное по умолчанию следовательно запустится с системными настройками ОС.
у вас:


port.Open();
if (!port.IsOpen)
MessageBox.Show("Порт не открыт");

какой порт ты открыли? с какой скоростью с ним работать? сколько бит данных? стоповые биты?
должно быть примерно так:



_serialPort = new SerialPort();
_serialPort.PortName = SetPortName("COM6");
_serialPort.BaudRate = SetPortBaudRate(9600);
_serialPort.Parity = SetPortParity(Parity.None);
_serialPort.DataBits = SetPortDataBits(8);
_serialPort.StopBits = SetPortStopBits(StopBits.One);
_serialPort.ReadTimeout = 500;
_serialPort.WriteTimeout = 500;
_serialPort.Open();
if (! _serialPort.IsOpen)
MessageBox.Show("Порт не открыт");

private void button1_Click(object sender, EventArgs e)
{

var mbus = ModbusSerialMaster.CreateRtu(port);
MessageBox.Show((mbus.ReadHoldingRegisters(0, 3, 1)[1]).ToString());
}

//или в одну строчку если жестко надо задать

SerialPort _serialPort = new SerialPort( ″COM1″ , 9600, Parity.None, 8, StopBits.One);
_serialPort.Open();
if (! _serialPort.IsOpen)
MessageBox.Show("Порт не открыт");

private void button1_Click(object sender, EventArgs e)
{

var mbus = ModbusSerialMaster.CreateRtu(port);
MessageBox.Show((mbus.ReadHoldingRegisters(0, 3, 1)[1]).ToString());
}



а вы не задали настройки порта поэтому и не читается все собственно.
не факт что у вас системные настройки стоят такие же как у плк к которому подключаетесь.
ну и не забывайте ошибки порта обрабатывать иначе замучаетесь и не будете знать в чем ошибка)).

Задайте настройки порта у плк и точно такие же пропишите в программе один в один. не забудте указать ком порт в программе через который идет общение с плк.

В общем инициализируйте порт попробуйте.
За работу библиотеки ни чего не скажу... как там читается - пишется.
Я пробую без библиотек ибо это интересней.


п.с. За ошибки ответственности не несу)). Я пытаюсь вас направить.
вот ссылочка https://msdn.microsoft.com/ru-ru/library/system.io.ports.serialport(v=vs.110).aspx там все про ком порт. Ну и гугл помогает.



Я кстати выше писал про порядок байт возможно в плк надо поменять режим с native mode на tracert mode. но если не имеете доступа к плк, то библиотеке надо указать порядок байт если она это умеет. если нет то пишите функции которые необходимы сами. пример я приводил. прикладываю проект буржуйского коллеги если не заработает то скорее всего у вас именно что то с порядком байт. ну или что то со связью

также попробуйте утилиту peakhmi. попробуйте с помощь нее подключиться к плк. вам нужно установить serial master. там можно выставить порядок байт если заработает то надо смотреть библиотеку как она работает

может в плк не настроен слейв, а мастер?
(если настроен мастер то вам не писать в порт надо, а читать то что из него приходит и отвечать что прочитали?(если библиотека не умеет отвечать. может функция не та используется?))

может адрес слейв не 1?

Вообще что нибудь работает с плк по этому интерфейсу? панель или другой девайс, программа.

да кстати если делали настройки в плк скорость и все такое возможно его надо перезагрузить малоли как там реализовано. может поэтому у вас не получалось.

fillpackart
22.01.2016, 11:01
https://onedrive.live.com/redir?resid=57F6DAF633928C5E!912&authkey=!ACIq96EW6ajWep8&v=3&ithint=photo%2cpng - порт настроен в конструкторе. Есть готовая прога, которая успешно опрашивает ПЛК по этому интерфейсу. Адрес слейва я програмно перебирал даже, как и numberOfPoints... Начсет библиотеки, я пытаюсь использовать её, потому, что даже пытаться разобраться в принципах работы этого протокола не хочу совершенно, я ведь даже не знаю, что за прога в ПЛК. Мне просто нужно вытащить данные, засунуть их в бд и разработать веб-приложения для визуализации всего этого дерьма. В общем, проблема не в настройках порта, они выставлены один в один, как на ПЛК

Kostennikov
22.01.2016, 13:00
Ну а что стоит обратиться к тому чья эта прога и узнать список регистров может вы пытаетесь читать то чего там нет))). Карта регистров есть(мне попадался как то плк там такой круговорот регистров был...)? И не работает ли эта прога одновременно с вашей?


Вы сами то не рассказываете что да как. все возможное я уже перебрал.
Давайте от конкретных значений отталкиваться.

Адрес плк. какой, номер регистра, его тип word, float. какой функцией читаете?

fillpackart
22.01.2016, 13:21
Те, чья прога с нами работать отказываются. Карты регистров нет. Я, честно говоря, даже не понимаю, что это такое. Адрес ПЛК 1. Функции все из библиотеки NModbus перебрал, в нелепой попытке прочитать с устройства хоть что-нибудь. А рассказываю я вам всё, что знаю сам, просто я почти ничего не знаю, в этом то вся и проблема. Спасибо вам кстати большое за то, что помогаете.

Kostennikov
22.01.2016, 14:18
Тогда вам надо чем то поснифать трафик с порта с той программы которая с плк работает. Чем не подскажу(смотрите профильные форумы типа киберфорума). желательно увидеть лог обмена. Или как вариант подключиться терминалом Putty например и попробывать имитировать запросы. второй вариант это пишите сначала программу которая будет сама перебирать.

Есть ли в той программе значение целочисленное которое статично или есть возможность что бы оно не менялось какое-то время, как вариант можно поставить плк в режим стоп(не знаю как с портом отключится или нет но по тсп в режиме стоп мой плк отвечает на запросы но значения заморожены. как раз то что на надо)?
Если есть то пробуйте 3й или 4й функцией.

т.е.
пишите в порт по адресу 1, функцией 3 или 4 по адресу регистра 1
читаете ответ
если ответ равен тому значению которое вам 100500% известно то поздравляю вас вы попали туда куда надо. запомните адрес регистра
если нет то увеличиваем адрес регистра на 1 и повторяем процедуру пишем читаем сравниваем.
Сразу говорю значение в плк известное вам не должно меняться во время этой процедуры
думаю с циклом то справитесь и со сравнением?


например у нас известное значение 100 то цикл от 0 до скажем будем читать 40 регистров значит до 40
шлем запрос плк
читаем
если ответ от плк равен 100 то запоминаем адрес
конец цикла


я бы так сделал

а дальше таким же методом нашел все остальные нужные регистры.

Я так понял плк отвечает что то. но вы не знаете что это.

Kostennikov
22.01.2016, 14:25
Те, чья прога с нами работать отказываются. Карты регистров нет. Я, честно говоря, даже не понимаю, что это такое. Адрес ПЛК 1. Функции все из библиотеки NModbus перебрал, в нелепой попытке прочитать с устройства хоть что-нибудь. А рассказываю я вам всё, что знаю сам, просто я почти ничего не знаю, в этом то вся и проблема. Спасибо вам кстати большое за то, что помогаете.

список регистров и их описание например
адрес регистра 40002 тип word (количество циклов розлива)
адрес регистра 40003 тип word (производительность в час)

табличка подобная .

Kostennikov
22.01.2016, 14:39
_serialPort = new SerialPort();
_serialPort.PortName = SetPortName("COM6");
_serialPort.BaudRate = SetPortBaudRate(9600);
_serialPort.Parity = SetPortParity(Parity.None);
_serialPort.DataBits = SetPortDataBits(8);
_serialPort.StopBits = SetPortStopBits(StopBits.One);
_serialPort.ReadTimeout = 500;
_serialPort.WriteTimeout = 500;
_serialPort.Open();
if (! _serialPort.IsOpen)
MessageBox.Show("Порт не открыт");
else
var mbus = ModbusSerialMaster.CreateRtu(port);
short i:=0 // это будет адрес регистра
while (i < 10000) // количство запросов для теста.
{
if (i= mbus.ReadHoldingRegisters(1, i, 1)) /// или как там в библиотеке прицесли к целочисленному значению
MessageBox.Show(i);
i++;
}


вот как то так адаптируйте код сами для перебора я условно написал трудно с паскаля резко перейти на с# я зам его не так давно изучать стал т.к. другой альтернативы нет.
Надеюсь мысль уловили


Сейчас только пришло в голову. а адрес регистра может не 1 а 40001 быть для 2 - 40002 и так же далее по аналогии

fillpackart
22.01.2016, 16:18
так у меня на функции mbus.ReadHoldingRegisters(0, 1, 1) прога повисает

Kostennikov
22.01.2016, 16:31
так у меня на функции mbus.ReadHoldingRegisters(0, 1, 1) прога повисает

должно быть mbus.ReadHoldingRegisters(1, 1, 1)

первая 1 это адрес слейв у плк я так понял он 1
вторая 1 это адрес регистра
третья 1 это количество регистров

в цикле надо увеличивать 2 единичку.
т.е.

mbus.ReadHoldingRegisters(1, i, 1)
i увеличивается в цикле

поправил пред мой комент

еще раз напоминаю я с библиотекой NMobbus не работал. где указывается какой параметр в запросе полностью на ваших плечах. я стараюсь идею подкинуть. пробуйте спрашивайте конкретно. ошибку открытия порта обрабатываете? может порт не открывается. и это не будет работать если плк работает с другой программой.
и да она зависнет на какойто момент она же в цикле))) на какое время я не могу сказать. но пока цикл не кончится будет висеть

fillpackart
22.01.2016, 17:12
пока пробую вот так:
var mbus = ModbusSerialMaster.CreateRtu(port);
label1.Text = "log:\n";
for (ushort i = 1; i < 50000; i++)
{
try
{
MessageBox.Show((mbus.ReadHoldingRegisters(1, i, 1)[1]).ToString());
label1.Text += "try #" + i + " succes!!!\n";
MessageBox.Show("Yes!");
}
catch { label1.Text += "try #"+i+" failed\n"; }
}

Kostennikov
22.01.2016, 19:46
можно попробывать от 1 до 100 и от 40000 до 40100. Хотя кто их знает как они там слейв делали... Хоть напиши потом что вышло?

capzap
23.01.2016, 07:14
Те, чья прога с нами работать отказываются. Карты регистров нет. Я, честно говоря, даже не понимаю, что это такое. Адрес ПЛК 1. Функции все из библиотеки NModbus перебрал, в нелепой попытке прочитать с устройства хоть что-нибудь. А рассказываю я вам всё, что знаю сам, просто я почти ничего не знаю, в этом то вся и проблема. Спасибо вам кстати большое за то, что помогаете.

я правильно понимаю, что Вы о плк63 говорите? Если да, тогда вот ссыль http://www.owen.ru/uploads/rp_plk63.73.pdf где в таблице А есть номера регистров, вторая и третья колонки, простой перебор от нуля не один раз будет уходить в таймаут

fillpackart
23.01.2016, 14:06
Так, вроде получилось, ПЛК начал что-то отвечать. Я правда не понимаю, что, но это уже дело техники) Спасибо вам за помощь! Кст, что означает параметр "количество регистров"?

capzap
23.01.2016, 14:21
Так, вроде получилось, ПЛК начал что-то отвечать. Я правда не понимаю, что, но это уже дело техники) Спасибо вам за помощь! Кст, что означает параметр "количество регистров"?

за один раз прочитать несколько, чтоб сэкономить время например

Kostennikov
25.01.2016, 10:08
я правильно понимаю, что Вы о плк63 говорите? Если да, тогда вот ссыль http://www.owen.ru/uploads/rp_plk63.73.pdf где в таблице А есть номера регистров, вторая и третья колонки, простой перебор от нуля не один раз будет уходить в таймаут

Так надо эти таймауты выдержать. По опыту плк иногда в перезагрузку уходит. но если между запросами таймаут поставить или читать 100% известный регистр после неудачи то такого не будет. Я так понял счетчик ошибок после успешного чтения обнуляется или как там в плк задумано. Да долго зато надежно.