Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.67/3: Рейтинг темы: голосов - 3, средняя оценка - 4.67
1 / 1 / 0
Регистрация: 24.12.2014
Сообщений: 14

SerialPort получение и отправка данных

07.11.2023, 11:27. Показов 991. Ответов 10
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте!

Суть проблемы, есть электрооборудование, которое подключено к комьютеру по интерфейсу RS485.
Устройство необходимо постоянно мониторить, для этого на устройство нужно отправить команду и получить ответ от устройства.
Таких команд (различных) множество, в данный момент 39шт.

Проблемы как таковой не существовало, пока запросы были одиночные, т.е. отправил один запрос - получил один ответ.

С отправкой множества команд возникли сложности, я создаю массив исходных команд, далее создаю цикл в котором я перебираю значения массива и формирую пакет для отправки. Дальше начинаются сложности, идея была такая: в цикле перебора массива, итарация первая i=0 - отправляю первое значение массива в компорт, получаю первый ответ из компорта, записываю его в массив входящих данных, инкремент i++, далее следующая итерация цикла. Но так, конечно же, не работает. Я не понимаю как считать и записать данные в ходе цикла, т.к. они приходят в событии DataReceived, которым я никак не управляю. Полный перебор цикла отправляет 39 пакетов в компорт не дожидаясь ответа, оборудование успевает что-то попытаться ответить и зависает.
Единственное, что пришло на ум, добавить зедержку по времени, для того чтобы чтение успело пройти thread.sleep(500); но пока я это решение не успел протестировать.
Что-то я конкретно запутался, прошу помощи.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
namespace TestE48_RS485
{
    public partial class Form1 : Form
    {
        int dataINLength;
        int[] dataInDec;
        string dataIN;
 
        public Form1()
        {
            InitializeComponent();
        }
 
        private void cBoxCOMPORT_DropDown(object sender, EventArgs e)
        {
            string[] ports = SerialPort.GetPortNames();
            cBoxCOMPORT.Items.Clear();
            cBoxCOMPORT.Items.AddRange(ports);
        }
 
        private void btnOpenClose_Click_Click(object sender, EventArgs e)
        {
            try
            {
                if (serialPort1.IsOpen == false)
                {
                    //Настройки com ports из описания протокола
                    serialPort1.PortName = cBoxCOMPORT.Text;
                    serialPort1.BaudRate = 9600;
                    serialPort1.DataBits = 8;
                    serialPort1.StopBits = (StopBits)Enum.Parse(typeof(StopBits), "One");
                    serialPort1.Parity = (Parity)Enum.Parse(typeof(Parity), "None");
                    serialPort1.ReadTimeout = 500;
                    serialPort1.Open();
 
                    lblStatusCom.Text = "Порт ОТКРЫТ";
                    btnOpenClose.Text = "Закрыть порт";
                }
 
                else
                {
                    serialPort1.Close();
                    lblStatusCom.Text = "Порт ЗАКРЫТ";
                    btnOpenClose.Text = "Открыть порт";
                }
            }
            catch (Exception err) 
        { MessageBox.Show(err.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); }
        }        
 
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            List<int> dataBuffer = new List<int>();
 
            while (serialPort1.BytesToRead > 0)
            {
                try
                {
                    dataBuffer.Add(serialPort1.ReadByte());
                }
                catch (Exception error) { MessageBox.Show(error.Message); }
            }
 
            dataINLength = dataBuffer.Count();
            dataInDec = new int[dataINLength];
            dataInDec = dataBuffer.ToArray();            
 
            this.Invoke(new EventHandler(ShowData));
        }
 
        private void ShowData(object sender, EventArgs e)
        {
            richTextBox1.Text += "Входящий фрейм : \n"; 
 
            for (int i = 0; i < dataInDec.Length; i++)
            {
                richTextBox1.Text += dataInDec[i].ToString("X2") + " "; // Вывод полученных данных
 
        //Дбавить массив для накопления данных из dataInDec[i]
            }
        }
        
        private void button2_Click(object sender, EventArgs e)
        {
            //Массив данных для отправки запроса 
            string[][] strArray_39_In_SF = new string[39][];// [][];
            strArray_39_In_SF[0] = new string[12] { "7E", "30", "31", "34", "30", "30", "31", "30", "30", "38", "36", "0D" };
            strArray_39_In_SF[1] = new string[16] { "7E", "30", "31", "34", "30", "30", "35", "30", "32", "30", "3A", "32", "38", "36", "38", "0D" };
            ...
            strArray_39_In_SF[38] = new string[12] { "7E", "30", "31", "34", "32", "3C", "32", "30", "30", "39", "35", "0D" };
 
            byte[][] btArrayDataTest = new byte[strArray_39_In_SF.Length][]; 
            
            richTextBox1.Text = "";
 
 
            if (serialPort1.IsOpen)
            {
                try
                {
                    for (int i = 0; i < strArray_39_In_SF.Length; i++)
                    {
                        btArrayDataTest[i] = new byte[strArray_39_In_SF[i].Length];                   
                                            
                        for (int j = 0; j < strArray_39_In_SF[i].Length; j++)
                        {
                            btArrayDataTest[i][j] = Convert.ToByte(strArray_39_In_SF[i][j], 16); //Массив для отправки в утройство
                            richTextBox1.Text += btArrayDataTest[i][j].ToString("X2") + " "; // Вывод отправляемых данных
                            serialPort1.Write(btArrayDataTest[i][j]); //Отправка в утройство
 
                thread.sleep(500); // Задержка для чтения данных
                        }
                        richTextBox1.Text += "\n";                         
                    }
                }
                catch (Exception error) { MessageBox.Show(error.Message); }
            }
            else { richTextBox1.Text = "ВНИМАНИЕ!!! \n" + "COM Port закрыт"; }
        }
    }
}
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
07.11.2023, 11:27
Ответы с готовыми решениями:

Отправка массива в serialPort
Здравствуйте, подскажите пожалуйста. Программа создаёт int mas01={82,164,73,146,37,74,148,41} и мне это массив надо отправить в serialPort,...

Метод Post, получение данных со страницы и отправка
Здравствуйте, есть код страницы &lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;...

Получение капчи с сайта и отправка данных в форму на сайте
Помогите начинающему программисту. Допустим нужна программа для регистрации тут. Как это (получение капчи и отправка данных в форму)...

10
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3430 / 2749 / 575
Регистрация: 04.09.2018
Сообщений: 8,628
Записей в блоге: 3
07.11.2023, 11:44
Цитата Сообщение от NSluggard Посмотреть сообщение
что пришло на ум, добавить зедержку по времени, для того чтобы чтение успело пройти thread.sleep(500);
Хреновый способ. Лучше передавать новый пакет с командой после того, как отработал DataReceived. Но может быть такое, что устройство не ответило (мало ли что, там, неверная команда или данные побились...). Тогда отправка новых команд должна происходить по некоему таймауту, по таймеру.

Добавлено через 3 минуты
Цитата Сообщение от NSluggard Посмотреть сообщение
которым я никак не управляю.
Им и не нужно управлять. Оно само сообщит о приеме новой пачки данных. Вот тут то и нужно сперва их куда-то записать или что ты там с ними делаешь, а после сразу отправить новую команду. И массив ты выбрал неудачного типа. Вообще, лучше список примени.
0
1 / 1 / 0
Регистрация: 24.12.2014
Сообщений: 14
07.11.2023, 11:51  [ТС]
Лучше передавать новый пакет с командой после того, как отработал DataReceived
Согласен. Не понимаю, как это в цикле сделать.
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
07.11.2023, 11:56
NSluggard, а протокол-то как называется?
0
1 / 1 / 0
Регистрация: 24.12.2014
Сообщений: 14
07.11.2023, 12:00  [ТС]
System Power Monitoring Protocol
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3430 / 2749 / 575
Регистрация: 04.09.2018
Сообщений: 8,628
Записей в блоге: 3
07.11.2023, 12:03
Цитата Сообщение от NSluggard Посмотреть сообщение
как это в цикле сделать
Цикл здесь не нужен. Достаточно завести глобальный счетчик и икрементить его по каждой отправке новой посылки. Дошли до последней - обнулили. И так по кругу.
1
1 / 1 / 0
Регистрация: 24.12.2014
Сообщений: 14
07.11.2023, 12:31  [ТС]
wizard41, спасибо за наводку. Если не затруднит, можете какой-то пример прислать?

Я правильно понял, что событие нужно в DataReceived вызывать? Типа if глобальный счетчик?
Крайне интересная мысль. Если я правильно понял, пример наверное не нужен.

А почему массив выбран неудачно?
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3430 / 2749 / 575
Регистрация: 04.09.2018
Сообщений: 8,628
Записей в блоге: 3
07.11.2023, 12:58
Лучший ответ Сообщение было отмечено NSluggard как решение

Решение

Цитата Сообщение от NSluggard Посмотреть сообщение
Если не затруднит, можете какой-то пример прислать?
Студии нет, пишу в блокноте, но принцип примерно таков:
C#
1
2
3
SerialPort port;
bool flag = false;  // Флажок "зацикленности"
int counter = 0;
C#
1
2
3
4
5
6
7
8
// Список команд для отправки в устройство...
List<byte[]> commands = new List<byte[]>()
{
    new byte[] { 0x7e, 0x30, 0x31, 0x34, 0x30, 0x30, 0x31, 0x30, 0x30, 0x38, 0x36, 0x0d },
    new byte[] { 0x7e, 0x30, 0x31, 0x34, 0x30, 0x30, 0x35, 0x30, 0x32, 0x30, 0x3a, 0x32, 0x38, 0x36, 0x38, 0x0d },
    // .....
    new byte[] { 0x7e, 0x30, 0x31, 0x34, 0x32, 0x3c, 0x32, 0x30, 0x30, 0x39, 0x35, 0x0d }
};
C#
1
2
3
4
5
6
// Метод отправляющий очередную посылку...
private void Send()
{
    port.Write(commands[counter], 0, commands[counter].Length);
    if (++counter > commands.Count) counter = 0;
}
C#
1
2
3
4
5
6
7
8
9
10
private void button2_Click(object sender, EventArgs e)
{
    flag = !flag;
 
    // Инициализация первой отправки команды...
    if (flag)
    {
        Send();
    }
}
C#
1
2
3
4
5
6
7
8
9
10
11
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    // Здесь прием данных от устройства...
    // Что-то с ними делаем...
 
    // Отправка новой команды зацикленно!...
    if (flag)
    {
        Send();
    }
}
Добавлено через 5 минут
Т.е. после каждой отправки мы сдвигаемся на одну команду вперед, пока не дойдем до конца списка. После чего начинается все сначала. Пока не выключим флаг передачи.
Если ответа от устройства не будет, то передачи не произойдет. Вот тут нужен таймер, чтобы в этом случае по нему отправить очередную посылку...

И еще - не проработан вопрос гарантии полноценного приема. Ответ от устройства может приходить "рваным", т.е. весь пакет может прийти и в двух ответах. Но это уже другая песня..
1
1 / 1 / 0
Регистрация: 24.12.2014
Сообщений: 14
07.11.2023, 23:47  [ТС]
wizard41, огромное спасибо за помощь, заработало!

