Forum: PC-Programmierung C++ Serialport Datenverlust


von Andre (Gast)


Lesenswert?

Hallo zusamen,

ich stehe total auf dem Schlauch, ich versuche mit Visual Studio 2012 
C++ einen, oder auch mehrere, Com Ports zu lesen. Ich habe mir dazu nach 
bestem Wissen aus dem Internet etwas zusammen gesucht. Nur leider habe 
ich ganz komische Effekte:
Manchmal scheint es so als ob Readfile() dem Eingang sagt wo er die 
nächsten Daten hin schreiben soll, wenn ich durchsteppe dann steht das 
nächste Byte sofort im Eingangsbuffer, manchmal aber eben auch erst nach 
dem nächsten Readfile(). Und manchmal gehen einfach Bytes verloren.

Ich blicke das echt nicht. Kann das daran liegen dass der 
Entwicklungsrechner eine VM ist? Mit Putty zu Putty funktioniert es 
problemlos.

Der folgende Code läuft als Thread.

Danke schonmal worweg.
1
int Serial::StartComm(void)
2
{
3
4
  OVERLAPPED oEv,ovRd;
5
  char inbuff[INPUTBUFFERSIZE];
6
  int buffptr = 0;
7
  BOOL stillread = TRUE;
8
  BOOL rc = 0;
9
  int CommTimeoutCnt = 0;
10
11
  DWORD Err;
12
  DWORD EventMask;
13
14
  int i;
15
16
  oEv.hEvent = CreateEvent(
17
    NULL,   // default security attributes 
18
    TRUE,   // manual-reset event 
19
    FALSE,  // not signaled 
20
    NULL    // no name
21
    );
22
    
23
  // Initialize the rest of the OVERLAPPED structure to zero.
24
  oEv.Internal = 0;
25
  oEv.InternalHigh = 0;
26
  oEv.Offset = 0;
27
  oEv.OffsetHigh = 0;
28
29
  assert(oEv.hEvent);
30
31
  ovRd.hEvent = CreateEvent(
32
    NULL,   // default security attributes 
33
    FALSE,   // not manual-reset event 
34
    FALSE,  // not signaled 
35
    NULL    // no name
36
    );
37
38
  // Initialize the rest of the OVERLAPPED structure to zero.
39
  ovRd.Internal = 0;
40
  ovRd.InternalHigh = 0;
41
  ovRd.Offset = 0;
42
  ovRd.OffsetHigh = 0;
43
44
  assert(ovRd.hEvent);
45
46
  //Verwende folgendes Event: warte auf irgendein Char
47
  SetCommMask(hPort, EV_RXCHAR);
48
    
49
  while(1)
50
  {
51
52
    rc = ::WaitCommEvent(hPort, &EventMask, &oEv); 
53
  
54
    if (!rc) Err = ::GetLastError(); 
55
  
56
    // Gucken ob "pending", wenn ja auf Beendigung warten: 
57
    if (Err == ERROR_IO_PENDING) 
58
    { 
59
  
60
      CommTimeoutCnt = 0;
61
      while (1) 
62
       { 
63
          
64
        // Max. 100 msec auf den Event warten: 
65
        DWORD wait_result = ::WaitForSingleObject(oEv.hEvent, 100); 
66
67
        if (wait_result == WAIT_TIMEOUT) 
68
        { 
69
          // Timeout
70
          
71
          if((CommTimeoutCnt++) > (COMMTIMEOUT_SEK*10))
72
          {
73
            CommTimeoutCnt = 0;
74
            MeldeCommTimeout();
75
          }
76
        } 
77
        else if (wait_result == WAIT_OBJECT_0) 
78
        { 
79
          // Zeichen da 
80
          CommTimeoutCnt = 0;
81
          break; 
82
        } 
83
        else 
84
        { 
85
          // schwerer Fehler
86
          while(1); 
87
        } 
88
      } 
89
   
90
      DWORD bytes_transferred = 0; 
91
      rc = GetOverlappedResult(hPort, &oEv, &bytes_transferred, FALSE); 
92
  
93
      if (rc) Err = 0; 
94
      else Err = ::GetLastError(); 
95
  
96
      if ((Err == ERROR_IO_PENDING) || (Err == ERROR_IO_INCOMPLETE)) 
97
      { 
98
        // Schwerer Fehler 
99
        while(1);
100
      } 
101
  
102
    } 
103
     
104
    if (rc) 
105
    { 
106
      // WaitCommEvent war erfolgreich
107
      stillread = TRUE;
108
109
      while( stillread )
110
      {
111
        if(buffptr && inbuff[buffptr-1] == 0x0D)
112
        {
113
          //Jetzt kam ein LF
114
          if(strlen(inbuff) == 8)
115
          {
116
            //Datenlänge stimmt
117
118
            //Status lesen
119
            status = inbuff[0];
120
121
            //LF ersetzen
122
            inbuff[7] = 0;
123
124
            //Zahl ermitteln
125
            wert = atof(&inbuff[1]);
126
127
            //Signalisieren dass neue Daten da sind
128
            MeldeNeueWerte();            
129
          }
130
          else
131
          {
132
            MeldeDataError();
133
          }
134
          //inbuff löschen
135
          for(i=0;i<sizeof(inbuff);i++)
136
          {
137
            inbuff[i] = 0;
138
          }
139
          //Pointer zurück setzen
140
          buffptr = 0;
141
        }
142
        else if (buffptr > 8)
143
        {
144
          //inbuff löschen
145
          for(i=0;i<sizeof(inbuff);i++)
146
          {
147
            inbuff[i] = 0;
148
          }
149
          //Pointer zurück setzen
150
          buffptr = 0;
151
          MeldeDataError();
152
        }
153
        if(ReadFile(hPort,&inbuff[buffptr], 1, NULL, &ovRd))
154
        {
155
          //Schön, neue Daten wurden gelesen
156
          buffptr++;
157
        }
158
        else
159
        {
160
          //Kein Byte gelesen
161
          Err = GetLastError();
162
          stillread = FALSE;
163
        }
164
      }
165
    } 
166
    else 
167
    { 
168
      // WaitCommEvent war nicht erfolgreich, der Grund steht in "error"
169
      Err = GetLastError();
170
    }
171
  }
172
  return 1;
173
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

1
    rc = ::WaitCommEvent(hPort, &EventMask, &oEv); 
2
  
3
    if (!rc) Err = ::GetLastError(); 
4
  
5
    // Gucken ob "pending", wenn ja auf Beendigung warten: 
6
    if (Err == ERROR_IO_PENDING) 
7
    { 
8
  
9
      CommTimeoutCnt = 0;

Was geschieht hier, wenn rc gültig ist?

Genau: Deine komplette Fehlerbehandlungsroutine wird durchlaufen, wenn 
Err /zufälligerweise/ vom letzten Aufruf her noch den Wert 
ERROR_IO_PENDING enthält -- obwohl rc doch gültig war.

Da Du weiter unten auch den Fall auswertest, daß rc gültig ist, 
solltest Du dem if-Statement hier ein paar geschweifte Klammern 
spendieren, so daß die Bestimmung und Auswertung von Err nur erfolgt, 
wenn rc ungültig ist.

Danach kommt ein else anstelle der zweiten Auswertung von rc, und 
Dein Programm bekommt die Chance, sich anders zu verhalten.

Das abschließende else und der letzte Aufruf von GetLastError dürfte 
dann auch klar als Unfug erkennbar sein.

von Andre (Gast)


Lesenswert?

Danke, ist natürlich klar.

Aber das Grundproblem liegt vermutlich woanders: ich hab jetzt erst 
verstanden was overlapped bedeutet.

Also habe ich das jetzt umgebaut. Muss man denn eigentlich auf einen 
neuen Char warten? Denn wenn ich ReadFile() aufrufe kann ich mit 
WaitForSingleObject ja auch darauf warten, oder spricht da was gegen?
Der folgende Code funzt jedenfalls:
1
//Overlapped 
2
int Serial::StartComm(void)
3
{
4
5
  OVERLAPPED ovRd;
6
  char inbuff[INPUTBUFFERSIZE];
7
  int buffptr = 0;
8
  int CommTimeoutCnt = 0;
9
  DWORD bytes_transferred = 0;
10
  DWORD wait_result = 0;
11
12
  DWORD Err;
13
  DWORD EventMask;
14
15
  int i = 0;
16
17
  ovRd.hEvent = CreateEvent(
18
    NULL,   // default security attributes 
19
    FALSE,   // not manual-reset event 
20
    FALSE,  // not signaled 
21
    NULL    // no name
22
    );
23
24
  // Initialize the rest of the OVERLAPPED structure to zero.
25
  ovRd.Internal = 0;
26
  ovRd.InternalHigh = 0;
27
  ovRd.Offset = 0;
28
  ovRd.OffsetHigh = 0;
29
30
  assert(ovRd.hEvent);
31
32
  //Verwende folgendes Event: warte auf irgendein Char
33
  SetCommMask(hPort, EV_RXCHAR);
34
    
35
  while(1)
36
  {
37
38
    while(ReadFile(hPort,&inbuff[buffptr++], 1, NULL, &ovRd))
39
    {
40
      //Schön, neue Daten wurden gelesen
41
      CheckBuffer(inbuff,&buffptr,sizeof(inbuff));
42
      //gleich nochmal
43
    }
44
    
45
    //Kein Byte gelesen
46
    Err = GetLastError();
47
48
    if (Err == ERROR_IO_PENDING)
49
    {
50
      //Warte auf ein Zeichen, denn der Port wird ja OVERLAPPED benutzt
51
      CommTimeoutCnt = 0;
52
      while (1) 
53
      {       
54
        // Max. 100 msec auf den Event warten: 
55
        wait_result = ::WaitForSingleObject(ovRd.hEvent, 100); 
56
57
        if (wait_result == WAIT_TIMEOUT) 
58
        { 
59
          // Timeout, noch kein Zeichen da      
60
          if((CommTimeoutCnt++) > (COMMTIMEOUT_SEK*10))
61
          {
62
            CommTimeoutCnt = 0;
63
            MeldeCommTimeout();
64
          }
65
        } 
66
        else if (wait_result == WAIT_OBJECT_0) 
67
        { 
68
          // Zeichen da     
69
          CommTimeoutCnt = 0;
70
          break; 
71
        } 
72
        else 
73
        { 
74
          // schwerer Fehler
75
          Err = GetLastError();
76
          while(1); 
77
        } 
78
      }
79
80
      //Jetzt kam vermutlich ein Zeichen
81
      //bytes_transferred ist undefined
82
      if(!(GetOverlappedResult(hPort, &ovRd, &bytes_transferred, FALSE)))
83
      {
84
        Err = ::GetLastError(); 
85
        if ((Err == ERROR_IO_PENDING) || (Err == ERROR_IO_INCOMPLETE)) 
86
        { 
87
          // Es kam wohl doch kein Zeichen, schwerer Fehler, da ein Zeichen da sein muss,
88
          // denn sonst wäre WaitForSingleObject ja nicht beendet worden
89
          while(1);
90
        }
91
      }
92
      else
93
      {
94
        //Juhu, das Zeichen kam wirklich
95
        CheckBuffer(inbuff,&buffptr,sizeof(inbuff));
96
        //Und wieder von vorn
97
      }
98
    }
99
    else
100
    {
101
      //ReadFile wurde nicht mit ERROR_IO_PENDING abgebrochen
102
      //Was soll das? Schwerer Fehler?
103
      while(1);
104
    }
105
  }
106
  return 1;
107
}

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.