Robuster Zugriff auf schwierige serielle HW in CSharp

25/10/2007 - 15:00 von johannes | Report spam
Hallo allerseits,

ich habe eine C#-Applikation für einen Prüfstand entwickelt, die u.a.
6 Waagen fernsteuern muss. Diese sind alle an unterschiedlichen COM-
Ports. Dafür gibt es auf dem Prüfstand-PC auch eine PCI-Karte, die bis
zu 8 COM-Ports implementiert.

Die Waagen (Kern-Waagen, falls das jemand etwas sagt) lassen sich mit
2 Befehlen fernsteuern: "s" bedeutet "Wiegen und Messwert übertragen"
und "t" bedeutet "Tarieren" (auf 0 stellen).

Mir ist es bisher nicht gelungen, dass die Kommunikation robust
funktioniert. Das grundsàtzliche Problem ist, dass mir die Waagen
nicht den Empfang von Befehlen quittieren. Ich habe also keine Ahnung,
ob ein Befehl angekommen und verarbeitet wird oder nicht. Das
Waagenprotokoll ist leider àußerst rudimentàr. Außerdem können die
Waagen offenbar nur dann einen Mess-Wert senden bzw. die Tarierung
durchführen, wenn sie völlig ruhig sind (sie sind sehr empfindlich,
die Auflösung liegt bei 0,1 g). Manchmal weiß ich auch nicht, warum
sie nicht gleich den Befehl verarbeiten können.

Beim Messen ("s"-Befehl) wird, wenn die Übertragung "geglückt" ist,
der Wert in ASCII übertragen, am Ende steht ein . Im Code (s.u.)
wird solange gepollt, bis diese Zeichenfolge kommt. (Beim Tarieren
("t"-Befehl) bekomme ich garnichts zurück, auch wennŽs geklappt hat.)

So, wie ich es sehe, gibt es leider offenbar folgende Möglichkeiten,
nachdem ich einen Befehl gesendet habe:

1. Er wird nach kurzer Zeit wie gewünscht ausgeführt (Glück gehabt)
2. Er vergluckert (die Waage führt ihn nie aus)
3. Er wird nach làngerer Zeit (1 Minute und mehr) doch noch ausgeführt
(der schlimmste Fall, weil er mir in weitere Befehle/Messungen
reinpfuschen kann)

Zuerst habe ich das ganze synchron versucht (TX und RX auf dem selben
Thread, sonst ohne Threads, ohne Timer), wobei ich den jeweiligen
Befehl für alle Waagen gleichzeitig gesendet habe. Das hat meist
funktioniert, aber logischerweise irgendwann zum Halt geführt. Dann
habe ich RX auf einem separaten Thread geführt, was aber letztlich
noch schlechter funktioniert hat. Ich habe auch versucht, zuerst mit
Waage1 anzufangen, nach dem Ergebnis mit Waage2 weiterzumachen usw.
Das hat aber 10mal solange gedauert und meine Probleme auch nicht
gelöst.

Jetzt habe ich folgendes grundsàtzliches Design:
TX an alle, Timeout-Timer starten und im selben Thread RX-pollen (ich
muss dazu sagen, dass bereits das 1.TX nicht im Main-Thread ablàuft,
weil es in einem System.Timers.Timer.Elapsed Handler durchgeführt
wird, ich verwende nur dieses Timer-Objekt, nicht den Timer in
System.Forms). Nach dem Timeout die Waagen erneut senden lassen
(wieder mit TimeoutTime.Start()), die noch kein Ergebnis
zurückgeliefert haben usw. bis alle Waagen es gepackt haben. Das
funktioniert bisher am robustesten, aber manchmal bekomme ich beim
Konvertieren des empfangenen Wertes in eine double eine Exception, das
der zu konvertierende String nicht das richtige Format hat. Nach
einigen Debug-Versuchen mit Primitivst-Mitteln (auf dem Prüfstand-PC
làuft kein VS, die Debug-Version scheint keine Exception von einem
anderen Thread anzuzeigen, sondern stürzt einfach ab) habe ich
rausgefunden, dass dies immer passiert, wenn der String leer ist. Mir
ist es aber nicht klar, wie das sein kann.

Hier noch der wichtigste Code:

public void SendCommand(string funcCode)
{
// funcCode: s Stabiler Waegewert wird von den Waagen
gesendet
// t Die Waagen fuehren die Tarafunktion aus
for (int i = 0; i < Config.nInstallierteWaagen; i++)
{
rxFinishedFlags[i] = false;
rx[i] = "";
try
{
SPCom[i].ReadExisting();
}
catch { }

SPCom[i].Write(funcCode);
}

if (funcCode == "s")
{
bTimeout = false;
timeoutTimer.Start();
}
bJobWeiter = (funcCode == "s");

bool bFertig = true;
while ((bJobWeiter)&&(!bTimeout))
{
for (int i = 0; i < Config.nInstallierteWaagen; i++)
{
try
{
if (!rxFinishedFlags[i])
{
rx[i] += SPCom[i].ReadExisting();
if (rx[i].EndsWith(""))
{
rxFinishedFlags[i] = true;
}
}
}
catch { }

} // for

for (int i = 0; i < Config.nInstallierteWaagen; i++)
{
bFertig = bFertig && rxFinishedFlags[i];
}
bJobWeiter = !bFertig;
} // while


if(funcCode=="s") {
string str = "";
for(int i=0; i<Config.nInstallierteWaagen; i++) {
str += (i+1).ToString() + ": " + rx[i];
}
AppHandling2.Instance.SetWaageRX(str); // Zum Ansehen
if(bFertig) {
timeoutTimer.Stop();
double[] results = ProcessRX(rx); // Hier gibts
die Exception bei Convert.ToDouble(rx[i])

// Lesen war erfolgreich, an uebergeordnete
Instanz Event mit results senden

}

}
else if (funcCode == "t")
{
...
}
}

Der Timeout-Timer ruft bei Timeout folgende Funktion auf:

public void ReSendCommand(string funcCode)
{
// funcCode: s Stabiler Waegewert wird von den Waagen
gesendet
// t Die Waagen fuehren die Tarafunktion aus
for (int i = 0; i < Config.nInstallierteWaagen; i++)
{
if (!rxFinishedFlags[i])
{
rx[i] = "";
SPCom[i].Write(funcCode);
}
}

if (funcCode == "s")
{
bTimeout = false;
timeoutTimer.Start();
}

// Rest aehnlich zu SendCommand

Kann sich jemand erklàren, warum diese Exception beim Konvertieren
manchmal auftritt oder besser, wie kann es sein, dass der zu
konvertierende String "" ist? Und meist nachdem die Exception
aufgetreten ist, bleibt die Applikation haengen. Warum?

Vielleicht hatte jemand schon mal ein àhnliches Problem.


Viele Grüße

Johannes
 

Lesen sie die antworten

#1 Stefan Braumeister
25/10/2007 - 20:54 | Warnen spam
schrieb:
Hallo allerseits,

ich habe eine C#-Applikation für einen Prüfstand entwickelt, die u.a.
6 Waagen fernsteuern muss. Diese sind alle an unterschiedlichen COM-
Ports. Dafür gibt es auf dem Prüfstand-PC auch eine PCI-Karte, die bis
zu 8 COM-Ports implementiert.

Die Waagen (Kern-Waagen, falls das jemand etwas sagt) lassen sich mit
2 Befehlen fernsteuern: "s" bedeutet "Wiegen und Messwert übertragen"
und "t" bedeutet "Tarieren" (auf 0 stellen).

Mir ist es bisher nicht gelungen, dass die Kommunikation robust
funktioniert. Das grundsàtzliche Problem ist, dass mir die Waagen



Dein Code erscheint mir auf den ersten Blick auch nicht besonders robust
zu sein:-)

nicht den Empfang von Befehlen quittieren. Ich habe also keine Ahnung,
ob ein Befehl angekommen und verarbeitet wird oder nicht. Das
Waagenprotokoll ist leider àußerst rudimentàr. Außerdem können die
Waagen offenbar nur dann einen Mess-Wert senden bzw. die Tarierung
durchführen, wenn sie völlig ruhig sind (sie sind sehr empfindlich,
die Auflösung liegt bei 0,1 g). Manchmal weiß ich auch nicht, warum
sie nicht gleich den Befehl verarbeiten können.



Wie wàrs, wenn du mal mit nem Oszi die seriele
Schnittstellenkommunikation beobachtest?
Damit dürftest du sehr schnelle herausfinden, ob das Signal auch zum
richtigen Zeitpunkt korrekt übertragen wird.


Beim Messen ("s"-Befehl) wird, wenn die Übertragung "geglückt" ist,
der Wert in ASCII übertragen, am Ende steht ein . Im Code (s.u.)
wird solange gepollt, bis diese Zeichenfolge kommt. (Beim Tarieren
("t"-Befehl) bekomme ich garnichts zurück, auch wennŽs geklappt hat.)

So, wie ich es sehe, gibt es leider offenbar folgende Möglichkeiten,
nachdem ich einen Befehl gesendet habe:

1. Er wird nach kurzer Zeit wie gewünscht ausgeführt (Glück gehabt)
2. Er vergluckert (die Waage führt ihn nie aus)
3. Er wird nach làngerer Zeit (1 Minute und mehr) doch noch ausgeführt
(der schlimmste Fall, weil er mir in weitere Befehle/Messungen
reinpfuschen kann)

Zuerst habe ich das ganze synchron versucht (TX und RX auf dem selben
Thread, sonst ohne Threads, ohne Timer), wobei ich den jeweiligen
Befehl für alle Waagen gleichzeitig gesendet habe. Das hat meist
funktioniert, aber logischerweise irgendwann zum Halt geführt. Dann
habe ich RX auf einem separaten Thread geführt, was aber letztlich
noch schlechter funktioniert hat. Ich habe auch versucht, zuerst mit
Waage1 anzufangen, nach dem Ergebnis mit Waage2 weiterzumachen usw.
Das hat aber 10mal solange gedauert und meine Probleme auch nicht
gelöst.

Jetzt habe ich folgendes grundsàtzliches Design:
TX an alle, Timeout-Timer starten und im selben Thread RX-pollen (ich
muss dazu sagen, dass bereits das 1.TX nicht im Main-Thread ablàuft,
weil es in einem System.Timers.Timer.Elapsed Handler durchgeführt
wird, ich verwende nur dieses Timer-Objekt, nicht den Timer in
System.Forms). Nach dem Timeout die Waagen erneut senden lassen
(wieder mit TimeoutTime.Start()), die noch kein Ergebnis
zurückgeliefert haben usw. bis alle Waagen es gepackt haben. Das
funktioniert bisher am robustesten, aber manchmal bekomme ich beim
Konvertieren des empfangenen Wertes in eine double eine Exception, das
der zu konvertierende String nicht das richtige Format hat. Nach
einigen Debug-Versuchen mit Primitivst-Mitteln (auf dem Prüfstand-PC
làuft kein VS, die Debug-Version scheint keine Exception von einem
anderen Thread anzuzeigen, sondern stürzt einfach ab) habe ich
rausgefunden, dass dies immer passiert, wenn der String leer ist. Mir
ist es aber nicht klar, wie das sein kann.



Was für ein Text:-)

Hier noch der wichtigste Code:

public void SendCommand(string funcCode)
{
// funcCode: s Stabiler Waegewert wird von den Waagen
gesendet
// t Die Waagen fuehren die Tarafunktion aus
for (int i = 0; i < Config.nInstallierteWaagen; i++)
{
rxFinishedFlags[i] = false;
rx[i] = "";
try
{
SPCom[i].ReadExisting();
}
catch { }

SPCom[i].Write(funcCode);
}

if (funcCode == "s")
{
bTimeout = false;
timeoutTimer.Start();
}
bJobWeiter = (funcCode == "s");

bool bFertig = true;
while ((bJobWeiter)&&(!bTimeout))
{
for (int i = 0; i < Config.nInstallierteWaagen; i++)
{
try
{
if (!rxFinishedFlags[i])
{
rx[i] += SPCom[i].ReadExisting();
if (rx[i].EndsWith(""))
{
rxFinishedFlags[i] = true;
}


Ends with sagt aber alleine nicht besonders viel aus oder?
Da kann ja alles mögliche davor drin stehen.
}
}
catch { }

} // for

for (int i = 0; i < Config.nInstallierteWaagen; i++)
{
bFertig = bFertig && rxFinishedFlags[i];
}
bJobWeiter = !bFertig;
} // while


if(funcCode=="s") {
string str = "";
for(int i=0; i<Config.nInstallierteWaagen; i++) {
str += (i+1).ToString() + ": " + rx[i];
}
AppHandling2.Instance.SetWaageRX(str); // Zum Ansehen
if(bFertig) {
timeoutTimer.Stop();
double[] results = ProcessRX(rx); // Hier gibts
die Exception bei Convert.ToDouble(rx[i])



Du prüfst nirgends ob in rx[i] auch ein konvertierbarer Wert steht, du
prüfst oben lediglich ob am Ende steht.


// Lesen war erfolgreich, an uebergeordnete
Instanz Event mit results senden

}

}
else if (funcCode == "t")
{
...
}
}

Der Timeout-Timer ruft bei Timeout folgende Funktion auf:

public void ReSendCommand(string funcCode)
{
// funcCode: s Stabiler Waegewert wird von den Waagen
gesendet
// t Die Waagen fuehren die Tarafunktion aus
for (int i = 0; i < Config.nInstallierteWaagen; i++)
{
if (!rxFinishedFlags[i])
{
rx[i] = "";
SPCom[i].Write(funcCode);
}
}

if (funcCode == "s")
{
bTimeout = false;
timeoutTimer.Start();
}

// Rest aehnlich zu SendCommand

Kann sich jemand erklàren, warum diese Exception beim Konvertieren
manchmal auftritt oder besser, wie kann es sein, dass der zu
konvertierende String "" ist? Und meist nachdem die Exception
aufgetreten ist, bleibt die Applikation haengen. Warum?

Vielleicht hatte jemand schon mal ein àhnliches Problem.


Viele Grüße

Johannes




Gruß Stefan

Ähnliche fragen