kramann.info
© Guido Kramann

Login: Passwort:










7.1 Datenaustausch und Automatisierung von Berechnungs- und Visualisierungsprozessen

ASCII-Dateien - Die einfachste Schnittstelle der Welt

  • Am einfachsten lassen sich Methoden für den Datenaustausch entwickeln, die auf die Erzeugung und Benutzung von Dateien im ASII-Format basieren.
  • Hierzu gibt es eine Reihe unterstützender Befehle auf der DOS-Ebene (Systembefehle / Eingabeaufforderung unter Windows), bzw. in den diversen Linux-Shells als Shellbefehle.
  • Unsere Betrachtungen beschränlken sich zwar auf Windows, lassen sich aber ohne weiteres auch auf Linux übertragen.

Exportieren und Importieren von Daten mit C++ - pipen

  • Es besteht die Möglichkeit Daten, die ein Programm normalerweise auf der Eingabeaufforderung ausgeben würde, in eine Datei umzuleiten.
  • Dieser Vorgang ist unter Linux/Unix als "pipen" bekannt und auch unter Windows unter Verwendung des Zeichens ">" möglich.
  • Das folgende C/C++ Programm gibt eine 3x3-Matrix in der DOS-Shell aus:
#include<iostream>
using namespace std;
int main()
{
    double matrix[9] = {1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9};
    for(int i=0;i<9;i++)
    {
        if(i%3==0 && i>0)
            cout<<endl;
        cout<<matrix[i]<<" ";
    }    
}

Code 7.1-1: 01_dreimaldreimatrix.cpp - Erstes Testprogramm zur Entwicklung von Datenaustauschskonzepten.

  • Wenn man eine Eingabeaufforderung öffnet (Start->Ausführen...->cmd), in das Verzeichnis geht, wo der Quelltext liegt (cd <Pfad>), ihn kompiliert (g++ -o <Zielname ohne Endung> <Name der Datei mit Quellcode>) und das entstandene Executable dort startet, erscheint die 3x3-Matrix in der Eingabeaufforderung.
  • Hier die entsprechende Befehlsfolge in der Eingabeaufforderung (Beispiel):
C:\Dokumente und Einstellungen\guido>cd ..
C:\Dokumente und Einstellungen>cd ..
C:\>cd lokal
C:\lokal>cd visualisierung
C:\lokal\visualisierung>g++ -o 01_dreimaldreimatrix 01_dreimaldreimatrix.cpp
C:\lokal\visualisierung>01_dreimaldreimatrix.exe
1.1 2.2 3.3
4.4 5.5 6.6
7.7 8.8 9.9
C:\lokal\visualisierung>

Code 7.1-2: Befehlsfolge zum Kompilieren und Starten eines C/C++ - Programms.

  • Wir nehmen an, die ausgegebene Matrix sei ein Berechnungsergebnis und wir wollen es in eine Datei umleiten.
  • Ohne Änderung des Programms kann dies durch "pipen" geschehen.
  • Durch folgenden Befehl wird die Ausgabe in die Datei "matrix.txt" umgeleitet:
C:\lokal\visualisierung>01_dreimaldreimatrix.exe > matrix.txt

Code 7.1-3: Umleiten der Ausgabe in die Datei matrix.txt

  • Um nicht immer diese Umleitung von Hand vornehmen zu müssen, lassen sich auch die Abfolge der notwendigen Befehle in ein Shellscript schreiben.
  • Unter Linux kann jede beliebige Datei ausführbar gemacht werden, unter Windows sind Textdateien mit der Endung .bat ausführbar, so genannte Batch-Dateien.
  • Für unser Beispiel erzeugen wir eine Textdatei und ändern deren Endung in .bat
  • Die Datei heißt dann "ausfuehren.bat", kann durch Doppelklicken ausgeführt werden.
  • Sie beinhaltet folgende Befehlszeilen (das Programm wird auch neu kompiliert und matrix.txt nach Ausführung im Notepad angezeigt.):
g++ -o 01_dreimaldreimatrix 01_dreimaldreimatrix.cpp
01_dreimaldreimatrix.exe > matrix.txt
notepad matrix.txt

Code 7.1-4: ausfuehren.bat - Befehlszeilen in der Batchdatei.

Exportieren und Importieren von Daten mit C++ - fstream

  • C++ bietet die Möglichkeit, eine Umleitung der Standardausgabe auf eine Datei innerhalb des Programms vorzunehmen.
  • Dies gelingt mit Hilfe der Befehle der Library fstream
  • Das folgende Programm integriert einen linearen Einmasseschwinger mittels Euler-Integration und speichert die Größen t,x und v spaltenweise in der Datei schwinger.txt:
