@Sechih

Обработать поток байт от USB CDC C#?

Здравствуйте, пытаюсь написать приложку winform, которая принимает данные по usb-cdc, с C# можно сказать только знакомлюсь в плотную, постепенно изучаю. Знаю только чистый CИ, пишу под stm32. По примерам из интернета написал прием данных в отдельном потоке, в дебаге видно как все сохраняется в массив _bufer. Сейчас нужно обработать данные и посчитать/проверить CRC, и вывести в listview. Как правильнее это сделать(хочется изначально учиться писать правильно)? Мысль создать еще один метод, через "указатель" например ref, сослаться на _bufer внутри метода, и внутри созданного метода обрабатывать эти данные и выводить в listview, может даже этот метод вызывать в отдельном потоке?
Как правильно доставать данные из этого буфера по сути они хранятся в item которая с var является тем же массивом?
Немного ломают восприятия классов после обычных структур в СИ, можно попробовать поиграться со статическими методами или классами, но в видеоуроках говорят что их нужно применять не во всех случаях.
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 System.IO.Ports;
using System.Threading;


namespace PS_aide_1._0
{
    public partial class Form1 : Form
    {
        bool flag_btn_StartStop_click ;
        bool flag_btn_open_click;

        private const int DataSize = 39;    //  число в байтах
        private readonly byte[] _bufer = new byte[DataSize];
        private int _stepIndex;
        private bool _startRead;

        public Form1()
        {
            InitializeComponent();
            SerialPortFind();
        }

        private void SerialPortFind()
        {
            try
            {
                String[] ports = SerialPort.GetPortNames();
                cboPort.Items.AddRange(ports);
                cboPort.SelectedIndex = 0;
            }
            catch (Exception ex)
            {

                MessageBox.Show(ex.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            
        }
        private void SerialPortInit(bool flag) 
        {
            if (flag == true)
            {
                serialPort1 = new SerialPort();
                serialPort1.PortName = cboPort.Text;
                serialPort1.Open();
            }
            else {
                serialPort1.Close();
            }

        }

        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void OpenPort_Btn_Click(object sender, EventArgs e)
        {
            flag_btn_open_click ^= true;
           if (flag_btn_open_click)
            {
                OpenPort_Btn.Text = "Close";
                Start_Stop_button.Enabled = true;
                try
                {SerialPortInit(flag_btn_open_click);}
                catch (Exception ex)
                {MessageBox.Show(ex.Message, "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
                 return; }
            }
            else
            {
                OpenPort_Btn.Text = "Open";
                Start_Stop_button.Text = "Start";
                Start_Stop_button.Enabled = false;
                SerialPortInit(flag_btn_open_click);
                flag_btn_StartStop_click = false;
            }



        }

        private void Start_Stop_button_Click(object sender, EventArgs e)
        {
            flag_btn_StartStop_click ^= true;
            if (flag_btn_StartStop_click)
            {
                Start_Stop_button.Text = "Stop";
                //Thread thread = new Thread( delegate (){/*вызываемый метод*/}  );
                //thread.Start();
                //или так
                // Thread thread = new Thread(()=>{/*вызываемый метод*/});
                //thread.Start();
                //или так
                //new Thread(() => {/*вызываемый метод*/}).Start();
                new Thread(() => {

                        serialPort1.DataReceived += serialPort1_DataReceived;
                      
                }) { Priority = ThreadPriority.Normal }.Start();
                
                //Parallel.Invoke(() => {/*task1*/}, ()=> { /*task2*/}, ()=> {/*task3*/});

                //byte[] buf = new byte[38];
                ////serialPort1.Read(buf,0,38);
                //string temp = mySerialPort.ReadExisting();
                //    listBox1.Items.Add(temp);     
            }
            else
            {
                Start_Stop_button.Text = "Start";
            }

        }

       private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            var port = (SerialPort)sender;
            try
            {
                //  узнаем сколько байт пришло
                int buferSize = port.BytesToRead;
                for (int i = 0; i < buferSize; ++i)
                {   
                    byte bt = (byte)port.ReadByte();//  читаем по одному байту
                    if (0x02 == bt)  //  если встретили начало кадра (0xFF) - начинаем запись в _bufer
                    {
                        _stepIndex = 0;
                        _startRead = true;
                        // >>> закомментировать если надо сохранять этот байт
                        //_bufer[_stepIndex] = bt;
                        //++_stepIndex;
                        //<<<
                    }
                    if (_startRead) //  дописываем в буфер все остальное
                    {
                        _bufer[_stepIndex] = bt;
                        ++_stepIndex;
                    }

                    if (_stepIndex == DataSize && _startRead) //  когда буфер наполнился данными
                    {

                       
                        var item = _bufer[39];//здесь хранятся данные
                        _startRead = false;
                    }
                }

            }
            catch { }
        }


    }
}

616eec22308d2067262599.jpeg
616eed0d03f48908044816.png
  • Вопрос задан
  • 121 просмотр
Пригласить эксперта
Ответы на вопрос 1
igolets
@igolets
Программист C#, MSSQL
  1. Хорошо бы код, который работает с оборудованием (читает данные) вынести в отдельный класс и сделать под него интерфейс. Для чего нужно — если нужно будет разделить работу "железячника" и программиста интерфейса, второму можно будет дать возможность запускать программу с "заглушкой", чтобы он пилил интерфейс без доступа к железу. Ну, или для демонстрации программы запустить без доступа к железу.
  2. Соответственно, По событию на форме (старт/стоп) вызываются некие методы класса работы с железом и происходит получение/накопление данных. В зависимости от логики этого класса вызываются события (по получении байта, или полного пакета)
  3. По событию (код формы навесил обработчики событий при инициализации кода, работающего с железом) форма уже обращается к экземпляру класса, который работает с железом и получает от него данные (скорее всего, в сыром бинарном виде) и что-то с ними делает. Например, преобразует в класс-модельку, которая добавляется в биндингсорс (List), который используется как DataSource для ListView
  4. Код трансляции бинарных данных в модельку лучше тоже в отдельный класс вынести. Могу предположить, что могут быть разные формы представления данных.


Вкратце так.

Ну, а код класса имитации будет имитировать работу настоящего класса, вызывать нужные события и отдавать заранее записанные данные, или генерить по какому-то алгоритму. Переключение между заглушкой и настоящим классом реализуется, например, при помощи Dependency Injection, для C# можно использовать что-то типа https://habr.com/ru/post/50845/. В конфиг-файле прописывается, какой класс используется и всё, один и тот же код в рантайме использует разные классы. Лично я, например, так реализую работу с Фискальными аппаратами и терминалами оплаты пластиковыми картами.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы