alidayan Ответов: 1

Программа застревает при чтении данных из HID C#


Всем привет,
Я пытаюсь разработать приложение, которое может взаимодействовать с устройством HID. Я могу отправлять и получать данные. Но когда я получаю свою программу, она застревает, что означает, что вы продолжаете отправлять и получать данные, но другие функции, такие как изменение текста метки, закрытие программы, рисование графика и т. д., не работают. Связь начинается, когда я нажимаю кнопку приема. Кто-нибудь может мне помочь?

Что я уже пробовал:

<pre>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 CyUSB;

namespace EGI
{
    public partial class EGI_Main : Form
    {
        // *********************** START OF PROGRAM MAIN ****************************** //

        USBDeviceList usbDevices = null;            // Pointer to list of USB devices
        CyHidDevice myHidDevice = null;             // Handle of USB device

        int VID = 0x04B4;                           // Cypress Vendor ID
        int PID = 0xE177;                           // Example Project Product ID

        bool communicate = false;                   // Communication status

        byte que = 00;                              // Command Byte
        byte PGAValue = 02;                         // PGA
        byte[] receivedDatas = new byte[128];       // Byte array to store 128 bytes of received data from device

        /**********************************************************************
        * NAME: EGI - Ekin Generic HID Communication Interface
        *
        * DESCRIPTION: Main function called initially upon the starting of the
        * application. Used to un-initialized variables, the GUI application, register
        * the event handlers, and check for a connected device.
        *
        ***********************************************************************/

        public EGI_Main()
        {
            InitializeComponent();
            // Create a list of CYUSB devices for this application
            usbDevices = new USBDeviceList(CyConst.DEVICES_HID);
            //Add event handlers for device attachment and device removal
            usbDevices.DeviceAttached += new EventHandler(usbDevices_DeviceAttached);
            usbDevices.DeviceRemoved += new EventHandler(usbDevices_DeviceRemoved);
            //Connect to the USB device
            GetDevice();
        }

        /**********************************************************************
        * NAME: EGI_Main_Load
        *
        * DESCRIPTION: Loads and sets necessary companents and settings on start.
        *
        ***********************************************************************/