#include<iostream>
#include<vector>
#include<fstream>
using namespace std;
int main()
{
   double m=1.0; //Masse m=1kg
   double C=1.0; //Federkonstante C=1N/m
   double D=0.1; //Dämpfungskonstante D=1Ns/m
   double t0=0.0,x0=1.0,v0=0.0; //Anfangsbedingungen
   double t,x,v,xneu,vneu;      //Zeit und die Zustandsgrößen
   double dt=0.01;              //Zeitschrittweite
   double f0,f1;                //Variablen zur Berechnung der aktuellen Steigung (rechte Seite des DGLS)   
   double tend = 30.0;           //Endzeitpunkt der Simulation
   vector<double> t_merker,x_merker,v_merker; //Vektorelemente, um den Verlauf der Simulation zwischenzuspeichern.
   //Anfangsbedingungen einstellen:
   x=x0;
   v=v0;
   t=t0;
   //***********************
   //Simulation durchführen:
   //***********************
   while(t<=tend)
   {
        //aktuellen Zustand speichern:
        t_merker.push_back(t);
        x_merker.push_back(x);
        v_merker.push_back(v);
        //Aktuelle Steigung berechnen:
        //DGLS:
        //x'=v
        //v'=-(C/m)*x-(D/m)*v
        f0=v;
        f1=-(C/m)*x-(D/m)*v;
        //Integrationsschritt durchführen:
        xneu = x + f0*dt;
        vneu = v + f1*dt;        
        //Ergebnis zurückschreiben:
        x = xneu;
        v = vneu;
        t+=dt; //Delta t hinzufügen
   }
   //*********************************
   //Simulationsergebnisse abspeichern
   //*********************************
   ofstream dateiausgabe("schwinger.txt"); //Stream zur Ausgabe in Datei öffnen.
   //Daten in Datei schreiben:
   for(int i=0;i<t_merker.size();i++)
       dateiausgabe<<t_merker[i]<<" "<<x_merker[i]<<" "<<v_merker[i]<<endl;
   //Stream schließen:
   dateiausgabe.close();
}

Code 7.1-5: 02_einmasseschwinger.cpp - Einmasseschwinger, Programm speichert die Größen t,x und v spaltenweise in der Datei schwinger.txt.

  • Die Daten können leicht mit Scilab eingelesen und geplottet werden.
  • Die folgenden Befehle befinden sich in dem Scilab-Skript mit dem Namen 02_schwinger.sce.
  • Es kann durch "exec 02_schwinger.sce" aus Scilab heraus gestartet werden, nachdem man durch cd <aktuelles Verzeichnis> in Scilab in das Verzeichnis gegangen ist, wo das Skript liegt:
m=fscanfMat('schwinger.txt');
plot(m(:,1),m(:,2),m(:,1),m(:,3));

Code 7.1-6: 02_schwinger.sce - Scilab-Skript zum laden der Daten in schwinger.txt als Matrix und zur Visualisierung der Zeitverläufe der Zustzandsgrößen x und v.

  • Wird nur diese eine Kurve oder wenige erzeugt, so bietet es sich an, die notwendigen Achsbeschriftungen interaktiv durchzuführen.
  • Dies geht direkt im Plot-Fenster über "edit->current axis properties" (vergl. nachfolgenden Screenshot):
  • Wie dies auch im Skript geschehen kann, wird in einem nachfolgenden Unterkapitel behandelt.
Screenshot des Plot-Fensters.

Bild 7.1-1: Screenshot des Plot-Fensters.

Laden von ASCII-Daten in C++
  • Nachdem nun aufgezeigt wurde, wie man Daten in C++ in eine Datei schreiben kann, sollen nun noch zwei einfache Möglichkeiten aufgezeigt werden, sie in ein C++-Programm zu laden.
  • Einerseits könnte ein vom Simulationsprogramm getrenntes C++ Programm die Simulationsdaten laden und wie zuvor das Scilab-Programm anzeigen.
  • Andererseits könnte man auch die Simulationsparameter in einer Extradatei als Zahlenliste ablegen und für die Simulation verwenden.
  • Der Übersichtlichkeit wegen wird hier nur der Ladevorgang gezeigt, ohne die nachfolgende Verarbeitung.
  • Ein Ansatz zur Visualisierung von Daten direkt in C++ erfolgt an späterer Stelle.
  • Zunächst wird aufgezeigt, wie man Parameter laden kann.
  • Hier gibt es wiederum zwei Möglichkeiten.
Erste Möglichkeit Parameter zu laden: direkte Übergabe an das verarbeitende Programm in der Eingabeaufforderung
  • Nachfolgendes Programm hat die Anzahl der übergebenen Parameter in der Variablen panz zur Verfügung.
  • Die Parameter selbst liegen zunächst als Array von Zeichenarray (also als Text) in der Variablen param vor.
  • Das Programm prüft zunächst, ob die Anzahl der übergebenen Parameter 3+1 ist.
  • In param befindet sich nämlich als erster Eintrag der Programmname selbst, also "03_paramlesen.exe".
  • Erst dann folgen die Parameter Masse m, Federsteifigkeit C und Dämpfung D.
  • Entsprechend muß das Programm in der Eingabeaufforderung aufgerufen werden, Beispiel:
  • 03_paramlesen.exe 2.0 1.0 0.1
  • Die drei Parameter werden also direkt hinter dem Programmnamen geschrieben und stehen im Anschluß im Programm zur Verfügung.
  • "panz" und "param" sind übringens frei gewählte Namen:
#include<iostream>
using namespace std;
int main(int panz, char** param)
{
    double m,C,D;
    if(panz!=3+1)
    {
        cout<<"So benutzt man dieses Programm:"<<endl;
        cout<<"03_paramlesen.exe <Masse m> <Federsteifigkeit C> <Dämpfung D>"<<endl;
        cout<<"Beispiel:"<<endl;
        cout<<"03_paramlesen.exe 2.0 1.0 0.1"<<endl;
    }
    else
    {
        m = atof(param[1]);
        C = atof(param[2]);
        D = atof(param[3]);
        cout<<"Masse m="<<m<<endl;
        cout<<"Federsteifigkeit C="<<C<<endl;
        cout<<"Daempfung  D="<<D<<endl;
    }
}

Code 7.1-7: 03_paramlesen.cpp - Diesem Programm werden drei Parameter direkt durch die Eingabeaufforderung übergeben.

  • Beispielsitzung in der Eingabeaufforderung:
C:\lokal\visualisierung>03_paramlesen.exe
So benutzt man dieses Programm:
03_paramlesen.exe <Masse m> <Federsteifigkeit C> <Dõmpfung D>
Beispiel:
03_paramlesen.exe 2.0 1.0 0.1
C:\lokal\visualisierung>03_paramlesen.exe 2.0 1.0 0.2
Masse m=2
Federsteifigkeit C=1
Daempfung  D=0.2
C:\lokal\visualisierung>

Code 7.1-8: Beispielsitzung in der Eingabeaufforderung.

Zweite Möglichkeit Parameter zu laden: Übergabe über eine Datei.
  • Als Vorbereitung zur Verwendung des nachfolgenden Programms, werden die Parameter in der Textdatei parameter.txt in einer Spalte gespeichert.
  • Der Inhalt dieser Datei sieht dann einfach so aus:
2.0
1.0
0.1

Code 7.1-9: parameter.txt - Datei mit Parameterliste (m,C und D).

  • Folgendes Programm öffnet nun die Datei als Stream und liest die nacheinander auftauchenden Elemente als double-Werte ein:
#include<iostream>
#include<fstream>
#include<vector>
using namespace std;
int main()
{
    double m,C,D;
    ifstream datenlesen("parameter.txt");
    double wert;
    vector<double> werte;  //Stream öffnen (Stream-Objekt anlegen)
    //Alle Zahlen in Datei einlesen:
    while(datenlesen>>wert)
        werte.push_back(wert);    
    //Stream schließen:
    datenlesen.close();
    m = werte[0];
    C = werte[1];
    D = werte[2];
    cout<<"Masse m="<<m<<endl;
    cout<<"Federsteifigkeit C="<<C<<endl;
    cout<<"Daempfung  D="<<D<<endl;   
}

Code 7.1-10: 04_paramdateilesen.cpp - Programm, dass die Zahlen in der Datei parameter.txt einliest.

Einlesen von Matrizen in C++
  • Würde man mit obiger Methode die Matrix mit den Simulationsdaten lesen, so ginge die Information der Zeilen und Spaltenanzahl verloren und alle Daten wären in einem einzigen Vektor gespeichert.
  • Statt dessen werden im folgenden erst die Zeilen der Datei einzeln eingelesen und im Anschluß die Zeilen in einzelne Zahlen zerlegt.
  • Durch Verwendung eines eigenen Streams für die Zeilen (stringstream), wird auch der zweite Zerlegungsschritt einfach.
  • Um eine gewisse Resistenz gegen Leerzeilen oder Beschriftungen im Kopf der Matrix zu erlangen, wird vor dem Hinzufügen einer Zeile beim Zusammenbau der Matrix geprüft, ob überhaupt Zahlen darin gefunden wurden.