По поводу отсутствия ответа и таймера на проверку я понял, примерно так и думал.
По поводу рваного ответа тоже думал проверку поставить на контрольную сумму и таймаут на ожидание ответа, правда из-за таймаута скорость упадёт.
Я так понял, что в рваных пекетах может скрываться другая угроза, типа коллизии, я когда ранее тестил неправильную отправку запросов хорошо подвесил несколько раз устройство.
1
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3430 / 2749 / 575
Регистрация: 04.09.2018
Сообщений: 8,628
Записей в блоге: 3
08.11.2023, 01:25
NSluggard, всегда радуюсь понимающим слушателям. С остальными вопросами разберемся завтра.
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3430 / 2749 / 575
Регистрация: 04.09.2018
Сообщений: 8,628
Записей в блоге: 3
08.11.2023, 13:16
Цитата Сообщение от NSluggard Посмотреть сообщение
поставить на контрольную сумму
Если протокол содержит CRC, то нужно использовать его для идентификации корректных посылок.
Цитата Сообщение от NSluggard Посмотреть сообщение
Я так понял, что в рваных пекетах может скрываться другая угроза, типа коллизии
Да, что-то типа того. Если бы посылки были фиксированной длины, то можно было просто принимать любые входящие пакеты до того момента, как буфер не заполнится по этой фиксированной длине.
При посылках переменной длины используется терминатор (один из способов) - некий символ, означающий конец пакета. Соответственно, принимать данные пока не встретится этот символ.
Для устройства, как можешь видеть, этот терминатор есть в конце всех команд - "0x0D". По нему устройство "понимает" что хорош, вся пачка нужной длины получена.

Т.о. в зависимости от конкретной ситуации, тебе нужно реализовать это одним из приведенных способов, чтобы быть более уверенным в целостности принимаемых данных.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
08.11.2023, 13:16
Помогаю со студенческими работами здесь

Отправка и получение данных по UDP
Всем доброго дня! Прошу помощи и консультации. Язык C# изучаю относительно недавно. До этого, в основном, работал с Java. ...

Отправка и получение данных TCP
Всех приветствую! Итак, имеется сервак: internal class Program { static void Main(string args) { ...

Получение данных с SerialPort после запроса
Добрый день. Есть устройство, подключенное по SerialPort, скорость 57600. Стоит задача посылать пакет байт - 0x01, 0x03, 0x80, 0x0A,...

Отправка данных с формы и получение данных с базы
Привет, форумчане. Прошу помощи разобраться в некоторых моментах. Есть форма: &lt;form...

Отправка и получение данных ajax
Отправляю данные через ajax $.ajax({ url: '../send.php', type: 'post', data:{ 'Weight': FullWeight, ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Новые блоги и статьи
Жизнь в неопределённости
kumehtar 23.03.2026
Жизнь — это постоянное существование в неопределённости. Например, даже если у тебя есть список дел, невозможно дойти до точки, где всё окончательно завершено и больше ничего не осталось. В принципе,. . .
Модель здравоСохранения: работники работают быстрее после её введения.
anaschu 23.03.2026
geJalZw1fLo Корпорация до введения программа здравоохранения имела много невыполненных работниками заданий, после введения программы количество заданий выросло. Но на выплатах по больничным это. . .
1С: Контроль уникальности заводского номера
Maks 23.03.2026
Алгоритм контроля уникальности заводского (или серийного) номера на примере документа выдачи шин для спецтехники с табличной частью. Данные берутся из регистра сведений, по которому настроено. . .
Хочу заставить корпорации вкладываться в здоровье сотрудников: делаю мат модель здравосохранения
anaschu 22.03.2026
e7EYtONaj8Y Z4Tv2zpXVVo https:/ / github. com/ shumilovas/ med2. git
1С: Программный отбор элементов справочника по группе
Maks 22.03.2026
Установка программного отбора элементов справочника "Номенклатура" из модуля формы документа. В качестве фильтра для отбора справочника служит группа номенклатуры. Отбор по наименованию группы. . .
Как я обхитрил таблицу Word
Alexander-7 21.03.2026
Когда мигает курсор у внешнего края таблицы, и нам надо перейти на новую строку, а при нажатии Enter создается новый ряд таблицы с ячейками, то мы вместо нервных нажатий Энтеров мы пишем любые буквы. . .
Krabik - рыболовный бот для WoW 3.3.5a
AmbA 21.03.2026
без регистрации и смс. Это не торговля, приложение не содержит рекламы. Выполняет свою непосредственную задачу - автоматизацию рыбалки в WoW - и ничего более. Однако если админы будут против -. . .
1С: Программный отбор элементов справочника по значению перечисления
Maks 21.03.2026
Установка программного отбора элементов справочника "Сотрудники" из модуля формы документа. В качестве фильтра для отбора служит значение перечислений. / / Событие "НачалоВыбора" реквизита на форме. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru