Problem mit Get-Methode
-
Was du da machst ist humbug. Du prüfst ob o_Edu32fp != NULL (was in C++ auch einfach nur 0 entspricht) und gibst 0 zurück wenn es 0 ist. Dann kannst du gleich 0 zurück geben.
rudpower schrieb:
c_Edu32fp* TF_Gaszaehler::GetEdu32fp() //in der Form des Messgeräts { // if (o_Edu32fp!=NULL) return o_Edu32fp; // else return 0; return o_Edu32fp; }Dein fehler liegt einfach bei der verwendung von TF_Gaszaehler::GetEdu32fp().
double volumen = 0 ; if( gas_zaehler.GetEdu32fp() ){ // Wenn nicht 0 volumen = gas_zaehler.GetEdu32fp()->ErfasseVolumen(); }
-
ja mit dem return 0 das ist quatsch

Das Programm ist umfangreicher, ich versuchs mal kurz zu zeigen:
Habe eine Klasse c_Messwerte:class c_Messwerte { private: double m_volumen; public: double GetVolumen(void)const; void SetMesswerte(double p_vol); }; c_Messwerte::c_Messwerte() : m_volumen(0.0) {} void c_Messwerte::SetMesswerte(double p_vol) {m_volumen = p_vol;} double c_Messwerte::GetVolumen(void)const {return m_volumen;}dann hab ich eine Klasse für das Messgerät, hier c_Edu32fp:
class c_Edu32fp { private: bool m_connect; comclass m_port; public: c_Edu32fp(AnsiString p_port); ~c_Edu32fp(); bool GetConnect(); double ErfasseVolumen(); }; c_Edu32fp::c_Edu32fp(AnsiString p_port) : m_connect(0) { m_connect = m_port.OpenComm(p_port.c_str()); if(m_connect) { m_port.SetDCB(9600); m_port.SetReadTimeouts(100, 10, 10); } else ShowMessage("Ritteruhr EDU32FP nicht verbunden!"); } //--------------------------------------------------------------------------- c_Edu32fp::~c_Edu32fp() {if(m_connect) m_port.CloseComm();} //--------------------------------------------------------------------------- double c_Edu32fp::ErfasseVolumen() { if (this && m_connect) { char rec[50] = {0}; unsigned char send[] = {0x16}; while(m_port.ReceiveData(rec,1)>0); //Buffer leeren m_port.SendData(send,1); m_port.ReceiveData(rec,50); return StrToFloat(AnsiString(rec).SubString(4,9).Trim()); } else return 0; } bool c_Edu32fp::GetConnect() {return m_connect;}Nun gibt es die Form für das Messgerät wo auch das Objekt erzeugt wird:
class TF_Gaszaehler : public TForm { ... private: c_Edu32fp* o_Edu32fp; public: ... c_Edu32fp* GetEdu32fp(); }; void __fastcall TF_Gaszaehler::Gaszaehler_B_PortOeffnenClick(TObject *Sender) { o_Edu32fp = new c_Edu32fp(Gaszaehler_CB_Schnittstelle->Text); } //--------------------------------------------------------------------------- c_Edu32fp* TF_Gaszaehler::GetEdu32fp() { if (o_Edu32fp!=NULL) return o_Edu32fp; }diese GetMethode brauch ich um in einer Threadklasse darauf zuzugreifen:
class c_ThreadMesswerte : public TThread { private: LONGLONG frequenz, alt, neu; protected: void __fastcall Execute(); public: __fastcall c_ThreadMesswerte(bool CreateSuspended); void __fastcall VCLAccess(); }; __fastcall c_ThreadMesswerte::c_ThreadMesswerte(bool CreateSuspended) : TThread(CreateSuspended) { QueryPerformanceFrequency((LARGE_INTEGER*)&frequenz); Priority = tpHighest; } //--------------------------------------------------------------------------- void __fastcall c_ThreadMesswerte::Execute() { QueryPerformanceCounter((LARGE_INTEGER*)&alt); while(!Terminated) { QueryPerformanceCounter((LARGE_INTEGER*)&neu); if ((neu-alt)/(double)frequenz >= 0.1) Synchronize(VCLAccess); Sleep(1); } } //--------------------------------------------------------------------------- void __fastcall c_ThreadMesswerte::VCLAccess() { F_HO->HO_E_AnzeigeGaszaehlerVolumen->Text = FloatToStr(F_Gaszaehler->GetEdu32fp()->ErfasseVolumen()); // Anzeige des Messwerts auf der Hauptoberfläche (HO) }Zusätzlich wird in der Formklasse der Hauptoberfläche alles in einem Vektor gespeichert. Hier wird auch der Thread gestartet oder pausiert.
class TF_HO : public TForm { private: std::vector<c_Messwerte>v_Messwerte; c_ThreadMesswerte* o_ThreadMesswerte; c_Messwerte o_Messwerte; public: void ErfasseMessdaten(int AnzahlMesswerte); }; void TF_HO::ErfasseMessdaten(int AnzahlMesswerte) { o_Messwerte.SetMesswerte(F_Gaszaehler->GetEdu32fp()->ErfasseVolumen()); v_Messwerte.reserve(AnzahlMesswerte); v_Messwerte.push_back(o_Messwerte); }Natürlich sind es noch viel mehr Messgeräte und Messwerte. Habe der Einfachheit halber mal alles rausgenommen und auch die Methoden der Forms usw rausgenommen.
Die Daten werden auch noch gespeichert, angezeigt usw.
Deshalb muss ich aufpassen, da eine Messung (in dem Fall starten des Threads und gleichzeitiges Füllen des Vektors) auch möglich sein soll wenn nicht alle Geräte verbunden sind. Es gibt einen Start Button, der den Thread startet (Resume) und gleichzeitig den Vektor füllt (zB 100 mal alle 1 Sek.).Funktionieren tut es so, ausser dass es Probleme gibt bei der Typumwandlung AnsiString in double.
-
was mich grad etwas wundert ist, dass m_connect beim Start nicht false ist obwohl ich es mit false initialisiere im Konstruktor. Das habe ich gerade mit dem Debugger erfahren.
-
Bei sowas
o_Messwerte.SetMesswerte(F_Gaszaehler->GetEdu32fp()->ErfasseVolumen());Wunderst du dich über SegFaults?
Wenn F_Gaszaehler->GetEdu32fp() NULL zurück liefert, passiert das ja.
Du solltest da den Rückgabewert erst auf NULL prüfen (am besten speicherst du den) bevor du Funktionen darauf aufrufst.Wo hast du dich mit dem Debugger reingehängt, um das m_connect zu überprüfen?
Im übrigen ist m_connect ein bool und kein int, den darf man dann direkt mit "false" statt "0" initialisieren. Find ich persönlich einfacher, da ich gleich sehe "Ah, bool".
-
Du kannst das z.B. so machen:
o_Messwerte.SetMesswerte( F_Gaszaehler->GetEdu32fp() ? F_Gaszaehler->GetEdu32fp()->ErfasseVolumen() // Wenn der zurück gegebene Zeiger nicht 0 ist, setze den Wert von ErfasseVolumen() : 0 // Wenn der Zeiger 0 ist dann setz 0 );Oder so, was das gleiche ist nur mit if else anstatt dem ternären operator
if( F_Gaszaehler->GetEdu32fp() ) { o_Messwerte.SetMesswerte( F_Gaszaehler->GetEdu32fp()->ErfasseVolumen() ); // Wenn der zurück gegebene Zeiger nicht 0 ist, setze den Wert von ErfasseVolumen() } else { o_Messwerte.SetMesswerte( 0 ); // Wenn der Zeiger 0 ist dann setz 0 }
-
ja ok, das mit dem bool leuchtet ein. Hab ich gleich mal geändert. Hab mich bei if (m_connect) mit dem Debugger reingehängt. Dann zeigt er an, dass m_connect==true obwohl kein Gerät verbunden ist.
-
rudpower schrieb:
Dann zeigt er an, dass m_connect==true obwohl kein Gerät verbunden ist.
Schau eine Zeile höher:
m_connect = m_port.OpenComm(p_port.c_str());Da weist du dem m_connect einen neuen Wert zu. Wenn eine Zeile darunter m_connect nicht mehr false ist heißt das m_port.OpenComm() hat true zurück gegeben.
Wenn also keine Verbindung besteht heißt das wiederum dass OpenComm Mist baut.
Außer{ m_port.SetDCB(9600); m_port.SetReadTimeouts(100, 10, 10); }machen m_port irgendwie ungültig. Da aber hier über comclass nichts bekannt ist kann man auch nichts darüber sagen.
-
hier ist die comclass:
// Öffnet den Port und gibt ein Handle zurück bool comclass::OpenComm(char* port){ hCom = CreateFile(port,// z.B. "COM1", GENERIC_READ|GENERIC_WRITE,//zum Senden und Empfangen 0, // Für comm devices exclusive-access notwendig 0, // Keine security attributes OPEN_EXISTING, // Für comm devices notwendig 0, // Kein overlapped I/O 0); // Für comm devices muss hTemplate NULL sein if(hCom == INVALID_HANDLE_VALUE) return false; else return true; } // setzen der Einstellungen void comclass::SetDCB(int baud){ DCB dcb; // Device Control Block GetCommState(hCom, &dcb); // DCB lesen dcb.BaudRate = baud; // Baudrate z.B.:9600 dcb.ByteSize = 8; // 8 Datenbits dcb.Parity = NOPARITY; // keine Parity dcb.StopBits = ONESTOPBIT; // 1 Stopbit dcb.fDtrControl = 1; // SetCommState konfiguriert die serielle Schnittstelle SetCommState(hCom, &dcb); } // setzen der Timeout-Zeiten void comclass::SetReadTimeouts(int intervalTO, int readSingleTO, int writeSingleTO){ COMMTIMEOUTS t; // Alle Wert in Millisekunden // Werte für ReadFile: t.ReadIntervalTimeout=intervalTO; // Zeit zwischen zwei Zeichen t.ReadTotalTimeoutMultiplier=readSingleTO; // pro Zeichen t.ReadTotalTimeoutConstant=readSingleTO; // Werte für WriteFile: wenn beide 0 sind, kein Timeout // beim Schreiben t.WriteTotalTimeoutMultiplier=writeSingleTO; t.WriteTotalTimeoutConstant=writeSingleTO; SetCommTimeouts(hCom,&t); } // Daten senden int comclass::SendData(char Data[],int n){ DWORD NumberOfBytesWritten; //Anzahl der gesendeten Bytes WriteFile(hCom, // handle des Com-Ports Data, // Adresse der Daten n, // Anzahl der zu sendenden Bytes &NumberOfBytesWritten, // Adresse übergeben 0); // kein overlapped I/O return NumberOfBytesWritten; } // Daten empfangen DWORD comclass::ReceiveData(char* Data,int n){ DWORD NumberOfBytesRead; // Anzahl der gelesenen Bytes ReadFile(hCom, // handle des Com-Ports Data, // Adresse des Datenpuffers n, // Anzahl der zu lesenden Bytes &NumberOfBytesRead, // Adresse übergeben 0); // kein overlapped I/O return NumberOfBytesRead; } // schließen der seriellen Schnittstelle void comclass::CloseComm(void){ try { if(hCom) CloseHandle(hCom); } catch(...) { ; } }
-
double c_Edu32fp::ErfasseVolumen() { if (m_connect) //hier hatte ich den Haltepunkt gesetzt .... }Hier dürfte er ja gar nicht reinlaufen, da ich doch m_connect mit false im Konstruktor initialisiert habe.
-
Da er dort in die Schleife läuft gibt es denk ich mal meinen EConvertError "" ist kein gültiger Gleitkommawert.
zeigt er in dieser Zeile an:
"F_HO->HO_E_AnzeigeGaszaehlerVolumen->Text = FloatToStr(F_Gaszaehler->GetEdu32fp()->ErfasseVolumen());"
-
ich glaube, das Problem ist, dass er den Konstruktor gar nicht aufruft, da dieser ja erst mit dem Klick auf den Button Gaszaehler_B_PortOeffnen aufruft. Normalerweise würde ich ja das Objekt im Konstruktor der Form F_Gaszaehler erzeugen. Da aber der Konstruktor der Klasse c_Edu32fp einen Parameter erwartet (den Namen der Schnittstelle) kann ich das nicht machen. Wie kann ich das Problem beheben?
-
habe das Problem insoweit gelöst, dass ich nun in der Geräteklasse eine neue Methode erstellt hab, die den COM-Port öffnet und als Parameter diesen entgegennimmt. Den Konstruktor konnte ich so mit dem Konstruktor der Form aufrufen. So existiert das Objekt immer während der Laufzeit. Jetzt kann ich das Programm auch laufen lassen wenn das Gerät ausgeschaltet ist bzw man nicht den Button Port öffnen klickt, der ja die neue Methode öffnet. Verbindet man aber das Gerät (also ruft die Get Methode auf:
void c_Messgeraet::PortOeffnen(AnsiString p_port) { m_connect = m_port.OpenComm(p_port.c_str()); if(m_connect) { m_port.SetDCB(9600); m_port.SetReadTimeouts(100, 10, 10); } else ShowMessage("Messgerät nicht verbunden!"); }und schaltet dann das Gerät ab hab ich wieder eine Exception. Dann funktioniert die Umwandlung String in double nicht mehr. Er versucht dann "" in double zu wandeln (also einen Leerstring). Das ist nicht gut. Das gleiche passiert beim einschalten, da das Gerät dann als String die Typbezeichnung sendet und die natürlich nicht in double gewandelt werden kann.
-
nun funktioniert das soweit ganz gut. Hier meine Methode zur Erfassung:
double c_Messgeraet::ErfasseVolumen() { if (m_connect) { char rec[50] = {0}; unsigned char send[] = {0x16}; while(m_port.ReceiveData(rec,1)>0); // Buffer leeren m_port.SendData(send,1); m_port.ReceiveData(rec,50); try {return StrToFloat(AnsiString(rec).SubString(4,9).Trim());} catch(...) { m_connect = false; return 0; } } else return 0; }wenn das Gerät nicht verbunden ist, steht in dem Feld rec alles /0. Durch die Umwandlung in AnsiString und Trim bleibt dann nur ein Leerstring "".Da dies nicht in ein Float umgewandelt werden kann fange ich dies mit try/catch ab. Das funktioniert.
Beim Aufruf der Methode in meinem Thread wird dann im Editfeld entweder der Messwert ausgegeben oder der AnsiString "getrennt":if (F_Messgeraet->GetMessgeraet()->GetConnect()) F_HO->E_AnzeigeVolumen->Text = FloatToStr(F_Messgeraet->GetMessgeraet()->ErfasseVolumen()); else F_HO->E_AnzeigeVolumen->Text = "getrennt";Problem gibt es in der PortOeffnen-Methode:
void c_Messgeraet::PortOeffnen(AnsiString p_port) { m_connect = m_port.OpenComm(p_port.c_str()); if(m_connect) { m_port.SetDCB(9600); m_port.SetReadTimeouts(100,10,10); } else { m_port.CloseComm(); ShowMessage("Messgerät nicht verbunden!"); } }Gebe ich jetzt nachdem verbunden wurde und die Messwerte auch angezeigt werden hier
void __fastcall TF_Messgeraet::B_OpenPortClick(TObject *Sender) { o_Messgeraet->SetConnect(false); o_Messgeraet->PortOeffnen(E_SerialPort->Text); //Argument aus Editfeld Close(); }einen anderen Port an zeigt er wie gewünscht "getrennt" an. Gebe ich nun wieder den richtigen Port an läuft er immer wieder in die else Verzweigung der PortOeffnen(AnsiString p_port)-Methode. Er setzt m_connect alo nicht auf true und verbindet nicht neu.
Ich hab dann versucht einfach m_port.CloseComm(); vorher aufzurufen. Das gibt dann gleich eine "externe Exception C0000008".