#include<iostream>
#include<vector>
#include<fstream>
#include<sstream>
using namespace std;
int main()
{
    vector<vector<double>*> matrix;
    vector<string> zeilen;
    ifstream dateilesen("schwinger.txt");
    //Alle Zeilen einlesen
    char puffer[3000]; 
    while(dateilesen.getline(puffer,3000))
        zeilen.push_back(puffer);
    dateilesen.close();
    for(int i=0;i<zeilen.size();i++)//alle Zeilen zerlegen, wenn möglich
    {
        vector<double>* werte = new vector<double>();
        double wert;
        stringstream zeilenstream(zeilen[i]); //Zeile in Stream-Objekt verwandeln
        while(zeilenstream>>wert)  //solange in Zeile eine double-Zahl gefunden wird...
             werte->push_back(wert); 
        //Nur wenn werte nicht leer in matrix merken, sonst löschen:
        if(werte->size()>0)
            matrix.push_back(werte);
    }       
    //Das ganze in eine echte double-Matrix umwandeln:
    int anzahl_zeilen = matrix.size();
    int anzahl_spalten = matrix[0]->size();
    double** mat = new double*[anzahl_zeilen];   
    for(int i=0;i<anzahl_zeilen;i++)
        mat[i] = new double[anzahl_spalten];
    for(int i=0;i<anzahl_zeilen;i++)
        for(int k=0;k<anzahl_spalten;k++)
            mat[i][k] = matrix[i]->at(k);
    //Kontrollausgabe
    cout<<"eingelesene Matrix hat "<<anzahl_zeilen<<" Zeilen und "<<anzahl_spalten<<" Spalten, Inhalt:"<<endl;
    for(int i=0;i<anzahl_zeilen;i++)
    {
        for(int k=0;k<anzahl_spalten;k++)
        {
            cout<<mat[i][k]<<" ";
        }
        cout<<endl;
    }
}

Code 7.1-11: 05_matrizenlesen.cpp - Einlesen einer Matrix aus double-Werten variabler Größe in C++.

Starten von Scilab im Batch-Modus von einem C++ Programm aus
  • Anstatt die Steuerung von Scilab und einem C++ - Programm von Hand oder durch Batch-Dateien vorzunehmen, besteht auch die Möglichkeit durch die C-Funktion system() externe Programme von einem C/C++-Programm aus zu starten.
  • Zudem ist es möglich, Scilab per Konsolenbefehl zu starten.
  • Dies eröffnet die Möglichkeit alle Programm abläufe auch innerhalb eines C/C++-Programms zu organisieren, auch wenn ein, oder mehrere Scilab-Programme beteiligt sind.
  • Das folgende C++-Programm erzeugt zwei Scilab-Skripte, startet sie und verwendet auch die von den Skripten erzeugten Daten weiter.
  • Der absolute Pfad nach WScilex.exe muß eventuell bei eigenen Anwendungen angepaßt werden.
#include<iostream>
#include<vector>
#include<fstream>
#include<sstream>
using namespace std;
int main()
{
    //Simulationsprogramm als Scilab-Skript erzeugen:
    string skript1 = "            \
m = 1.0;                       \n \
C = 1.0;                       \n \
                               \n \
function f = rechteSeite(t,y)  \n \
    A = [[0,-C/m];[1,0]];      \n \
    f = A*y;                   \n \
endfunction                    \n \
                               \n \
t = 0:0.01:30;                 \n \
y0 = [0,1]\';                  \n \
t0 = 0;                        \n \
y  = ode(y0,t0,t,rechteSeite); \n";
    skript1.append("fprintfMat(\'schwinger2.txt\',[t\',y\']);\n");
    ofstream data("skript1.sce");
    data<<skript1;
    data.close();
    //Scilab ohne GUI im Batchmodus mit vorgegebenem Skript ausführen:
    system("C:\\Programme\\scilab-4.1\\bin\\Scilex.exe -nouserstartup -ns -nb -nw -nwni -nogui -e exec(\'skript1.sce\');quit");
    //Visualisierungsprogramm als Scilab-Skript erzeugen:
    string skript2 = "m=fscanfMat('schwinger.txt');\nplot(m(:,1),m(:,2),m(:,1),m(:,3));\n";
    ofstream data2("skript2.sce");
    data2<<skript2;
    data2.close();
    system("C:\\Programme\\scilab-4.1\\bin\\Scilex.exe -nouserstartup -nb -nw -e exec(\'skript2.sce\');");
}

Code 7.1-12: 06_scilabsteuerung.cpp - In C++ zwei Scilab-Skripte erzeugen, speichern und ablaufen lassen.