Serial Communication возвращает неправильный ответ, когда что-то работает в фоновом режиме (например, просмотр жесткого диска)

У меня большие проблемы с последовательными запросами.

Описание от того, что я хочу:

  • установить последовательное соединение, посылать последовательные запросы 6 датчикам температуры один за другим (это делается каждые 0,5 секунды в цикле)

  • вопрос и ответ-назначение хранятся в массиве List

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

Моя проблема:

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

Где я думаю, что проблема может быть:

  • В приватной пустоте ReceiveThread() класса SerialCommunication

Вот мой код:


Класс CommunicationArray:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Hardwarecommunication
{
    public class CommunicationArray
    {
        public string request { get; set; }
        public object myObject { get; set; }
        public string objectType { get; set; }
    }
}

Класс SerialCommunication

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.IO.Ports;
using System.Windows.Forms;

namespace Hardwarecommunication
{
    class SerialCommunication
    {
        Thread t2;
        Thread t;
        private SerialPort serialPort = new SerialPort("COM2", 115200, Parity.Even, 8, StopBits.One);
        string serialAnswer = "";
        private volatile bool _shouldStop;
        private int counter;

        List<CommunicationArray> ar = new List<CommunicationArray>();

        object[] o = new object[3];


        public void addListener(string request, object myObject, string objectType)
        {
            CommunicationArray sa = new CommunicationArray();

            sa.request = request;
            sa.myObject = myObject;
            sa.objectType = objectType;
            ar.Add(sa);
        }

        public void startListen()
        {
            t2 = new Thread(() => writeSerialPortThread());
            t2.Start();
        }



        public void startSerialPort2()
        {

            try
            {
                serialPort.Open();
                //MessageBox.Show("Connection opend!");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
        }

        public void stopSerialPort2()
        {
            try
            {
                if (serialPort.IsOpen == true)
                    // Connection  closed
                    serialPort.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }


        private void writeSerialPortThread()
        {
            string request = "";

            for (int i = 0; i < ar.Count(); i++)
            {
                request = ar[i].request;
                //request = ((object[])ar[0])[0].ToString();
                //if (!t.IsAlive)
                //{
                try
                {
                    t = new Thread(ReceiveThread);
                    _shouldStop = false;
                    //MessageBox.Show("start thread");
                    t.Start();
                    serialPort.Write(request);
                    Thread.Sleep(50);
                    _shouldStop = true;
                    t.Join();
                }
                catch
                {
                }
                Label tmpLabelObject = (Label)ar[i].myObject;
                serialAnswer = serialAnswer.Replace("=", "");
                if (tmpLabelObject.InvokeRequired)
                {
                    MethodInvoker UpdateLabel = delegate
                    {
                        tmpLabelObject.Text = serialAnswer;
                    };
                    try
                    {
                        tmpLabelObject.Invoke(UpdateLabel);
                    }
                    catch
                    {
                    }
                }
            }
        }

        private void ReceiveThread()
        {
            //MessageBox.Show("in thread");
            while (!_shouldStop)
            {
                serialAnswer = "";
                try           
                {
                    //MessageBox.Show("in thread");
                    serialAnswer = serialPort.ReadTo("\r");
                    if (serialAnswer != "")
                    {
                    }
                    return;
                }
                catch (TimeoutException) { }
            }
        }
    }
}

Class Form1 // для установления соединения и запуска запроса датчика

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;

namespace Hardwarecommunication
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private SerialCommunication serialCommunication1 = new SerialCommunication();

        private void Form1_Load(object sender, EventArgs e)
        {
            //start up serial connection
            serialCommunication1.startSerialPort2();
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            timerRecord.Enabled = true;
            if (this.buttonStart.Text == "Start")
                this.buttonStart.Text = "Stop";
            else
                this.buttonStart.Text = "Start";            
        }

        private void timerRecord_Tick(object sender, EventArgs e)
        {
            if (this.buttonStart.Text == "Stop")
            {
                this.serialCommunication1.startListen();
            }
        }

        private void buttonFillRequestArray_Click(object sender, EventArgs e)
        {         
            this.serialCommunication1.addListener("$0BR00\r" + "\r", this.labelResult0, "label0"); //request to the hardware
            this.serialCommunication1.addListener("$0BR01\r" + "\r", this.labelResult1, "label1");
            this.serialCommunication1.addListener("$01R00\r" + "\r", this.labelResult2, "label2");
            this.serialCommunication1.addListener("$01R01\r" + "\r", this.labelResult3, "label3");
            this.serialCommunication1.addListener("$01R02\r" + "\r", this.labelResult4, "label4");          
        }
    }
}

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

1 ответ

Решение

Замечания: serialPort.Write(string) это неблокирующее хранилище в буфере вывода.

Это означает, что следующее не гарантирует, что вы даже закончили писать свой запрос, прежде чем перестанете слушать ответ:

serialPort.Write(request);
Thread.Sleep(50);
_shouldStop = true;

Вы можете добавить:

while( serialPort.BytesToWrite > 0 ) Thread.Sleep(1); // force blocking

но это плохо советовали.

Мне интересно одно. Здесь есть только один последовательный порт. Почему вы хотите, чтобы с ним работало много разных потоков, когда вы можете управлять всем взаимодействием последовательного порта с одним потоком? (Или в худшем случае 1 поток для ввода 1 поток для вывода)

Для меня гораздо больше смысла хранить запросы в какой-то очереди, а затем очищать их по одному для обработки в одном потоке. Ответы могут быть аналогичным образом поставлены в очередь или отправлены как события назад вызывающей стороне.

РЕДАКТИРОВАТЬ: Если вы не возражаете против одного цикла чтения / записи за раз, вы можете попробовать:

string response;
lock(serialPort) {
    // serialPort.DiscardInBuffer(); // only if garbage in buffer.
    serialPort.Write(request);
    response = serialPort.ReadTo("\r"); // this call will block till \r is read.
                                        // be sure \r ends response (only 1)
}
Другие вопросы по тегам