        private void EGI_Main_Load(object sender, EventArgs e)
        {
            // Real data graph settings
            chartResult.ChartAreas[0].AxisX.ScaleView.Zoom(0, 127);
            chartResult.ChartAreas[0].AxisY.ScaleView.Zoom(0, 255);
            chartResult.Series[0].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
            chartResult.Series[0].Color = Color.Black;
            chartResult.Series[0].BorderWidth = 3;

            // On line average result graph settings
            chartResult.Series.Add("onLineAvg");
            chartResult.Series["onLineAvg"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
            chartResult.Series["onLineAvg"].Color = Color.Red;
            chartResult.Series["onLineAvg"].BorderWidth = 3;

            // Multipline with on line average result graph settings
            chartResult.Series.Add("multOnLineAvg");
            chartResult.Series["multOnLineAvg"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
            chartResult.Series["multOnLineAvg"].Color = Color.Navy;
            chartResult.Series["multOnLineAvg"].BorderWidth = 3;

            // Exceed point(s) graph settings
            chartResult.Series.Add("exceed");
            chartResult.Series["exceed"].Color = Color.Brown;
            chartResult.Series["exceed"].BorderWidth = 3;
        }

        /**********************************************************************
        * NAME: usbDevices_DeviceRemoved
        *
        * DESCRIPTION: Event handler for the removal of a USB device. When the removal
        * of a USB device is detected, this function will be called which will check to
        * see if the device removed was the device we were using. If so, then reset
        * device handler (myHidDevice), disable the timer, and update the GUI.
        *
        ***********************************************************************/

        public void usbDevices_DeviceRemoved(object sender, EventArgs e)
        {
            USBEventArgs usbEvent = e as USBEventArgs;
            if ((usbEvent.ProductID == PID) && (usbEvent.VendorID == VID))
            {
                InputTimer.Enabled = false; // Disable interrupts for polling HID
                myHidDevice = null;
                GetDevice(); // Process device status
            }
        }

        /**********************************************************************
        * NAME: usbDevices_DeviceAttached
        *
        * DESCRIPTION: Event handler for the attachment of a USB device. The function
        * first checks to see if a matching device is already connected by seeing
        * if the handler (myHidDevice) is null. If no device is previously attached,
        * the function will call GetDevice to check and see if a matching device was
        * attached.
        *
        ***********************************************************************/

        public void usbDevices_DeviceAttached(object sender, EventArgs e)
        {
            if (myHidDevice == null)
            {
                GetDevice(); // Process device status
            }
        }

        /**********************************************************************
        * NAME: GetDevice
        *
        * DESCRIPTION: Function checks to see if a matching USB device is attached
        * based on the VID and PID provided in the application. When a device is
        * found, it is assigned a handler (myHidDevice) and the GUI is updated to
        * reflect the connection. Additionally, if the device is not connected,
        * the function will update the GUI to reflect the disconnection.
        *
        ***********************************************************************/
        public void GetDevice()
        {
            //Look for device matching VID/PID
            myHidDevice = usbDevices[VID, PID] as CyHidDevice;
            if (myHidDevice != null) //Check to see if device is already connected
            {
                Status.Text = "Connected";
                Status.ForeColor = Color.Green;
                InputTimer.Enabled = true; //Enable background timer
            }
            else
            {
                Status.Text = "Disconnected";
                Status.ForeColor = Color.Red;
            }
        }
        
        /**********************************************************************
        * NAME: Set_VidPid_Click
        *
        * DESCRIPTION: Updates the applications Vendor ID and Product ID based on
        * user input when the "Set" button is clicked. This will cause the default VID
        * and PID of 0x04B4 and 0xE177 to be overwritten. The function will then
        * call GetDevice() to check for matching USB device.
        *
        ***********************************************************************/
        private void Set_VidPid_Click(object sender, EventArgs e)
        {
            //Respond to update of VID and PID value by pressing the "Set" button
            VID = Convert.ToInt32(VidTextBox.Text, 16);
            PID = Convert.ToInt32(PidTextBox.Text, 16);
            GetDevice();
        }

        /**********************************************************************
        * NAME: receive_Click
        *
        * DESCRIPTION: Starts and stops communication with device.
        *
        ***********************************************************************/

        private void receive_Click(object sender, EventArgs e)
        {
            if (communicate)
            {
                communicate = false;
                receive.Text = "Communicate";
                receive.BackColor = Color.White;
                receive.ForeColor = Color.Black;
            }
            else
            {
                //not working
                communicate = true;
                receive.Text = "Stop";
                receive.BackColor = Color.Red;
                receive.ForeColor = Color.White;
                que = 00;
                //working
                communicateNow();
            }
        }

        /**********************************************************************
        * NAME: communicateNow
        *
        * DESCRIPTION:Sends commands to device.
        *
        ***********************************************************************/

        private void communicateNow()
        {
            if (communicate)
            {
                // Load data into Output Buffer
                myHidDevice.Outputs.DataBuf[0] = 00;                                    // Report ID
                myHidDevice.Outputs.DataBuf[1] = (byte)numericUpDownThreshold.Value;    // Threshold
                myHidDevice.Outputs.DataBuf[2] = (byte)numericUpDownPB.Value;           // PB
                myHidDevice.Outputs.DataBuf[3] = PGAValue;                              // PGA
                myHidDevice.Outputs.DataBuf[4] = 87;                                    // W/R
                myHidDevice.Outputs.DataBuf[5] = que;                                   // Command byte
                myHidDevice.Outputs.DataBuf[6] = 00;
                myHidDevice.Outputs.DataBuf[7] = 00;
                myHidDevice.Outputs.DataBuf[8] = 00;
                myHidDevice.Outputs.DataBuf[9] = 00;

                // Function call to send data to device
                myHidDevice.WriteOutput();
                // Function call to receive data from device
                myHidDevice.ReadInput();

                byte[] tempData = myHidDevice.Inputs.DataBuf;

                for (int i = 2; i < 34; i++)
                {
                    if (que == 0)
                        receivedDatas[i - 2] = tempData[i - 2];
                    else if (que == 1)
                        receivedDatas[32 + i - 2] = tempData[i - 2];
                    else if (que == 2)
                        receivedDatas[64 + i - 2] = tempData[i - 2];
                    else if (que == 3)
                        receivedDatas[96 + i - 2] = tempData[i - 2];
                }

                if (que < 03)
                {
                    que++;
                    communicateNow();
                }
                else
                {
                    que = 00;
                    dataProcess(receivedDatas);
                }
            }
        }
        

        /**********************************************************************
        * NAME: dataProcess
        *
        * DESCRIPTION: Regroup the datas, writes them to specific textboxes and
        * calls calculateAndDrawGraph to calculate and draw graph.
        *
        ***********************************************************************/

        private void dataProcess(byte[] realDatas)
        {
            bool highLightPower = false;
            bool lowLightPower = false;

            // Clear textboxes
            receivedData_00.Text = null;
            receivedData_01.Text = null;
            receivedData_02.Text = null;
            receivedData_03.Text = null;

            // Count datas to separate to textboxes
            int countOfDatas = 0;

            // Write all datas to specified textboxes
            foreach (byte r in realDatas)
            {
                if (countOfDatas < 32)
                    receivedData_00.AppendText(r + " - ");
                else if (countOfDatas < 64)
                    receivedData_01.AppendText(r + " - ");
                else if (countOfDatas < 96)
                    receivedData_02.AppendText(r + " - ");
                else if (countOfDatas < 128)
                    receivedData_03.AppendText(r + " - ");

                // Check whether light power is high or low.
                if (r >= 190)
                    highLightPower = true;
                else if (r <= 20)
                    lowLightPower = false;

                countOfDatas++;
            }

            //not working
            if (highLightPower)
            {
                labelLightPower.BackColor = Color.Red;
                labelLightPower.ForeColor = Color.White;
                labelLightPower.Text = "Decrease";
            }
            else if (lowLightPower)
            {
                labelLightPower.BackColor = Color.Yellow;
                labelLightPower.ForeColor = Color.Black;
                labelLightPower.Text = "Increase";
            }
            else
            {
                labelLightPower.BackColor = Color.Green;
                labelLightPower.ForeColor = Color.White;
                labelLightPower.Text = "";
            }

            calculateAndDrawGraph(realDatas);
        }

        /**********************************************************************
        * NAME: calculateAndDrawGraph
        *
        * DESCRIPTION: Calculates averages and draws their graphs. Recall 
        * communicateNow() to receive new datas.
        *
        ***********************************************************************/

        private void calculateAndDrawGraph(byte[] realDatas)
        {
            //not working
            chartResult.Series[0].Points.Clear();
            for(int i = 0; i < 128; i++)
            {
                chartResult.Series[0].Points.AddXY(i, realDatas[i]);
            }
            //working
            communicateNow();
        }

        /**********************************************************************
        * NAME: radioButton_CheckedChanged
        *
        * DESCRIPTION: Sets PGAValue.
        *
        ***********************************************************************/
        //cannot click radio buttons
        private void radioButton_CheckedChanged(object sender, EventArgs e)
        {
            if (radioButton1.Checked)
                PGAValue = 01;
            else if (radioButton2.Checked)
                PGAValue = 02;
            else if (radioButton4.Checked)
                PGAValue = 04;
            else if (radioButton8.Checked)
                PGAValue = 08;
        }

    }
}

1 Ответов

Рейтинг:
8

OriginalGriff

Начните с просмотра ваших методов WriteOutput и (что более важно) ReadInput: если они ждут ответа - а я предполагаю, что read, по крайней мере, делает это, - то это блокирующий вызов, а это означает, что ваш поток застрял в ожидании ответа удаленного устройства. В то время как это происходит, ваш поток пользовательского интерфейса застрял и не может делать никаких обновлений для вашего дисплея (или чего-либо еще, например, реагировать на ввод с помощью мыши или клавиатуры).

Единственным решением этой проблемы является полный редизайн с использованием отдельного потока для коммуникаций, который передает информацию потоку пользовательского интерфейса для рисования его графиков и так далее. Вы можете найти класс BackgroundWorker подходящим для этого, поскольку он включает в себя простой механизм, управляемый событиями, для обратной связи с задачей пользовательского интерфейса через индикатор выполнения (который может включать пользовательский объект для хранения ваших данных).


alidayan

Спасибо за ваш ответ. Да, устройство ждет команды для ответа, поэтому я должен отправить некоторые данные с помощью команды write и дождаться ответа от устройства с помощью метода read. Я попробую класс BackgroundWorker, который я никогда не пробовал. Кроме того, если я комментирую communicateNow() программа не будет застрять, но каждый раз, когда я хочу получить данные с устройства, необходимо нажать на кнопку приема.

OriginalGriff

Это довольно простой класс для использования:
https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=против 110).aspx
Включает в себя базовый пример.

alidayan

Большое спасибо. Я добавил:

private void backgroundWorkerGetAndProcessData_DoWork(object sender, DoWorkEventArgs e)        {            communicateNow();        }
и позвал с собой
backgroundWorkerGetAndProcessData.RunWorkerAsync();
когда RunWorkerCompleted вспомнил его с
private void backgroundWorkerGetAndProcessData_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)        {            backgroundWorkerGetAndProcessData.RunWorkerAsync();        }
а теперь займись системой.InvalidOperationException: 'коллекция была изменена; операция перечисления может не выполняться.' ошибка

OriginalGriff

Вот почему я сказал, что он нуждается в полной переработке: вы не можете получить доступ к элементам управления пользовательского интерфейса из любого потока, кроме потока пользовательского интерфейса, а стандартные элементы управления .NET не являются потокобезопасными. Вы не можете просто бросить весь код в другой поток и надеяться, что он работает, вам нужно подумать о том, что нужно сделать в каком потоке и как два потока должны взаимодействовать друг с другом и когда.