Hallo,
ich habe eine kleine GUI in C# erstellt. Mit dieser Gui werden/sollen Daten vom Mikrocontroller ausgelesen werden.
Es handelt sich um serielle Daten. Im Programm nutze ich die Bibliothek Syste.IO.Ports.
private void btn_Init_Click(object sender, EventArgs e)
{
mySerialPort = new SerialPort("COM5");
mySerialPort.BaudRate = 115200;
mySerialPort.Parity = Parity.None;
mySerialPort.StopBits = StopBits.One;
mySerialPort.DataBits = 8;
mySerialPort.Handshake = Handshake.None;
mySerialPort.ReadTimeout = 100;
//mySerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
mySerialPort.Open();
fifo_peekonly = new ConcurrentQueue<byte>();
fifo_queue = new BlockingCollection<byte>(fifo_peekonly);
mySerialPort.DataReceived += (sender, e) =>
{
byte[] buffer = new byte[mySerialPort.BytesToRead];
if (!mySerialPort.IsOpen)
{
throw new System.InvalidOperationException("Serial port is closed.");
}
mySerialPort.Read(buffer, 0, mySerialPort.BytesToRead);
foreach (var b in buffer)
fifo_queue.Add(b);
};
Thread thr = new Thread(new ThreadStart(mythread));
thr.Start();
}
public byte GetByteFromDevice()
{
byte b;
b = fifo_queue.Take();
return b;
}
public void mythread()
{
while(true)
{
if(recvByteCnt == 0)
{
recvLength = GetByteFromDevice();
recvByte = recvLength;
}
else
{
recvByte = GetByteFromDevice();
}
recvBuffer[recvByteCnt] = recvByte;
if (recvByteCnt >= recvLength)
{
recvFlag = 1;
recvByteCnt = 0;
Debug.Write("Complete\n");
}
else
{
if (recvByteCnt == 0)
{
Debug.Write("Start timeout\n");
}
recvByteCnt++;
}
//Debug.Write(recvByte.ToString() + "\n");
}
}
Nun fehlt quasi noch eine TimeOut Mechanismus. Dieser soll im thread nach der if Bedingung "if (recvByteCnt == 0)" eingesetzt werden.
Wie würde man solch eine Funktionalität realisieren?
Da hätte ich noch eine weitere Frage.
Wo ist der Unterscheid zwischen ConcurrentQueue und BlockingCollection?
fifo_peekonly = new ConcurrentQueue<byte>();
fifo_queue = new BlockingCollection<byte>(fifo_peekonly);
Dies habe ich von folgenden Link herausgezogen für mein Projekt:
c-sharp-serial-port-sometimes-missing-data/45432817#45432817
Wo ist der Unterscheid zwischen ConcurrentQueue und BlockingCollection?
Sowas kannst Du viel schneller beantwortet bekommen, wenn Du einfach in die Docs schaust 🙂
ConcurrentQueue<T> Class (System.Collections.Concurrent)
BlockingCollection<T> Class (System.Collections.Concurrent)
Also zwei vollständig verschiedene Konzepte.
Um die Frage zu beantworten:
Nun fehlt quasi noch eine TimeOut Mechanismus.
Wie würde man solch eine Funktionalität realisieren?
Dazu musst Du Dein Quellcode vollständig umbauen.
Threads sind ein Betriebssystem-Konzept, das Du nicht abbrechen kannst. Ein Thread kann nur abgeschossen werden und hat keinerlei Cancel-Möglichkeit (weil es eben vom Betriebssystem verwaltet wird).
Daher arbeitet man in .NET nur in sehr seltenen Fällen mit Threads, sondern man arbeitet mit Tasks.
Tasks sind eine Abstraktion von Threads; haben volle async/await-Support und können auch Cancel (zB durch ein Timeout) unterstützen.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo Abt, danke für deinen Post.
Ich habe für meine Anwendung BlockingCollection benutzt. Im Thread lese ich dann die bytes mit der Methode GetByteFromDevice aus.
Also wäre in meinem Fall die andere Variante geeigneter?
4 Minuten zwischen meinem Post und Deiner Antwort. Wieso liest Dir denn die beiden Sachen nicht mal wenigstens 15 Minuten durch, um die beiden Dinge zu verstehen?
Man kann beides nutzen; kommt halt drauf an, wie man das umsetzt.
Eine BlockingCollection verwendet man - wie man in der Doku sehen kann - eigentlich mit TryAdd
und GetConsumingEnumerable
.
Aber um deren Anwendung zu verstehen, sollte man halt die Doku lesen, sonst wirds schwer...
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo,
ich habe nun den Empfang der Daten in einer separaten Task realisiert.
Auch das Versenden von Daten läuft in einem anderen Task.
Nun ist es so wenn ich Daten versende, dann läuft das mit dem Empfangen von Daten schief.
bool SendFlagLedOn = false;
bool SendFlagLedOff = false;
Task recvTask = new Task(new Action(OnRxFrames));
recvTask.Start();
Task sendTask = new Task(new Action(OnTxFrames));
sendTask.Start();
private void OnRxFrames()
{
while (true)
{
if (recvByteCnt == 0)
{
recvLength = GetByteFromDevice();
recvByte = recvLength;
}
else
{
recvByte = GetByteFromDevice();
}
recvBuffer[recvByteCnt] = recvByte;
if (recvByteCnt >= recvLength)
{
recvFlag = 1;
recvByteCnt = 0;
Debug.Write("Complete\n");
this.Invoke(new EventHandler(DoUpDate));
}
else
{
recvByteCnt++;
}
}
}
private void OnTxFrames()
{
while (true)
{
if(SendFlagLedOn == true)
{
SendFlagLedOn = false;
byte[] txData = new byte[5];
txData[0] = 4;
txData[1] = 1;
txData[2] = 2;
txData[3] = 3;
txData[4] = 4;
string str = Encoding.Default.GetString(txData);
mySerialPort.Write(str);
}
if (SendFlagLedOff == true)
{
SendFlagLedOff = false;
byte[] txData = new byte[5];
txData[0] = 4;
txData[1] = 2;
txData[2] = 2;
txData[3] = 3;
txData[4] = 4;
string str = Encoding.Default.GetString(txData);
mySerialPort.Write(str);
}
}
}
Hallo,
was genau läuft denn schief?
Ein großer Fehler ist jetzt bei dir die erzeugten Endlosschleifen (schau mal im TaskManager auf die Prozessor-/Kernauslastung)!
Du mußt diese wartend oder blockierend machen (oder ist GetByteFromDevice()
blockierend, da du ja von ConcurrentQueue
und BlockingCollection
geschrieben hast?). Zumindestens für die Sendeschleife sollte ein Thread.Sleep(x)
hin (bei Benutzerauslösung der Flags reichen einige 100ms).
PS: Sofern du konstante Daten versendest, kann du einfach z.B. byte[] txData = { 4, 1, 2, 3, 4 };
schreiben (und sogar den string str
einmalig vor der Schleife erzeugen - bzw. 2 für die beiden Fälle).
Der eigentliche Queue Mechanismus fehlt ja nun komplett, oder übersehe ich etwas?
Weil, das war ja eigentlich schon eine gute Idee, die auch so weit verbreitet ist.
Mit der BlockingCollection, die man hier auch verwenden kann, kann man das blockierende Verhalten der Tasks auch erreichen.
Flow wäre dann:
GetConsumingEnumerable
verwenden!TryAdd
)Sobald keine Daten mehr kommen sollen, CompleteAdding
ausführen, sodass der verarbeitende Task sich, jedoch alle Daten in der Collection noch verarbeitet werden.
Mit einem CancellationToken, der bei der Verarbeitung abgeprüft wird, kann das Canceln erreicht werden - und eben das Enforce durch CompleteAdding
.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code