Forum: PC Hard- und Software C# Problem mit Serial Port Performace


von Ben S. (theben)


Lesenswert?

Hallo Leute

ich sitze gerade an einem Programm mit GUI, welches eine menge Daten per 
Serial Port einlesen soll. Leider hängt es bei mir immer wieder an der 
Datenrate. Um dies besser zu untersuchen, habe ich den betreffenden code 
in eine extra konsolen Anwendung gepackt:
1
    class UART_c
2
    {
3
        private SerialPort sport;
4
        private Thread arbeterThreat;
5
        public bool RUN = true;
6
7
        public List<int> Liste = new List<int>();
8
9
        public UART_c()
10
        {
11
            arbeterThreat = new Thread(arbeiterMethode);
12
            arbeterThreat.Start();
13
        }
14
15
        public void Stop()
16
        {
17
            RUN = false;
18
            arbeterThreat.Join();
19
            StreamWriter sw = new StreamWriter(new FileStream("out.txt", FileMode.OpenOrCreate));
20
            for (int i = 0; i < Liste.Count; i = i + 3)
21
            {
22
                sw.WriteLine(Liste[i].ToString("0000") + " " + Liste[i + 1].ToString("0000") + " " + Liste[i + 2].ToString("0000"));
23
            }
24
            sw.Flush();
25
            sw.Close();
26
        }
27
28
        private void arbeiterMethode()
29
        {
30
            sport = new SerialPort("COM8", 500000, Parity.None, 8, StopBits.One);
31
            sport.DataReceived += new SerialDataReceivedEventHandler(dongleDatenEmpfangEvent);
32
            sport.Open();
33
            while(RUN)
34
            {
35
                Thread.Sleep(10);
36
            }
37
            sport.DataReceived -= new SerialDataReceivedEventHandler(dongleDatenEmpfangEvent);
38
            sport.Close();
39
        }
40
41
        private void dongleDatenEmpfangEvent(object sender, SerialDataReceivedEventArgs e)
42
        {
43
            SerialPort sp = (SerialPort)sender;
44
            int eins = sp.BytesToRead;
45
            byte[] bArry = new byte[sp.BytesToRead];
46
            int zwei = bArry.Length;
47
            sp.Read(bArry, 0, bArry.Length);
48
            //string zeichen = sp.ReadExisting();
49
            //int zwei = zeichen.Length;
50
            int drei = sp.BytesToRead;
51
52
            Liste.Add(eins);
53
            Liste.Add(zwei);
54
            Liste.Add(drei);
55
        }
56
    }
Am anderen Ende des Serieal Ports hängt ein einfacher Arduino Mega per 
USB am Rechenr, der mit 500000 Baut Zeichenfolgen mit \n als endzeichen 
sendet. Diese Zeichenfolgen sind im wechsel mal 60 bytes mal 50 bytes 
lang.

Mit den integern eins zwei und drei dokumentiere ich die die Bufferlänge 
der lesbaren Daten vor dem ausnlesen, die bufferlänge meiner 
ausgelesenen Daten und die bufferlänge der lasbaren Daten nach dem 
auslesen.

Mit folgendem Coode rufe ich die Methoden auf und lasse mir nachdem ich 
zum abbrechen die Enter Taste gedückt habe die eins, zwei und drei werte 
in eine Datei schreiben (in Methode Stop()):
1
        static void Main(string[] args)
2
        {
3
            UART_c uart = new UART_c();
4
            Console.ReadLine();
5
            uart.Stop();
6
        }

Jetzt zu den ergebnissen:
eins zwei drei
3968 3968 0000
1213 1281 2687
2687 2687 0000
1184 1249 2719
2719 2719 0000
1439 1502 1574
2466 2466 0000
1323 1383 2154
2443 2577 0008
0008 0008 0000
1490 1556 1797
2023 2070 0342
0342 0342 0000
0849 0951 1034
1304 1357 0381
0509 0566 0357
0483 0536 0427
0558 0558 0000
1468 1583 1023
1569 1630 0755
0755 0755 0000
1479 1539 1207
1495 1550 0879
0879 0879 0000

Und nun zu meinen Problem. Nach dem ersten dongleDatenEmpfangEvent sind 
schon 3968 Zeichen im Buffer. Ok könnte sein, dass das Programm beim 
Starten etwas braucht aber beim zweiten Event sind schon wieder mehr als 
1000 Zeichen da. Dabei erwarte ich ja eigentlich nur knapp 100 Zeichen. 
Ok vielleicht ist das Windows 10 System im Hintergrund etwas langsamer. 
So könnte es passieren, dass das Event zwei oder drei Datenpakete (50er 
oder 60er Zeichenfolge) später kommt aber doch nicht 20 oder 40 
Datenpakete später. Schießlich abreite ich auf einem i7-4500U Prozessor, 
da kann doch nich der popelige Arduino, der die Zeichenketten sich jedes 
mal neu zusammenbaut, schneller sein?

Ist das C# so langsam oder habe ich ein Windows Resourcen Problem?

von Peter II (Gast)


Lesenswert?

Ben S. schrieb:
> Und nun zu meinen Problem. Nach dem ersten dongleDatenEmpfangEvent sind
> schon 3968 Zeichen im Buffer. Ok könnte sein, dass das Programm beim
> Starten etwas braucht aber beim zweiten Event sind schon wieder mehr als
> 1000 Zeichen da. Dabei erwarte ich ja eigentlich nur knapp 100 Zeichen.

hast du mal geschaut das in den Daten drin steht? Eventuell läuft etwas 
komplett schief und die bekommt sehr schnell sehr viel Müll.

von Ben S. (theben)


Lesenswert?

Die Daten stimmen "leider". Der Arduino simuliert eigentlich nur das 
echte System, was ich später auslesen will. Der Arduino baut mir nur 
Zeichenfolgen zusammen, die ähnlich sind, wie ich sie vom Zielsystem 
erwarte. mit dabei ist eine Laufende Nummer, mit der ich kontollieren 
kann, ob Datensätze verloren gegegangen sind.
Wie ich am Anfang geschrieben habe, sind die gezeigten Coode Abschnitte 
aus dem eigentlichen Programm. Dort habe ich mir das auch mal mit dem 
Buffergrößen angeben lassen. Dort ist es sogar so, dass der Resived 
Buffer ständig voll ist. Daran wieder das komische, mir gehen keine 
Daten verloren, also muss das Windows die daten vor diesem Buffer nocht 
irgendwo zwischenpeichern.

von Peter II (Gast)


Lesenswert?

Ben S. schrieb:
> Die Daten stimmen "leider".

dann macht es doch mal ohne Event. Wenn du eh einen eigenen Thread hast, 
wozu dann noch mit Events arbeiten?

du könntest mal noch die Zeitpunkte der Events mit ausgeben. Da muss ja 
sehr viel Zeit vergehen.
Wie hoch ist die CPU last, wenn das Programm läuft?

von Ben S. (theben)


Lesenswert?

Das ist eine gute Idee. Ich habe mal Integer drei mit den Zeiten 
befüllt:
eins zwei min:sek:....
3968 3968 56:03:8994672
1479 1546 56:03:9679901
2422 2422 56:03:9679901
1486 1547 56:04:0679187
2390 2421 56:04:0679187
1322 1377 56:04:1678661
2591 2591 56:04:1678661
1518 1581 56:04:2679226
1248 1305 56:04:2679226
0986 1082 56:04:2679226
1278 1339 56:04:3835024
2629 2629 56:04:3835024
1819 2232 56:04:4841200
1736 1736 56:04:4841200
1330 1383 56:04:5841837
2585 2585 56:04:5841837
1541 1738 56:04:6843650
1550 1604 56:04:6843650
0626 0626 56:04:6843650
1308 1366 56:04:7842585
1409 1525 56:04:7842585
0701 0740 56:04:7842585
0337 0337 56:04:7842585
1302 1352 56:04:9000663
1378 1504 56:04:9000663
0866 0905 56:04:9000663
0207 0207 56:04:9000663
1319 1374 56:05:0003490
1310 1344 56:05:0003490

Zeittechnisch sieht das eigentlich ganz gut aus.

von Ben S. (theben)


Lesenswert?

das Eventgesteuerte will ich eigentlich behalten, da ich nicht immer 
solche schnellen Datensatzwiederholungen erwarte. es könnte auch mal 
eine stunde pause sein und dann gehts wieder los...

Die CPU Last steigt bei dem kleinen Demo Programm nicht merklich an. Sie 
schwankt bei 5%-7% aber auch ohne das Programm

von Peter II (Gast)


Lesenswert?

Ben S. schrieb:
> das Eventgesteuerte will ich eigentlich behalten, da ich nicht immer
> solche schnellen Datensatzwiederholungen erwarte. es könnte auch mal
> eine stunde pause sein und dann gehts wieder los...

ja und? Dann blockiert das read ebend 30min - was stört dich daran?

von Ben S. (theben)


Lesenswert?

So langsam komme ich dem gewünschten ergebnis schon etwas näher.
ich habe jetzt die arbeiterMethode() wie folg umgeschrieben
1
private void arbeiterMethode()
2
        {
3
            sport = new SerialPort("COM8", 500000, Parity.None, 8, StopBits.One);
4
            //sport.DataReceived += new SerialDataReceivedEventHandler(dongleDatenEmpfangEvent);
5
            sport.Open();
6
            while(RUN)
7
            {
8
                byte e = (byte)sport.ReadByte();
9
                while(sport.BytesToRead > 0)
10
                {
11
                    int eins = sport.BytesToRead;
12
                    byte[] bArry = new byte[sport.BytesToRead + 1];
13
                    bArry[0] = e;
14
                    sport.Read(bArry, 1, bArry.Length - 1); 
15
                    int zwei = bArry.Length;
16
17
                    Liste.Add(eins.ToString("0000"));
18
                    Liste.Add(zwei.ToString("0000"));
19
                    Liste.Add(DateTime.Now.ToString("mm:ss:fffffff"));
20
                }
21
            }
22
            //sport.DataReceived -= new SerialDataReceivedEventHandler(dongleDatenEmpfangEvent);
23
            sport.Close();
24
        }
Das Ergebniss ist schon etwas besser:
eins zwei min:sek:....
3967 3968 14:52:6713074
1015 1069 14:52:7718014
2899 2900 14:52:8030932
1069 1116 14:52:8717385
1213 1265 14:52:8717385
0521 0569 14:52:8717385
1020 1021 14:52:8717385
0932 0985 14:52:9722136
1309 1372 14:52:9722136
0627 0689 14:52:9722136
0924 0925 14:52:9722136
0977 1038 14:53:0879137
0888 0947 14:53:0879137
0778 0837 14:53:0879137
1005 1067 14:53:0879137
0082 0083 14:53:0879137
0852 0916 14:53:1890003
3052 3053 14:53:1890003
1042 1094 14:53:2888087
0807 0864 14:53:2888087
0504 0542 14:53:2888087
0621 0676 14:53:2888087
0795 0796 14:53:2888087
0922 0966 14:53:3883456
1189 1247 14:53:3883456
1444 1502 14:53:3883456
0255 0256 14:53:3883456
0919 0966 14:53:4888451
1274 1329 14:53:4888451
0730 0784 14:53:4888451
0481 0539 14:53:4888451
0353 0354 14:53:4888451
0945 0996 14:53:6044647
1804 1861 14:53:6044647
0980 1036 14:53:6044647
0077 0078 14:53:6044647
0961 1016 14:53:7043638

Aber es werden immer noch Buffergrößen von über 1000 erreicht.

von Peter II (Gast)


Lesenswert?

>     byte e = (byte)sport.ReadByte();
>                 while(sport.BytesToRead > 0)
>                 {

warum so umständlich?

1
byte[] bArry = new byte[1024];
2
while( true ) {
3
  int cnt = sport.Read(bArry, 1, bArry.lengt ); 
4
  // Daten verarbeiten
5
}

von Sebastian S. (amateur)


Lesenswert?

500 K Baud sollte jeder normale, heutige Rechner mit links können.
Also geh‘ mal davon aus, dass es an anderer Stelle hakt.

von Peter II (Gast)


Lesenswert?

Sebastian S. schrieb:
> 500 K Baud sollte jeder normale, heutige Rechner mit links können.
> Also geh‘ mal davon aus, dass es an anderer Stelle hakt.

kann er ja, es gehen keine Daten verloren wie er schreibt.

von Sebastian S. (amateur)


Lesenswert?

Ist es möglich, dass das ganze "normal" ist?

Bei Fenster und ähnlichen Betriebssystemen ist es "normal", wenn das 
Programm mal eine Pause einlegt.
Z.B. um Deine Neugierde (Bildschirm) zu befriedigen.

Da in diesen Pausen weiterhin Daten anfallen, ist es schon möglich, dass 
diese vorübergehend in den "unter" dem Betriebssystem liegenden Puffern 
rumhängen, um dann mit "Copy im RAM"-Geschwindigkeit einzutrudeln.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.