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)
}