Hallo Freunde,
bisher hab ich mich fast ausschließlich mit der µC-Programmierung
beschäftigt. Nun hab ich mich ein wenig in die PC-Programmierung mit C#
eingelesen und habe mir recht schnell eine Klasse zur Kommunikation mit
dem µC geschrieben, inkl. GUI per WinForms.
Nun brauche ich einen kleinen Denkanstoß, denn ich finde, so wie ich es
derzeit mache, ist reinster Pfusch.
Vorweg: Momentan funktioniert alles einwandfrei. Wenn serielle Kommandos
allerdings recht schnell wiederholt von der GUI gesendet werden, gibt es
oft "Kollisionen". Mein µC sendet nach jedem erkannten Kommando eine
eigene Bestätigungssequenz.
Wenn diese Sequenz erkannt wird, schalte ich einen Bool-Typ auf true,
welcher anschließend wieder auf false gesetzt wird.
Hier mal ein Auszug der das veranschaulicht:
1
try
2
{
3
MyComPort.Open();
4
if (MyComPort.IsOpen == true)
5
{
6
Befehl(BefehlVerbinden);
7
while (rx_antwort == false)
8
;
9
Befehl(BefehlConfig);
10
}
11
}
12
catch (Exception ex)
13
{
14
MessageBox.Show("Fehler beim öffnen des Ports!\r\n" + ex.Message, "Fehler!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
15
}
Wie man erkennen kann, sitzt hier eine while-Schleife dazwischen die nur
darauf wartet, endlich eine Antwort zu bekommen, damit der nächste
Befehl gesendet werden kann. Bis die Antwort kommt, steht das Programm
aber. Das haut zwar hin nur frage ich mich, wie man das schöner lösen
könnte.
Hat da vielleicht jemand einen Tipp?
PS: Empfangene Daten werden per SerialDataReceivedEventArgs und
anschließender Auswertung bearbeitet.
LG,
roblue
Du könntest das Ganze z.B. in einen Thread packen, dadurch funktioniert
das GUI und die dazugehörigen Funktionen auch während man auf die
Antwort wartet.
Ist natürlich fraglich ob das in deiner Anwendung Sinn ergibt, aber
vllt. hilft's dir ja.
MfG
Hallo Joe!
Danke für die schnelle Antwort! Bin bereits auf dieses Thema gestoßen.
In meinem C# Handbuch hab ich schon ein wenig über die Threads gelesen.
Habe auch schon mal einen Versuch damit gemacht, allerdings hatte ich
zum Schluss unzählige laufende Threads offen, sodass sich die Anwendung
erst aufgehängt hat...
Muss mir das noch sehr genau ansehen. Im Prinzip bräuchte ich ja nur
meine "BefehlSenden" Methode in einem eigenen Thread. Allerdings weiß
ich noch nicht, wie das mit einer seperaten Klasse funktioniert.
LG,
roblue
Hallo zusammen!
Habe nun ein paar Beispiele mit Threads programmiert, allerdings nicht
ganz zufriedenstellend. Was haltet ihr davon:
1
// Lokale Methode zum schreiben der Befehle
2
private void BefehlSenden(byte[] Befehl)
3
{
4
int timeoutZeit = 100;
5
int lokalerCounter = 0;
6
7
if (MyComPort.IsOpen)
8
{
9
for (int i = 0; i <= Befehl.Length - 1; i++)
10
{
11
MyComPort.BaseStream.WriteByte(Befehl[i]);
12
}
13
while (rx_antwort == false)
14
{
15
lokalerCounter++;
16
Thread.Sleep(10);
17
18
if (lokalerCounter >= timeoutZeit)
19
{
20
MessageBox.Show("Keine Verbindung zur Steuerung!\r\n", "Fehler!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
21
break;
22
}
23
}
24
}
25
}
Mit Thread.Sleep(10) lasse ich in der Schleife das Programm für 10ms
anhalten und einen Counter hochzählen, bis die Timeout-Zeit von 1sek.
erreicht wurde.
Das Programm darf von mir aus ruhig kurzfristig in eine Schleife fallen.
Hab das nun ein paar mal versucht und das dürfte wohl funktionieren. Die
Frage, die ich mir stelle ist aber, ob das andere Programmierer auch so
machen bzw. machen würden?
LG,
roblue
ob erfahrene Windowsprogrammierer das so machen würden ist schwer zu
sagen, ich versteh ja nichmal warum du dieses Heckmeck machst. Ich hab
zwar schon öfter Programme geschrieben, welche die Serielle
Schnittstelle nutzen um mit einem uC zu Kommunizieren. Allerdings fällt
mir kein Einsatzzweck ein welcher deine Art der Kommunikation brauchen
würde.
Erklär mal was deine Schnittstelle da tut, vielleicht kann dir dann
jemand sagen ob man das hätte besser machen können.
Ich würde vermutlich eher eine Art Watchdog basteln, der anschlägt, wenn
eine gewisse Zeit lang nichts empfangen wurde und nicht eine Zeit lang
gucken, ob etwas da ist.
Es geht um die Auswertung eines seriellen Telegramms. Die Daten kommen
von einer Steuerung mit Mikrocontroller. Der sendet nun Daten im
folgenden Format:
BYTE1 = Telegrammstart
BYTE2 = Geräte-ID
BYTE3 = Anzahl der Bytes im Telegramm
BYTE4 = Funktion
BYTE5 = Daten 0
BYTE6 = Daten 1
usw...
Das sendet er nur nach Aufforderung, also ich muss sagen welche Daten
ich haben will bzw. welche ich übermitteln will. Zudem wird immer noch
eine Bestätigung in folgender Form mitgeschickt:
BYTE1 = Telegrammstart
BYTE2 = Geräte-ID
BYTE3 = Anzahl der Bytes im Telegramm
BYTE4 = 0x90
BYTE5 = 0x01
Und genau diese Antwort muss ich abwarten, bis ich den nächsten Befehl
an die Steuerung senden darf. Deswegen auch die Schleife, die auf die
Antwort wartet.
Ich würde das irgendwie so machen:
https://gist.github.com/70a4a5e5435fbd88908c
Der Button packt nen Kommando in eine Queue, und die wiederum wird von
einem BackgroundWorker sequentiell abgearbeitet.
Bei Klick auf den Button blockiert die GUI dann nicht.
BackgroundWorker kann man direkt auf eine Form ziehen und stellt quasi
eine Wrapper-Klasse um einen Thread dar:
- In DoWork() läuft der Thread ab.
- Mit .RunWorkerAsync() wird der Thread gestartet
- Mit .ReportProgress() kann der Thread der GUI Infos mitteilen
- Mit .CancelAsync() kann die GUI dem Thread via .CancellationPending
mitteilen, dass er mal Feierabend machen soll ;D
Mit:
1
lock(this)
2
{
3
// Synchronisierte Code
4
}
...sorgt man dafür der darin verbaute Code immer nur von einem Thread
gleichzeitig betreten werden darf (d.h. im Beispiel kann .Enqueue() und
.Dequeue() nicht gleichzeitig laufen)
Solche Abschnitte sollte man möglichst kurz halten: Wenn 2 Threads
gleichzeitig einen solchen Abschnitt betreten wollen, muss einer von
beiden warten - Im Falle des GUI-Threads würde die GUI dann solange
einfrieren.
Zu beachten bei Windows.Forms: GUI-Elemente (Textboxen, Labels, etc.)
dürfen in DoWork() nicht verändert werden, da sollte man immer den Umweg
über .ReportProgress() nehmen.