Problem bei Datenspeicherung
-
Hallo
Ja, das kann man so machen, dein Codeausszug zeigt keine groben Schwächen.
(Das TDateTime sowohl Datum als auch Zeit gleichzeitig speichern kann, wurde dir ja schon gesagt)bis bald
akari
-
Datensatz packe ich wieder in einen Vektor, da es mehrere Datensätze sind.
Nun muss ich an die einzelnen Messwerte rankommen, also an die Elemente von vMesswerte. Wenn ich z.B. an die Temperaturen im dritten Datensatz rankommen will muss ich doch das so schreiben oder?for (unsigned int i = 0; i<vDatensatz[2].vMesswerte.size(); i++) Memo1->Lines->Add(vDatensatz[2].vMesswerte[i].temperatur);
zu dem TDateTime. Ich hatte das so gemacht da ich Zeit und Datum in der Tabelle in je eine eigene Zelle schreiben wollte. Wenn ich in der Struktur schreibe:
struct Datensatz {TDateTime zeit;} und bei der Zuweisung dannzeit = Now();
Wie komm ich dann an die einzelnen Strings Uhrzeit und Datum? Da gabs doch was mit TimeString() und DateString? kann man das so schreiben: Memo1->Lines->Add(zeit.TimeString());
//Edit: ok ich seh grad das das mit TimeString() und DateString() funktioniert
-
Kurze Zwischenfrage:
Enthält dein Messwertdatensatz keinen Zeitstempel, du hast nur die Messwerte? Oder willst du sowas haben:
struct Messwert { TDateTime Timestamp_; float Temperatur_; }; struct Messung { TDateTime StartTimestamp_ TDateTime StopTimestamp_; AnsiString MessungsName_; AnsiString BenutzerName_; AnsiString Kommentar_; std::vector<Messwert> Messwerte_; };
Ohne den Zeitstempel des Messwerts kannst du z.B. keine Zeitverläufe erzeugen, und das wird mit Sicherheit irgendwann verlangt.
-
doch hat ich hier nur weggelassen um das Programm übersichtlicher zu halten. Da gibts auch noch jede menge mehr Variablen. Ich übergeb dann einfach die Systemzeit mit Now(), oder ist es besser den Systemtakt auszulesen und diesen Wert zu nehmen? Ich muss dazu sagen, dass der Prozess nicht so zeitkritisch ist und die Messwerte nur alle paar Minuten über einen langen Zeitraum über einen Timer ausgelesen werden.
Ich hab gerade das Problem das zwar die Messwerte in den Vektor geschrieben werden aber halt gleich alle. Es sollen aber nur für jeden Datensatz die Messwerte gespeichert werden. Ich vermute das Problem in meinem Timer. Hier mal der Timer, der ein einstellbares Intervall in Sekunden hat sowie die Anzahl der Messungen:
void __fastcall TForm1::btnStartClick(TObject *Sender) //Button startet den Timer { timDatenerfassung->Enabled = true; timDatenerfassung->Interval = StrToInt(txtAbstandMessungen->Text) * 1000; anzahlMesswerte = 0; datensatz.zeit = Now(); // Startzeitpunkt dür den aktuellen Datensatz }
void __fastcall TForm1::btnStopClick(TObject *Sender) { timDatenerfassung->Enabled = false; }
Timer:
void __fastcall TForm1::timDatenerfassungTimer(TObject *Sender) { anzahlMesswerte++; if (anzahlMesswerte <= StrToInt(txtAnzahlMessungen->Text)) { labStatus->Caption = "Status: "+IntToStr(anzahlMesswerte)+" / "+txtAnzahlMessungen->Text; messwerte.temperatur = rand(); //erst mal nur random-Messwerte datensatz.vMesswerte.push_back(messwerte); datensatz.bezeichnung = txtBezeichnung->Text; datensatz.bearbeiter = txtBearbeiter->Text; datensatz.kommentar = txtKommentar->Text; datensatz.messpunkte = datensatz.vMesswerte.size(); } else { timDatenerfassung->Enabled = false; //stoppt den Timer nach den Messungen vDatensatz.push_back(datensatz); //Datensatz nach der Messung in Vektor speichern grdDatensaetze->RowCount = vDatensatz.size()+1; grdDatensaetze->Cells[0][vDatensatz.size()] = vDatensatz.size(); grdDatensaetze->Cells[1][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].zeit.DateString(); grdDatensaetze->Cells[2][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].zeit.TimeString(); grdDatensaetze->Cells[3][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].bezeichnung; grdDatensaetze->Cells[4][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].bearbeiter; grdDatensaetze->Cells[5][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].kommentar; grdDatensaetze->Cells[6][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].messpunkte; } }
drücke ich auf den StartButton,gebe 5 in das Editfeld txtAnzahlMessungen ein und 1s für den Timer wird nach dem Durchlauf die Tabelle grdDatensaetze in der ersten Reihe mit den Daten gefüllt. Bei Messpunkte wird auch 5 angezeigt. Starte ich nun den Timer erneut und warte die 5 Sek ab steht plötzlich 10 bei Messpunkten. Irgendwo steckt da noch der Wurm drin.
-
hab jetzt die Messdaten zu den einzelnen Datensätzen mal ausgegeben und musste leider feststellen, das es bei jeden Datensatz immer die gleichen sind.
Nun hatte ich die Idee einfach für jeden Messwert einen Vektor zu nehmen. Kann man das machen. Am Ende werden es so 10 - 15 unterschiedliche Messwerte sein mit bis zu 10000 Einzelmesswerten:
struct Datensatz { TDateTime startzeit; AnsiString bezeichnung; AnsiString bearbeiter; AnsiString kommentar; int messpunkte; std::vector<float>vTemperatur; std::vector<float>Phwert; // hier noch weitere Vektoren std::vector<TDateTime>vTimestamp; }datensatz; std::vector<Datensatz>vDatensatz;
Füllen des Vektors mit Daten im Timer:
datensatz.vTemperatur.push_back(rand());
und den Datensatz speicher ich dann wieder in einem Vektor:
vDatensatz.push_back(datensatz);
Kann ich das so machen?
-
Hallo,
Klar kann man das so machen. Ich würde aber doch die messwerte in einer struct kapseln und die dann in den Vektor packen. Wozu ist eigentlich messpunkte da?
struct Datensatz { TDateTime startzeit; AnsiString bezeichnung; AnsiString bearbeiter; AnsiString kommentar; struct Messpunkt { float m_temperatur; float m_ph_wert; TDateTime m_time; Messpunkt(float temp, float ph, TDtaeTime time) : m_temperatur(temp), m_ph_wert(ph), m_time(time) {} Messpunkt() : m_temperatur(0.0), m_ph_wert(0.0) {} }; std::vector<Messpunkt> messpunkt; } Datensatz datensatz; std::vector<Datensatz> vDatensatz;
Ich habe mal gleich Konstruktoren bei Messpunkt eingefügt, da man die bestimmt brauchen kann.
-
ah ok, dann werd ich das so umsetzen. Die Variable messpunkte ist da, um die Anzahl der Messungen pro Datensatz abzuspeichern.
Ich danke erst mal für die schnelle Hilfe.
-
rudpower schrieb:
Die Variable messpunkte ist da, um die Anzahl der Messungen pro Datensatz abzuspeichern.
Dafür hat vector die Memberfunktion size().
-
ja ok, das leuchtet ein. Hab jetzt den Timer soweit fertig und wollte nun die Datensaetze in eine ComboBox schreiben zB:
"DatensatzNummer. Bezeichnung (AnzahlMesswerte Messwerte)"
(also zB. 1. Testmessung (20 Messwerte))
Dann kann man die Datensätze in der Combobox auswählen und in einer Tabelle werden die entsprechenden Messwerte oder eine Grafik angezeigt.Aber mit der ComboBox gibts ein kleines Problem. Wollt ich so machen, gab aber einen Fehler:
//Datensatz in ComboBox cmbDatensaetze schreiben: cmbDatensaetze->Items->Add( IntToStr(vDatensatz.size()) + ". " + vDatensatz[vDatensatz.size()-1].bezeichnung + " (" + IntToStr(vDatensatz[vDatensatz.size()-1].vMesspunkt.size()) + ")");
da gibts einen Mehrdeutigkeitsfehler zw. IntToStr(int) und IntToStr(__int64). Was kann ich da machen?
hier noch mal der ganze Timer:
void __fastcall TfrmHO::timDatenerfassungTimer(TObject *Sender) { timDatenerfassung->Tag++; if (timDatenerfassung->Tag <= StrToIntDef(txtAnzahlMessungen->Text,0)) { datensatz.messpunkt.Temperatur = rand(); datensatz.messpunkt.messzeit = Now(); datensatz.vMesspunkt.push_back(datensatz.messpunkt); //speichert Messwerte in Vektor } else { timDatenerfassung->Enabled = false; datensatz.bezeichnung = txtBezeichnung->Text; datensatz.bearbeiter = txtBearbeiter->Text; datensatz.kommentar = txtKommentar->Text; vDatensatz.push_back(datensatz); //Datensatz in Tabelle eintragen: grdDatensaetze->RowCount++; grdDatensaetze->FixedRows = 1; grdDatensaetze->Cells[0][vDatensatz.size()] = vDatensatz.size(); grdDatensaetze->Cells[1][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].startzeit.DateString(); grdDatensaetze->Cells[2][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].startzeit.TimeString(); grdDatensaetze->Cells[3][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].bezeichnung; grdDatensaetze->Cells[4][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].bearbeiter; grdDatensaetze->Cells[5][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].kommentar; grdDatensaetze->Cells[6][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].vMesspunkt.size(); //Datensatz in ComboBox cmbDatensaetze schreiben: cmbDatensaetze->Items->Add( IntToStr(vDatensatz.size()) + ". " + vDatensatz[vDatensatz.size()-1].bezeichnung + " (" + IntToStr(vDatensatz[vDatensatz.size()-1].vMesspunkt.size()) + ")"); } }
-
mach ich es so:
//Datensatz in ComboBox cmbDatensaetze schreiben: AnsiString s = vDatensatz.size(); s += ". " + vDatensatz[vDatensatz.size()-1].bezeichnung + " ("; s += vDatensatz[vDatensatz.size()-1].vMesspunkt.size(); s += " Messwerte)"; cmbDatensaetze->Items->Add(s);
funktioniert das ganze. Aber ich finds nicht grad übersichtlich und nachvollziehbar. Kann man das vll. besser machen?
-
vDatensatz[vDatensatz.size()-1].vMesspunkt.size()
gibt ein unsigend int zurück
Der Compiler bittet dich mit den Aufruf
Mehrdeutigkeitsfehler zw. IntToStr(int) und IntToStr(__int64)
ihn doch bitte anzugeben ob du an der Funktion einen int oder ein __int64 übergeben willst.hier z.B. ist angegeben das es ein in sein soll.
IntToStr((int)vDatensatz[vDatensatz.size()-1].vMesspunkt.size())
-
ah ok, danke.
Hab jetzt das Programm wie oben fertig aber da gibts noch ein Problem. In der Zeile
grdDatensaetze->Cells[6][vDatensatz.size()] = vDatensatz[vDatensatz.size()-1].vMesspunkt.size();
wird ja die Anzahl der Messwerte des jeweiligen Datnsatzes angezeigt. Starte ich den Timer wird der Datensatz angelegt und die Anzahl der Messwerte wird auch ordnungsgemäß in die Tabelle eingetragen. Starte ich dann die Messung (also den Timer) erneut stimmt die Anzahl der Messwerte nicht mehr. Nun wird die Anzahl für beide Datensätze angezeigt.
Habe jetzt die struct in eine externe Datei ausgelagert.Das Objekt datensatz und den Vektor vDatensatz erzeug ich in der Header der Form (als private). Wo erzeuge ich das Objekt der Struktur Messpunkt? Doch normalerweise hier oder?:
struct Datensatz { //... struct Messpunkt { //... }messpunkt; //??? hier erzeugen? std::vector<Messpunkt> messpunkt; }datensatz; //darf ich das Objekt datensatz auch hier erzeugen?
mein Timer:
void __fastcall TfrmHO::timDatenerfassungTimer(TObject *Sender) { timDatenerfassung->Tag++; //beim Start des Timers wird Tag über einen Button (btnStart) auf 0 gesetzt if (timDatenerfassung->Tag <= StrToIntDef(txtAnzahlMessungen->Text,0)) //txt Anzahl ist ein Edit zur Eingabe der Anzahl der Messungen { datensatz.messpunkt.temperatur = rand(); datensatz.messpunkt.messzeit = Now(); datensatz.vMesspunkt.push_back(datensatz.messpunkt); //speichert Messwerte in Vektor } else //wenn der Timer durch ist { timDatenerfassung->Enabled = false; datensatz.bezeichnung = txtBezeichnung->Text; datensatz.bearbeiter = txtBearbeiter->Text; datensatz.kommentar = txtKommentar->Text; vDatensatz.push_back(datensatz); //danach werden die Datensätze in eine Tabelle nacheinander eingetragen } }
Grüße rudpower
-
Ich würde lieber die Deklaration von der Definition trennen. Desweiteren ist die Variable messpunkt ja nur temporär nötig und muss somit gar nicht in die Klasse.
struct Datensatz { //... struct Messpunkt { //... }; // messpunkt; //??? hier erzeugen? - Nein, wird nicht gebraucht. std::vector<Messpunkt> messpunkt; }// datensatz; darf ich das Objekt datensatz auch hier erzeugen? // Du darfst das. Ich finde das aber nicht übersichtlich
besser finde ich:
struct Datensatz { //... struct Messpunkt { //... }; std::vector<Messpunkt> messpunkt; } Datensatz datensatz;
dann noch
void __fastcall TfrmHO::timDatenerfassungTimer(TObject *Sender) { timDatenerfassung->Tag++; //beim Start des Timers wird Tag über einen Button (btnStart) auf 0 gesetzt if (timDatenerfassung->Tag <= StrToIntDef(txtAnzahlMessungen->Text,0)) //txt Anzahl ist ein Edit zur Eingabe der Anzahl der Messungen { Datensatz::Messpunkt tempmess; tempmess.temperatur = rand(); tempmess.messzeit = Now(); datensatz.vMesspunkt.push_back(tempmess); //speichert Messwerte in Vektor // oder kürzer mit Konstruktor von Messpunkt datensatz.vMesspunkt.push_back(Datensatz::Messpunkt(rand(), 0.0, Now())); } else //wenn der Timer durch ist { timDatenerfassung->Enabled = false; datensatz.bezeichnung = txtBezeichnung->Text; datensatz.bearbeiter = txtBearbeiter->Text; datensatz.kommentar = txtKommentar->Text; vDatensatz.push_back(datensatz); //danach werden die Datensätze in eine Tabelle nacheinander eingetragen } }
-
Das ist Käse!
Einen Messpunkt im Datensatz zu haben, diesen mit Daten zu füllen und dann in den Vektor der Messpunkte einzufügen ist kein guter Ansatz. Du solltest dir vorher über Ablauflogik und Design deiner Datenstrukturen Gedanken machen, wie es logisch zusammenpasst. Bei deinem Ansatz habe ich den Eindruck, dass du solange Sachen rumgeschoben hast, bis es funktioniert, jetzt aber alles andere als intuitiv zu verstehen ist.
Grundsätzlich sollte jede Funktion genau eine Aufgabe erledigen, dein Timer sollte zwar entscheiden, wann eine Messung gestoppt wird, aber selbst keine Daten in Datenstrukturen eintragen. Überhaupt solltest du GUI und Funktionalität voneinander trennen.
Wie auch immer, ein besserer Ansatz ist folgender:
void __fastcall TfrmHO::OnClickBtnStart( TObject* Sender ) { // neue Messung starten StarteMessung( txtBezeichnung->Text, txtBearbeiter->Text, txtKommentar->Text ); } void __fastcall TfrmH0::OnTimer( TObject* Sender ) { // Anzahl der Datensätze erfasst? if( Messungen.back().Messdaten.size() < StrToIntDef( txtAnzahlMessungen->Text,0 ) ) { ErfasseDaten(); } else { StoppeAktuelleMessung(); } } void StarteMessung( const AnsiString& Bezeichnung, const AnsiString& Bearbeiter, const AnsiString& Kommentar ) { // neues Messungsobjekt erzeugen und in Vektor eintragen DatenSatz Messung; Messung.Bezeichnung = Bezeichnung; Messung.Bearbeiter = Bearbeiter; Messung.Kommentar = Kommentar; Messung.StartZeit = Now(); Messungen.push_back( Messung ); } void StoppeAktuelleMessung() { // Vektor mit Messungen darf hier nicht leer sein assert( !Messungen.empty() ); // Stoppzeitpunkt in letzte Messung eintragen Messungen.back().StoppZeit = Now(); } void ErfasseDaten() { // Vektor mit Messungen darf hier nicht leer sein assert( !Messungen.empty() ); Messdaten Daten; Daten.Zeitpunkt = Now(); Daten.Temperatur = 21.0; Daten.Irgendwas = WasAnderes; Messungen.back().Messdaten.push_back( Daten ); }
Die drei Funktionen sind freie Funktionen, können aber auch als Member einer Dokumentklasse realsiert werden. Die Variable Messungen kann eine globale Variable sein (besser nicht!), ein Member eben dieser Dokumentklasse sein oder von einem Singleton bereitgestellt werden.
Du müsstest auch noch dafür sorgen, dass keine neue Messung gestartet werden kann, solange eine andere Messung bereits läuft (zweimaliges Klicken des Start Buttons verhindern), da sonst für die aktuelle Messung kein Stoppzeitstempel gesetzt wird.
Edit:
Ein Attribut eines X-beliebigen GUI Elements (hier konkret: TTimer) als Zähler zu missbrauchen ist noch größerer Käse. Bei kleineren Projekten mag das funktionieren, aber wenn das Projekt wächst und neue Features dazukommen verliert man schneller den Überblick, als einem lieb ist. Wenn du irgendetwas zählen musst dann führ halt eine Zählvariable ein. Oder guck, ob die Wert nicht irgendwo schon implizit zur Verfügung steht (konkret hier: size() von std::vector liefert dir die Anzahl der bereits erfassten Messdaten der aktuellen Messung)
-
was macht
assert( !Messungen.empty() );
? assert kenn ich noch gar nicht.
Die Befehle zum starten und stoppen des Timers schreib ich in die Buttons oder in die jeweilige Funktion?
Messungen.back()-> die Funktion back kenn ich auch noch nicht, was macht die?
-
Hallo
1. assert
2. In den Button-Events rufst du Thread::Start und ::Stop auf
3. std::vector::backbis bald
akari
-
das assert beendet also das Programm wenn der Vektor 0 ist?
ein Problem habe ich noch mit Thread::Start und Thread::Stop. Was hat es damit auf sich? Den Timer starte ich doch mit Enabled=true oder nicht?
-
Hallo
1. Ja, der Vektor darf an dieser Stelle nicht leer sein, sonst ist irgendwas ganz mächtig schief gegangen und es ist sicherer, das Programm zu beenden. Diese Fehlerbehandlung ist also noch rabiater als eine Exception.
2. Ja, aber wenn der Benutzer in der Lage sein soll den Thread im Lauf abbzubrechen und ihn dann wieder neu zu starten, dann brauchst du diese extra Behandlungen.
bis bald
akari
-
In welchen Fall soll denn der Vektor 0 sein, das leuchtet mir nicht so recht ein?
Einen Thread habe ich doch gar nicht. Ausserdem starte ich doch einen Thread mit Resume und stoppe mit suspend bzw terminate. Was meinst du damit?