Dokument 1.pdf - Staatliche Studienakademie Glauchau
Dokument 1.pdf - Staatliche Studienakademie Glauchau
Dokument 1.pdf - Staatliche Studienakademie Glauchau
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
TRANSIM TECHNOLOGY CORPORATION<br />
Bachelor Thesis<br />
Erweiterung des Engine Framework und der Web-Schematic<br />
Lösung der Transim Technology Corporation um eine<br />
Echtzeitkommunikation zwischen Simulator und Schematic<br />
Vorgelegt am: 30.08.2013<br />
Von:<br />
Martin Meyer<br />
Flemmingstraße 19<br />
09116 Chemnitz<br />
Studiengang:<br />
Technische Informatik<br />
Studienrichtung: Prozessinformatik<br />
Seminargruppe:<br />
4TI10-1<br />
Matrikelnummer: 4000652<br />
Praxispartner:<br />
Transim Technology Corporation<br />
Annaberger Straße 240<br />
09125 Chemnitz<br />
Gutachter:<br />
Herr Dipl.-Inf. (FH) Frank Vogel (Transim Technology Corp.)<br />
Herr Dr. Mathias Sporer (<strong>Staatliche</strong> <strong>Studienakademie</strong> <strong>Glauchau</strong>)
Inhaltsverzeichnis<br />
Inhaltsverzeichnis<br />
Inhaltsverzeichnis ................................................................................................... IV<br />
Abbildungsverzeichnis ........................................................................................... VI<br />
Tabellenverzeichnis ............................................................................................... VII<br />
Formelverzeichnis ................................................................................................. VIII<br />
Listingverzeichnis ................................................................................................... IX<br />
Abkürzungsverzeichnis ........................................................................................... X<br />
1 Einführung ................................................................................................... 1<br />
1.1 Hinführung zum Thema ................................................................................. 1<br />
1.2 Motivation ...................................................................................................... 1<br />
1.3 Einblick in das EngineFramework ................................................................. 2<br />
1.4 Hinweise zur Arbeit ....................................................................................... 3<br />
1.4.1 Verwendete Beispiele und Simulationen ....................................................... 3<br />
1.4.2 Gliederung ..................................................................................................... 5<br />
2 Analyse des bestehenden Systems .......................................................... 6<br />
2.1 Die Simulationsanwendung “Qucs” ............................................................... 6<br />
2.2 Der EngineServer .......................................................................................... 7<br />
2.2.1 Allgemein ...................................................................................................... 7<br />
2.2.2 Der EngineServer als Webdienst .................................................................. 8<br />
2.3 Die QucsEngine ............................................................................................ 8<br />
2.4 Die Darstellung der Simulationsdaten in der Webanwendung ...................... 9<br />
3 Änderungen an Qucs ................................................................................ 11<br />
3.1 Vorüberlegungen ......................................................................................... 11<br />
3.1.1 Kommunikation zwischen den Anwendungen ............................................. 11<br />
3.1.2 Verwendung des Netzwerkprotokolls .......................................................... 12<br />
3.1.3 Zuweisung der gesendeten Daten zu einer Simulation ............................... 13<br />
3.1.4 Formatierung der Simulationsergebnisse .................................................... 14<br />
3.1.4.1 Analyse der bisherigen Ausgabedateien von Qucs ..................................... 14<br />
3.1.4.2 Betrachtungen zu der Paketgröße und dem Datenformat ........................... 15<br />
3.1.5 Nachbilden zeitintensiver Simulationen ....................................................... 17<br />
3.1.6 Die Netzwerkkommunikation ....................................................................... 17<br />
3.2 Implementierung ......................................................................................... 18<br />
3.2.1 Der Programmeinstieg ................................................................................ 18<br />
3.2.2 Die Netzwerkklasse ..................................................................................... 18<br />
3.2.3 Einbinden der Netzwerkklasse .................................................................... 19<br />
3.2.3.1 Die Klassenhierarchie der Simulationssoftware .......................................... 19<br />
3.2.3.2 Erkennen der Simulationsergebnisse .......................................................... 20<br />
3.2.3.3 Erkennen der Abhängigkeiten ..................................................................... 20<br />
Seite IV
Inhaltsverzeichnis<br />
4 Die QucsLiveEngine ................................................................................. 22<br />
4.1 Vorbetrachtungen ........................................................................................ 22<br />
4.1.1 Starten der Simulation ................................................................................. 22<br />
4.1.2 Empfangen der Simulationsdaten ............................................................... 22<br />
4.1.3 Auswerten des XML-<strong>Dokument</strong>es ............................................................... 23<br />
4.1.4 Konvertieren der Daten ............................................................................... 24<br />
4.1.5 Erzeugen der Waveform Dateien ................................................................ 25<br />
4.1.6 Die Manifest Datei ....................................................................................... 26<br />
4.1.7 Speichern von Kurvenscharen .................................................................... 26<br />
4.2 Implementierung ......................................................................................... 27<br />
4.2.1 Starten des TCP-Servers und der Simulationsanwendung ......................... 27<br />
4.2.2 Empfangen der Daten ................................................................................. 28<br />
4.2.3 Auslesen des TCP-Stroms .......................................................................... 29<br />
4.2.4 Speichern der Simulationsergebnisse ......................................................... 30<br />
5 Der EngineServer ...................................................................................... 32<br />
5.1 Vorbetrachtungen ........................................................................................ 32<br />
5.1.1 Das Postprocessing .................................................................................... 32<br />
5.1.2 Bereitstellen der Simulationsdaten .............................................................. 34<br />
5.2 Implementierung ......................................................................................... 35<br />
5.2.1 Anpassen der QucsLiveEngine ................................................................... 35<br />
5.2.2 Speichern der Daten im Zwischenspeicher des EngineServers .................. 36<br />
5.2.3 Starten des Postprocessing ........................................................................ 37<br />
6 Anzeige der Simulationsergebnisse in WebScope ................................ 38<br />
6.1 Vorbetrachtung ............................................................................................ 38<br />
6.1.1 Anfrage der Simulationsdaten ..................................................................... 38<br />
6.1.2 Ausgabe der Simulationsdaten zu Laufzeit ................................................. 38<br />
6.1.2.1 Allgemein .................................................................................................... 38<br />
6.1.2.2 Erneutes Abfragen aller Ergebnisse (Polling).............................................. 39<br />
6.1.2.3 Abfragen nach neuen Ergebnissen (Long Polling) ...................................... 39<br />
6.1.2.4 Websocket-Protokoll ................................................................................... 40<br />
6.1.2.5 Senden der Ergebnisse vom Webserver ..................................................... 40<br />
6.2 Implementierung ......................................................................................... 40<br />
6.2.1 Auslesen des Zwischenspeichers im EngineServer .................................... 40<br />
6.2.2 Ausgabe der Simulationsdaten ................................................................... 41<br />
7 Bewertung und Ausblick .......................................................................... 43<br />
Quellenverzeichnis ................................................................................................. 44<br />
Anhangverzeichnis ................................................................................................. 46<br />
Seite V
Abbildungsverzeichnis<br />
Abbildungsverzeichnis<br />
Abbildung 1 Tiefpass erster Ordnung 3<br />
Abbildung 2 Transientsimulation des Tiefpasses 4<br />
Abbildung 3 Wechselstrom-Simulation des Tiefpasses 4<br />
Abbildung 4 Das Web-Schematic 9<br />
Abbildung 5 WebScope nach Abschluss einer Simulation 10<br />
Abbildung 6 Die Klassenhierarchie der Simulatoren von Qucs 19<br />
Abbildung 7 Speichern der Ergebnisse in der Waveform-Datei 25<br />
Seite VI
Tabellenverzeichnis<br />
Tabellenverzeichnis<br />
Tabelle 1 Vergleich des Overheads bei XML und JSON .................................... 16<br />
Tabelle 2 Format der Achsbezeichung in der Manifest-Datei ............................. 26<br />
Seite VII
Formelverzeichnis<br />
Formelverzeichnis<br />
Formel 1 Berechnung der Grenzfrequenz ............................................................ 5<br />
Formel 2 Differenz des Overheads von XML und JSON .................................... 17<br />
Seite VIII
Listingverzeichnis<br />
Listingverzeichnis<br />
Listing 1 Beispiel für eine Netzliste ...................................................................... 6<br />
Listing 2 Ausschnitt einer Qucs-Ausgabedatei .................................................. 14<br />
Listing 3 Startparameter für die Netzwerkkommunikation ................................. 18<br />
Listing 4 Auswahl des Ports für den TCP-Server .............................................. 28<br />
Listing 5 Lesen der XML-<strong>Dokument</strong>e aus dem TCP-Puffer ............................... 30<br />
Listing 6 Beispiel für die PostProcessingCommands ........................................ 32<br />
Listing 7 Von FlashGen erzeugte Datei zur Berschreibung der Diagramme ..... 33<br />
Listing 8 Änderungen an der generischen Klasse ............................................. 35<br />
Listing 9 Aufruf der Rückruffunktion in der QucsLiveEngine .............................. 36<br />
Listing 10 Änderungen an der EngineServer-Klasse ........................................... 36<br />
Listing 11 Polling in WebScope ........................................................................... 42<br />
Seite IX
Abkürzungsverzeichnis<br />
Abkürzungsverzeichnis<br />
AC<br />
Alternating current<br />
AJAX<br />
API<br />
CLI<br />
DLL<br />
DOM<br />
GPL<br />
HTML<br />
IIS<br />
IP<br />
JSON<br />
Qucs<br />
RFID<br />
SAX<br />
SimID<br />
TCP<br />
UserID<br />
UDP<br />
URL<br />
XML<br />
Asynchronous JavaScript and XML<br />
Application Programming Interface<br />
Common Language Infrastructure<br />
Dynamic Link Library<br />
Document Object Model<br />
GNU General Public License<br />
Hypertext Markup Language<br />
Internet Information Services<br />
Internet Protocol<br />
JavaScript Object Notation<br />
Quite Universal Circuit Simulator<br />
Radio-frequency identification<br />
Simple API for XML<br />
Simulation Identifikationsnummer<br />
Transmission Control Protocol<br />
Benutzeridentifikationsnummer<br />
User Datagram Protocol<br />
Uniform Resource Locator<br />
Extensible Markup Language<br />
Seite X
Einführung<br />
1 Einführung<br />
1.1 Hinführung zum Thema<br />
Die Transim Technology Corporation hat sich auf die Entwicklung von<br />
Webanwendungen spezialisiert, in denen unterschiedliche Simulationsmöglichkeiten,<br />
speziell für Firmen aus der Halbleiterindustrie, realisiert werden können. Für die<br />
Simulationen werden Anwendungen von Drittherstellern verwendet. Die<br />
Webanwendungen stellen dabei eine Schnittstelle zwischen den<br />
Simulationsanwendungen und der grafischen Oberfläche im Webbrowser des<br />
Endnutzers dar. Simulationen dienen der Analyse von realen Systemen durch die<br />
Verwendung von Modellen. Dadurch kann das Verhalten eines Systems untersucht<br />
werden, ohne dieses real nachbilden zu müssen. Ein weiterer Vorteil besteht darin,<br />
dass die Modelle parametrisiert werden können. In einer realen Nachbildung des<br />
Systems müssen Komponenten entsprechend ausgetauscht werden, im<br />
Simulationsmodell muss lediglich ein Parameter geändert werden. Es entsteht somit<br />
eine erhebliche Zeit- und Kostenersparnis. Für Firmen aus der Halbleiterindustrie<br />
bedeutet dies, dass sie ihren Kunden neue Bauelemente über eine<br />
Simulationsanwendung bereitstellen können. Die Kunden können über diese<br />
Anwendung testen, ob und in welcher Art und Weise die Bauelemente für ihre<br />
Zwecke verwendet werden können.<br />
Für die Simulation können hierbei unterschiedliche Arten angeboten werden. Die<br />
wichtigste Rolle spielen dabei die Simulationen von elektronischen Schaltungen. So<br />
stehen beispielsweise Transientsimulationen zur Verfügung, über die das<br />
Einschaltverhalten von Systemen analysiert werden kann, oder eine Wechselstrom-<br />
Simulation, die eine Analyse des Amplituden- und Phasengangs ermöglicht. Auf<br />
diese Simulationen wird in dieser Arbeit näher eingegangen. Es sind jedoch auch<br />
Simulationen möglich, über die zum Beispiel unterschiedliche Entwürfe von RFID-<br />
Antennen (engl.: radio-frequency identification) untersucht werden können.<br />
1.2 Motivation<br />
Neu entwickelte Bauelemente müssen für ihre Einsatzgebiete entsprechend<br />
angepasst werden. Ohne Simulationen müssten die Bauelemente in den realen<br />
Systemen umständlich getestet werden, bis sie ihre Funktion korrekt erfüllen. Die<br />
immer schneller voranschreitende Entwicklung der Technik setzt die Entwickler von<br />
elektronischen Geräten zunehmend unter Druck. Ohne die Verwendung von<br />
Simulationen müssten neue Bauelemente sehr zeit- und kostenaufwendig in ihren<br />
Einsatzgebieten getestet werden und würde zu einer langsameren Entwicklung<br />
führen.<br />
Die Komplexität der Bauelemente nimmt immer weiter zu, wodurch die Bedeutung<br />
der Simulation immer größer wird. Komplexere Bauelemente bedeuten jedoch auch<br />
Seite 1
Einführung<br />
komplexere Simulationen. Da die Rechenleistung eines Computers beschränkt ist,<br />
kommt es somit zu einer längeren Ausführungszeit der Simulation. In dem bisherigen<br />
System der Transim Technology Corporation werden die Simulationsergebnisse erst<br />
nach Abschluss der Simulation ausgegeben. Bei komplexen Simulationen führt dies<br />
zu einer langen Wartezeit des Endnutzers. Im Rahmen dieser Arbeit soll eine<br />
Möglichkeit gefunden werden, die Simulationsergebnisse während der Berechnung<br />
dem Endnutzer zur Verfügung zu stellen. Sieht dieser, dass beispielsweise<br />
Schwellwerte überschritten werden, kann er die Simulation abbrechen und mit<br />
geänderten Parametern erneut starten und muss nicht auf das Ende der gesamten<br />
Simulation warten.<br />
1.3 Einblick in das EngineFramework<br />
Wurde für eine Firma aus der Halbleiterindustrie eine Webanwendung entwickelt,<br />
kann der Endnutzer über diese zunächst die Bauelemente der Firma auswählen.<br />
Nach der Auswahl können für diese unterschiedliche Parameter festgelegt werden.<br />
Handelt es sich um eine elektronische Simulation wird dem Nutzer die Schaltung des<br />
Systems im Webbrowser angezeigt. Diese Anzeige wird WebSchematic genannt.<br />
Hier besteht die Möglichkeit, Elemente der Schaltung zu parametrisieren und eine<br />
Simulation zu Starten. Wurde die Simulation gestartet, werden die Parameter an den<br />
Webserver der Webanwendung übertragen. Um die Simulation zu starten, muss der<br />
Webserver zunächst eine Anfrage an den sogenannten JobManager stellen. Dieser<br />
verteilt die Simulationsanfragen auf unterschiedliche Simulationsserver auf. Diese<br />
Simulationsserver werden EngineServer genannt. Die Transim Technology<br />
Corporation nutzt dieses System, um die Simulationen auf mehrere Computer<br />
aufzuteilen. Steigt die Anzahl der Simulationen kann leicht ein neuer<br />
Simulationsserver in das System eingebunden werden. Nachdem der<br />
Simulationsanfrage ein EngineServer zugewiesen wurde, werden die<br />
Simulationsparameter an diesen weitergeleitet. In diesen ist unter anderem<br />
enthalten, welche Simulationsanwendung verwendet werden soll. Die Schnittstelle<br />
zwischen den Anwendungen und dem EngineServer erfolgt über die sogenannten<br />
Engines. Für jede Simulationsanwendung existiert eine eigene Engine. Nachdem die<br />
Parameter beim EngineServer eingegangen sind, startet dieser die entsprechende<br />
Engine. In dieser wird die Simulationsanwendung gestartet. Nach Abschluss der<br />
Simulation müssen die Simulationsergebnisse von der Engine in ein eigenes Format<br />
konvertiert und gespeichert werden. Ist die Simulation abgeschlossen, teilt der<br />
EngineServer dies dem Webserver der Webanwendung mit. Dieser kann nun die<br />
Simulationsergebnise vom EngineServer abfragen, und dem Endnutzer in der<br />
Webanwendung zur Verfügung stellen. Die graphische Darstellung der<br />
Simulationsergebnisse im Webbrowser erfolgt in dem sogenannten WebScope.<br />
Seite 2
Einführung<br />
1.4 Hinweise zur Arbeit<br />
1.4.1 Verwendete Beispiele und Simulationen<br />
Zum besseren Verständnis dieser Bachelor Thesis werden unterschiedliche<br />
Beispiele verwendet. Diese bauen auf den Tiefpass erster Ordnung aus der<br />
Elektronik auf. Unter einem Tiefpass ist ein Filter zu verstehen, der aus einem<br />
eingehenden Signal die Frequenzanteile abschwächt, die über der Grenzfrequenz<br />
des Tiefpasses liegen. Abbildung 1 zeigt die Schaltung eines Tiefpasses erster<br />
Ordnung.<br />
Abbildung 1 Tiefpass erster Ordnung<br />
Elektronische Schaltungen, wie der Tiefpass, lassen sich auf unterschiedliche Art<br />
und Weise simulieren. In dieser Arbeit wird lediglich auf die Transientsimulation und<br />
die Wechselstrom-Simulation, auch AC-Simulation (alternating current) genannt,<br />
eingegangen.<br />
Bei der Transientsimulation wird das zeitliche Verhalten eines Systems dargestellt.<br />
Sie wird verwendet um die Antwort eines Systems auf ein bestimmtes<br />
Eingangssignal zu analysieren. Als typische elektrische Kenngrößen lassen sich<br />
unter anderem die Flankensteilheit (engl.: slew rate) oder die Einschwingzeit (engl.:<br />
rise time) ablesen. Für diese Simulationsart muss die Endzeit angegeben werden.<br />
Bei einigen Simulatoren kann eine Startzeit angegeben werden. Diese beschränkt<br />
dabei lediglich die Ausgabedaten des Simulators. Da es sich um eine zeitliche<br />
Darstellung handelt, muss der Simulator stets bei t=0s beginnen, um das gesamte<br />
Verhalten des Systemes abzubilden. In Abbildung 2 ist das Ergebnis einer<br />
Transientsimulation zu dem in Abbildung 1 gezeigten Tiefpass aufgeführt (Startzeit:<br />
0s, Endzeit: 0.25ms).<br />
Seite 3
Einführung<br />
Abbildung 2 Transientsimulation des Tiefpasses<br />
Zu sehen ist, dass die Eingangsspannung Ue, sowie die Ausgangsspannung Ua über<br />
der Zeit abgebildet sind. In dem hier gewählten Beispiel ist zu erkennen, dass die<br />
Ausgangsspannung bereits deutlich geschwächt gegenüber der Eingangsspannung<br />
ist. Neben der Schwächung des Signales ist das Einschaltverhalten des Systems<br />
ersichtlich.<br />
Die Wechselstrom-Simulation ermöglicht es, den Verlauf des Amplituden- und<br />
Phasenganges eines Systems zu analysieren. Dazu wird eine Sinusspannungsquelle<br />
mit variabler Frequenz verwendet. Dem Simulator muss dabei die Anfangs- und<br />
Endfrequenz, sowie die Schrittweite mitgeteilt werden. Bei der Simulation werden für<br />
jede Frequenz die Ströme und Spannungen für jeden Knotenpunkt berechnet. In<br />
Abbildung 3 ist das Ergebnis einer Wechselstrom-Simulation für den Tiefpass aus<br />
Abbildung 1 dargestellt.<br />
Abbildung 3 Wechselstrom-Simulation des Tiefpasses<br />
Zu sehen ist der Amplituden- und Phasengang der Eingangs- und<br />
Ausgangsspannung über der Frequenz. Während die Eingangsspannung konstant<br />
bleibt, ist zu erkennen, dass die Ausgangsspannung bei der Grenzfrequenz einbricht<br />
Seite 4
Einführung<br />
und eine Phasenverschiebung erfolgt. Die Berechnung der Grenzfrequenz ist in<br />
Formel 1 aufgeführt. 1<br />
Formel 1 Berechnung der Grenzfrequenz<br />
Für jede Simulationsart besteht die Möglichkeit, Eigenschaften von Elementen zu<br />
parametrisieren. Für diesen Parameterdurchlauf muss ein Start- und Endwert, sowie<br />
die Anzahl der Zwischenschritte angegeben werden. Die Simulation wird für jeden<br />
Wert des Parameters durchgeführt. Als Ergebnis erhält man eine Kurvenschar. Die<br />
Parameterdurchläufe können dabei beliebig verschachtelt werden.<br />
Die Anwendung Qucs bietet die Möglichkeit, Gleichungen zu den Schaltungen<br />
anzugeben. Darüber lassen sich zusätzliche Werte berechnen, wie beispielsweise<br />
die Phasenverschiebung der Ausgangsspannung bei einer Wechselstrom-<br />
Simulation. Das Format der Transim Technology Corporation zur Darstellung von<br />
Schaltungen bietet derzeit noch keine Möglichkeit Gleichungen einzubinden. Aus<br />
diesem Grund wird im Rahmen dieser Arbeit nicht näher auf diese eingegangen.<br />
1.4.2 Gliederung<br />
Im Rahmen dieser Arbeit wird auf mehrere Programme bzw. Unterprogramme<br />
eingegangen die für die Realisierung der Aufgabe modifiziert werden müssen. In<br />
Kapitel 2 wird ein Überblick über die Programme gegeben und auf deren<br />
Besonderheiten eingegangen. Die anschließenden Kapitel gehen tiefgründiger auf<br />
die vorzunehmenden Modifizierungen an den Programmen ein. Hierbei werden<br />
zuerst Vorbetrachtungen getroffen und anschließend auf die Implementierung näher<br />
eingegangen. Die Reihenfolge der Kapitel ist an den Ablauf der Daten im System<br />
angepasst. So wird zunächst auf den Simulator Qucs eingegangen. Anschließend<br />
werden die Konvertierung und die Speicherung der Simulationsergebnisse im<br />
EngineFramework behandelt, bevor schließlich die Ausgabe der Daten im<br />
Webbrowser realisiert wird.<br />
1 vgl. online: Tiefpass auf Wikipedia.org (07.08.2013)<br />
Seite 5
2 Analyse des bestehenden Systems<br />
2.1 Die Simulationsanwendung “Qucs”<br />
Analyse des bestehenden Systems<br />
Die Anwendung Qucs (Quite Universal Circuit Simulator) dient der Simulation von<br />
Schaltungen. Der Quellcode ist unter der GPL (GNU General Public License) Lizenz<br />
frei verfügbar. 2 Im Rahmen der Studienarbeit wurde bereits näher auf die<br />
Anwendung eingegangen und Änderungen vorgenommen. 3 In dieser Bachelor<br />
Thesis wird die in der Studienarbeit angepasste Version von Qucs verwendet. Der<br />
Quellcode von Qucs ist in mehrere Teile gegliedert. In dieser Arbeit wird<br />
ausschließlich der Simulator „qucsator.exe“ betrachtet. Dieser kann unabhängig von<br />
den anderen Teilen erstellt und anschließend ausgeführt werden. Die<br />
Simulationsanwendung wurde in der Sprache C++ programmiert.<br />
Um eine Simulation zu starten, müssen dem Programm der Zielpfad zu einer<br />
Ausgabedatei, sowie der Quellpfad einer Netzlistendatei angegeben werden. In einer<br />
Netzliste werden alle Komponenten einer Schaltung in textueller Form gespeichert.<br />
Dazu zählen zum einen die verwendeten Bauteile. Dazu zählen sowohl analoge<br />
Elemente (wie Strom- oder Spannungsquellen, Kondensatoren, usw.), als auch<br />
digitale Elemente (wie Taktgeneratoren, Logikgatter, FlipFlops, usw.). Neben den<br />
Bauteilen befinden sich die Daten zu den entsprechenden Simulationen in der<br />
Netzliste wieder. Ein Beispiel für eine solche Netzliste ist in Listing 1 aufgeführt.<br />
# Qucs 0.0.17 C:/Qucs/Tiefpass/AC_Sweep.sch<br />
# Bauelemente:<br />
Vac:V1 gnd Ue U="1 V" f="1 GHz" Phase="0" Theta="0"<br />
R:R Ue Ua R="R1" Temp="26.85" Tc1="0.0" Tc2="0.0" Tnom="26.85"<br />
C:C gnd Ua C="1 nF" V=""<br />
# Simulationen:<br />
.AC:AC1 Type="log" Start="1 Hz" Stop="10 GHz" Points="50" Noise="no"<br />
.SW:SW1 Sim="AC1" Type="log" Param="R1" Start="5 Ohm" Stop="5 MOhm"<br />
Points="3"<br />
Listing 1 Beispiel für eine Netzliste<br />
Hier wurden, zur besseren Darstellung, die Komponenten nach Art sortiert und<br />
Kommentare eingefügt. Für die Simulationsanwendung sind die Art und die<br />
Reihenfolge der Komponenten nicht entscheidend.<br />
Wurde der Anwendung beim Start eine Netzliste übergeben, werden alle<br />
Simulationen ausgeführt. Die Ergebnisse werden anschließend in eine Ausgabedatei<br />
geschrieben. Für zeitaufwändige Simulationen bedeutet dies, dass lange auf die<br />
Ergebnisse gewartet werden muss. Eine Ausgabe der Zwischenergebnisse während<br />
der Simulation ist nicht möglich. Diese Funktion soll im Rahmen dieser Bachelor<br />
2 online: Qucs auf Sourceforge (12.07.2013)<br />
3 vgl. Studienarbeit MEYER, 2013<br />
Seite 6
Analyse des bestehenden Systems<br />
Thesis in die Anwendung implementiert werden. Die Zwischenergebnisse sollen<br />
dabei anderen Programmen zur Verfügung gestellt werden.<br />
2.2 Der EngineServer<br />
2.2.1 Allgemein<br />
Die Transim Technology Corporation nutzt Simulationsanwendungen von<br />
Drittherstellen. Somit können den Kunden unterschiedliche Simulationsmöglichkeiten<br />
zur Verfügug gestellt werden. Der EngineServer stellt dabei die Verbindung zwischen<br />
der Webanwendung und dem Simulator her. Zu den verwendeten<br />
Simulationsanwendungen zählen beispielsweise SIMetrix, Portunus, LTSpice, Qucs,<br />
etc. Diese benötigen zum Starten eine Abbildung der Schaltung in Form einer<br />
Netzliste, oder einer Schematic-Datei. Das Format dieser Dateien ist für jede<br />
Anwendung unterschiedlich. Ebenso werden die Simulationsergebnisse in einem,<br />
von der Software abhängigem Format gespeichert. Zur Darstellung der Schaltungen<br />
und der Simulationsergebnisse, hat die Transim Technology ein eigenes<br />
Datenformat entwickelt. Dies bedeutet, dass vor einer Simulation die Schaltung in<br />
das für die Anwendung spezifische Format konvertiert wird. Nach der Simulation<br />
müssen schließlich die Ergebnisse wieder in das Transim-spezifische Format<br />
konvertiert werden. Die Konvertierung der Schaltung in eine Qucs-Netzliste ist<br />
bereits in dem EngineFramework implementiert. Auf diese wird im Rahmen dieser<br />
Arbeit nicht näher eingegangen.<br />
Aufgrund der unterschiedlichen Formate der einzelnen Simulationsanwendungen,<br />
muss die Schnittstelle zu dem EngineServer für jede Software angepasst werden.<br />
Aus diesem Grund hat die Transim Technology Corporation eine Generische Klasse<br />
entwickelt, welche die grundlegenden Funktionen, wie das Starten der Simulation,<br />
oder das Konvertieren der Ergebnisse, bereits deklariert. Für jeden Simulator<br />
existiert eine eigene Klasse, welche von dieser generischen Klasse erbt. Diese<br />
Klassen, die die Schnittstelle zwischen dem Simulator und dem EngineServer<br />
darstellen werden folgend Engine genannt. Auf die Engine, die Qucs steuert, wird im<br />
folgenden Kapitel näher eingegangen.<br />
Neben dem Steuern der Simulationsanwendungen hat der EngineServer ebenfalls<br />
die Aufgabe, die Simulationsergebnisse in unterschiedlichen Arten weiter zu<br />
verarbeiten (genannt PostProcessing). So können beispielsweise die Ergebnisse, in<br />
einem kartesischen Koordinatensystem dargestellt, als Bilddatei gespeichert werden.<br />
Ebenfalls werden die Simulationsergebnisse für die Darstellung in einer<br />
Webanwendung vorbereitet. Darauf wird im späteren Verlauf der Arbeit näher<br />
eingegangen.<br />
Seite 7
Analyse des bestehenden Systems<br />
2.2.2 Der EngineServer als Webdienst<br />
Der EngineServer wurde als Webdienst implementiert. Webdienste dienen zur<br />
Interaktion zwischen unterschiedlichen Anwendungen über ein Netzwerk. Der<br />
Webdienst verfügt über eine Schnittstellenbeschreibung, in der definiert ist, wie mit<br />
dem Webdienst interagiert werden kann. Andere Programme können Anfragen an<br />
den Webdienst stellen, auf die dieser mit der entsprechenden Information antwortet.<br />
Der Webdienst wurde in der von Microsoft entwickelten C++/CLI programmiert.<br />
Dabei handelt es sich um eine Abwandlung der Programmiersprache C++, die um<br />
Sprachelemente erweitert wurde um einen Zugriff auf die Laufzeitumgebung der<br />
.NET-Plattform zu ermöglichen. Somit können Besonderheiten der Common<br />
Language Infrastructre (CLI), wie Ereignisse, automatische Speicherbereinigung,<br />
generische Klassen, etc. verwendet werden.<br />
Zum Ausführen des Webdienstes werden die Internet Information Services (IIS) von<br />
Microsoft benötigt. Diese werden von Microsoft für die eigenen Betriebssysteme zur<br />
Verfügung gestellt.<br />
Wird eine neue Simulation gestartet wird dieser eine eindeutige<br />
Identifikationsnummer für die Simulation (das JobHandle) zugewiesen. Diese wird<br />
benötigt, um eine Zuordnung zwischen der ursprünglichen Simulationsanfrage und<br />
den Simulationsergebnissen herstellen zu können.<br />
2.3 Die QucsEngine<br />
Im Rahmen der Studienarbeit wurde die QucsEngine zur Steuerung der Anwendung<br />
Qucs entwickelt. 4 Diese startet zunächst über eine Netzliste die Simulation. Ist diese<br />
abgeschlossen, werden die Simulationsergebnisse aus der Ausgabedatei<br />
ausgelesen und in das framework-spezifische Format konvertiert und gespeichert.<br />
Dazu wird die Ausgabedatei zeilenweise eingelesen und ausgewertet. Dieser<br />
Algorithmus ist für die Verarbeitung der Simulationsergebnisse zur Laufzeit nicht<br />
weiter verwendbar.<br />
Der bisherige Ablauf der QucsEngine soll weiterhin möglich sein. Dies bedeutet,<br />
dass der Engine mitgeteilt werden muss, welche Art von Simulation sie starten soll.<br />
Die QucsEngine ist von einer generischen Klasse abgeleitet. In dieser ist bisher noch<br />
keine Möglichkeit integriert zwischen unterschiedlichen Arten einer Simulation zu<br />
unterscheiden. Dazu muss sich zunächst firmenintern abgesprochen werden, wie<br />
diese Unterteilung erfolgen soll. Aus diesem Grund wurde sich vorerst dazu<br />
entschieden, eine neue Engine, die QucsLiveEngine, zu entwickeln.<br />
Die QucsEngine ist ebenfalls wie der EngineServer in C++/CLI programmiert. Somit<br />
können die Funktionen des .NET-Frameworks verwendet werden. Um die Engine auf<br />
4 vgl. Studienarbeit MEYER, 2013<br />
Seite 8
Analyse des bestehenden Systems<br />
dem EngineServer nutzen zu können, muss diese kompiliert und erstellt werden. Der<br />
Erstellprozess erzeugt eine dynamische Programmbibliothek (engl.: Dynamic Link<br />
Library – DLL). Diese kann nun im EngineServer verwendet werden. Dazu muss in<br />
einer Konfigurationsdatei ein Verweis auf die Bibliothek gesetzt und der<br />
EngineServer neu gestartet werden. Da die Engine von einer generischen Klasse<br />
abgeleitet ist, die dem EngineServer bekannt ist, muss dieser nicht neu erstellt<br />
werden, um die neue Engine verwenden zu können.<br />
2.4 Die Darstellung der Simulationsdaten in der Webanwendung<br />
Um die Darstellung einer Schaltung in einem Webbrowser zu ermöglichen, hat die<br />
Transim Technology Corporation das sogenannte Web-Schematic entwickelt. Dieses<br />
bietet neben der Darstellung der Schaltung ebenfalls die Möglichkeit, Parameter für<br />
einzelne Bauteile anzupassen. Über das Web-Schematic können die Simulationen<br />
der Schaltungen gestartet werden. In Abbildung 4 ist ein Beispiel für das Web-<br />
Schematic zu sehen.<br />
Abbildung 4 Das Web-Schematic<br />
Wurde eine Simulation gestartet muss der Nutzer der Webanwendung warten bis die<br />
Simulation beendet wurde. In dieser Zeit wird ein Dialogfenster angezeigt, das über<br />
die ungefähre restliche Bearbeitungszeit der Simulation Auskunft gibt. Ist die<br />
Simulation abgeschlossen werden die Simulationsdaten in einem zusätzlichen Modul<br />
angezeigt. Dieses Modul wird WebScope genannt. Hier können die<br />
Simulationsergebnisse als Kurvenverläufe in unterschiedlichen Diagrammen<br />
Seite 9
Analyse des bestehenden Systems<br />
abgebildet werden. Abbildung 5 zeigt ein Beispiel für die Ausgabe der<br />
Simulationsdaten in WebScope<br />
Abbildung 5 WebScope nach Abschluss einer Simulation<br />
Im Rahmen dieser Arbeit soll eine Möglichkeit entwickelt werden, dem Nutzer die<br />
Simulationsdaten zur Laufzeit der Simulation anzuzeigen.<br />
Seite 10
Änderungen an Qucs<br />
3 Änderungen an Qucs<br />
3.1 Vorüberlegungen<br />
3.1.1 Kommunikation zwischen den Anwendungen<br />
Zum Datenaustausch zwischen zwei oder mehreren Programmen, auch<br />
Interprozesskommunikation genannt, gibt es unterschiedliche Methoden die im<br />
Folgenden kurz näher erläutert werden: 5<br />
Shared Memory<br />
Bei dieser Methode wird ein Bereich im Arbeitsspeicher festgelegt, auf den mehrere<br />
Prozesse zugreifen können. Um ein gleichzeitiges Lesen/Schreiben von mehreren<br />
Prozessen zu verhindern wird ein Mechanismus zum Wechselseitigem Ausschluss<br />
(auch Mutex genannt) verwendet. Dem Empfänger muss dabei mitgeteilt werden,<br />
wann neue Daten vorliegen. Diese Methode eignet sich besonders bei großen<br />
Datenmengen, jedoch müssen Sender- und Empfängerprozess auf den<br />
gemeinsamen Speicher zugreifen können. Somit ist diese Methode nicht geeignet,<br />
wenn die Programme auf unterschiedlichen Computern ausgeführt werden.<br />
Ereignisse<br />
Bei dieser Methode kann der sendende Prozess ein sogenanntes Ereignis auslösen.<br />
Der empfangende Prozess kann sich für dieses Ereignis registrieren. Dazu muss ein<br />
Unterprogramm (auch Behandlungsroutine genannt) angegeben werden, welches<br />
bei Eintreten des Ereignisses ausgeführt werden soll. Ein Beispiel dieses Konzeptes<br />
stellen die Software Interrupts dar. Diese Methode wird nur von einigen<br />
Betriebssystemen unterstützt, wobei jedes Betriebssystem die Methode<br />
unterschiedlich implementiert hat.<br />
Funktionsaufrufe<br />
Eine weitere Methode besteht darin, einem anderen Prozess Zugriff auf einzelne<br />
Funktionen zu gewähren. Bei dieser Methode ist es möglich, dass sich Sender- und<br />
Empfängerprogramm auf unterschiedlichen Computern befinden. Diese Methode ist<br />
in vielen Programmiersprachen über zusätzliche Bibliotheken und Dienstprogramme<br />
eingebunden und somit weitestgehend vom Betriebssystem unabhängig.<br />
Datenströme<br />
Als letzte Methode stehen die Datenströme zur Verfügung. Diese bieten die<br />
Möglichkeit Daten sequentiell von dem Sender zum Empfänger weiter zu reichen.<br />
Dabei schreibt der Sender in den Datenstrom, während der Empfänger die Daten<br />
aus diesem Strom liest. Diese Methode kann über Pipes, Named Pipes oder<br />
5 vgl. online: Interprozesskommunikation auf Wikipedia.org (15.07.2013)<br />
Seite 11
Änderungen an Qucs<br />
verbindungsorientierte Netzwerkprotokolle realisiert werden. Hier spricht man auch<br />
von Sockets. Dabei kann über Pipes lediglich innerhalb eines Prozesses<br />
kommuniziert werden. Mithilfe von Named Pipes ist dies auch zwischen<br />
unterschiedlichen Prozessen möglich, jedoch müssen diese auf demselben<br />
Computer ausgeführt werden. Durch die Nutzung von Sockets können auch<br />
Prozesse, die auf unterschiedlichen Computern ausgeführt werden untereinander<br />
kommunizieren.<br />
Für die Wahl der Methode sind zunächst die Anforderungen zu betrachten. Es ist<br />
denkbar, dass Qucs die Simulationsergebnisse an eine Anwendung senden soll, die<br />
nicht auf demselben Computer ausgeführt wird. Somit fällt die Nutzung eines<br />
gemeinsamen Speichers aus der Auswahl. Des Weiteren soll eine hohe<br />
Kompatibilität der Kommunikation erreicht werden. Da die ereignisgesteuerte<br />
Kommunikation stark von dem Betriebssystem abhängt entfällt somit auch diese. Für<br />
die Verwendung von Funktionsaufrufen werden in C++ zusätzliche<br />
Programmbibliotheken benötigt. Die Wahl viel somit auf die Verwendung von<br />
Sockets, weil diese in den meisten Betriebssystemen in C++ zur Verfügung stehen.<br />
Um die Simulationsergebnisse über einen Socket einer anderen Anwendung zur<br />
Verfügung zu stellen, benötigt Qucs die Adressinformationen des Empfängers. Zu<br />
diesen Adressinformationen zählen die Adressfamilie, die IP-Adresse des Computers<br />
auf dem der Empfängerprozess ausgeführt wird, sowie der Port, auf den der<br />
Empfängerprozess gebunden ist. Die Adressfamilie wird dabei auf „Internetadresse“<br />
festgelegt. Die IP-Adresse, sowie der Port des Empfänger-Computers sollen variabel<br />
gehalten werden. Befinden sich Qucs und Empfängerprozess auf dem gleichen<br />
Computer muss als IP-Adresse 127.0.0.1 angegeben werden.<br />
3.1.2 Verwendung des Netzwerkprotokolls<br />
Wie aus Kapitel 3.1.1 hervorgeht, soll für die Übertragung der Dateien eine Socket-<br />
Kommunikation implementiert werden. Hierbei gibt es Stream- und Datagram-<br />
Sockets, die jeweils durch ein Protokoll vereinbart sind<br />
Transmission Control Protocol (TCP) – Stream Socket<br />
Das TCP stellt zwischen zwei Endpunkten eine Verbindung her, es ist somit ein<br />
verbindungsorientiertes Protokoll. Dabei können Daten in beide Richtungen<br />
übertragen werden. Treten bei der Verbindung Datenverluste auf, werden diese<br />
automatisch erkannt und behoben. Ebenfalls wird sichergestellt, dass die<br />
gesendeten Pakete in der gleichen Reihenfolge bei dem Empfänger eingehen in der<br />
sie beim Sender verschickt wurden.<br />
User Datagram Protocol (UDP) – Datagram Socket<br />
UDP ist, im Gegensatz zu TCP, ein verbindungsloses Netzwerkprotokoll. Alle Daten<br />
werden versendet, ohne zu prüfen, ob diese entgegengenommen werden.<br />
Seite 12
Änderungen an Qucs<br />
Datenverluste werden nicht erkannt, und somit auch nicht behoben. Die Reihenfolge<br />
der empfangenen Pakete muss nicht der Reihenfolge der gesendeten Pakete<br />
entsprechen.<br />
Der Vorteil von TCP besteht in dem Sicherheitsaspekt. Jedoch kann es zu<br />
Verzögerungen kommen, da ein weiteres Paket erst übertragen wird, wenn<br />
sichergestellt ist, dass das vorherige am Empfänger eingetroffen ist. Somit hat UDP<br />
einen erheblichen Vorteil, wenn es darum geht schnell Daten zu übertragen.<br />
Da zu diesem Zeitpunkt der Entwicklung nicht abzusehen ist, ob es darauf ankommt,<br />
die Ergebnisse sicher zu übertragen und dafür eine längere Übertragungszeit<br />
akzeptiert wird, oder ob es auf eine schnelle Übertragung ankommt, bei der auch<br />
Pakete verloren gehen können, ist die Entscheidung gefallen zunächst beide<br />
Protokolle in Qucs zu implementieren.<br />
3.1.3 Zuweisung der gesendeten Daten zu einer Simulation<br />
Aus den bisher getroffenen Überlegungen ergibt sich der folgende Ablauf auf dem<br />
EngineServer. Wird eine neue Simulation angefordert, wird eine neue QucsEngine<br />
erstellt. Diese startet Qucs und empfängt während der Simulation über eine<br />
Netzwerkschnittstelle die Ergebnisse. Es ist jedoch denkbar, dass während eine<br />
Simulation läuft, eine weitere gestartet wird. Somit werden weitere<br />
Simulationsergebnisse über das Netzwerk übertragen. Dies bedeutet, dass die<br />
übertragenen Daten, einer QucsEngine zugeordnet werden müssen. Bei der Lösung<br />
dieses Problems kamen die folgenden zwei Lösungen in Betracht.<br />
Zuweisung über eine Identifikationsnummer<br />
Eine Überlegung besteht darin, auf dem EngineServer eine Serverinstanz<br />
auszuführen, die alle Simulationsergebnisse entgegen nimmt. Anschließend weißt<br />
dieser die Ergebnisse an die entsprechende QucsEngine zu. Dafür muss jedoch eine<br />
Unterscheidung der eingehenden Daten erfolgen. Um dies zu erreichen kann Qucs<br />
eine eindeutige Identifikationsnummer beim Starten übergeben werden. Diese<br />
Nummer wird anschließend bei der Übertragung jedes einzelnen<br />
Simulationsergebnisses mit an den Server übertragen. Dieser kann anhand der<br />
Identifikationsnummer die Ergebnisse den Simulationen zuweisen. Bei dieser<br />
Variante wird lediglich ein Server zur Entgegennahme der Ergebnisse benötigt. Der<br />
Nachteil dieser Variante besteht jedoch darin, dass eine erhöhte Netzwerkauslastung<br />
stattfindet, und Qucs um einen zusätzlichen Startparameter erweitert werden muss.<br />
Zuweisung über einen Port<br />
Als zweite Möglichkeit kam in Betracht, dass jede QucsEngine eine eigene<br />
Serverinstanz startet. Diese bindet sich an einen beliebigen freien Port. Dieser Port<br />
wird beim Starten von Qucs übergeben. Somit werden die Simulationsergebnisse nur<br />
an den entsprechenden Port übertragen und es entstehen keine zusätzlichen<br />
Seite 13
Änderungen an Qucs<br />
Netzwerkdaten. Der Nachteil besteht jedoch darin, dass für jede Simulation eine<br />
eigene Serverinstanz erstellt werden muss.<br />
Die Wahl fiel schließlich auf die portbasierte Zuweisung. Da Qucs zum Starten des<br />
Netzwerkdienstes bereits die IP-Adresse und der Port der Serverinstanz übergeben<br />
werden müssen, entfällt hier der zusätzliche Startparameter. Die Serverinstanz kann<br />
geschlossen werden sobald die Simulation beendet ist. Bei der Verwendung der<br />
anderen Überlegung müsste die Instanz entweder permanent ausgeführt werden,<br />
oder eine Steuerung implementiert werden, die diese erst beendet, wenn keine<br />
Simulation ausgeführt wird.<br />
3.1.4 Formatierung der Simulationsergebnisse<br />
3.1.4.1 Analyse der bisherigen Ausgabedateien von Qucs<br />
Betrachtet man die Ausgabedateien von Qucs, erkennt man, dass die Ergebnisse in<br />
einer XML-ähnlichen Struktur gespeichert werden. In Listing 2 ist eine Ausgabedatei<br />
in gekürzter Form aufgeführt. Die vollständige Datei befindet sich in Anhang 1. Die<br />
Ergebnisse einer Simulation werden in den -Tags gespeichert. Als Attribute<br />
wird in ihnen angegeben, von welchen Eingangsparametern diese abhängen. Die<br />
Werte für die Eingangsparameter sind dabei in den -Tags hinterlegt.<br />
<br />
<br />
+1.00000000000e+000<br />
...<br />
<br />
<br />
...<br />
<br />
...<br />
<br />
-4.93480220054e-017-j3.14159265359e-009<br />
-4.93479002439e-007-j3.14158490204e-004<br />
...<br />
<br />
<br />
+1.00000000000e+000-j1.57079632679e-008<br />
...<br />
<br />
...<br />
Listing 2 Ausschnitt einer Qucs-Ausgabedatei<br />
Bei der Netzwerkausgabe der Simulationsergebnisse müssen diese Abhängigkeiten<br />
ebenfalls übergeben werden, da sonst keine Zuordnung der einzelnen Ergebnisse zu<br />
den Eingangsparametern möglich ist. Es ergibt sich daraus die Überlegung, dass<br />
nach einem erfolgten Simulationsschritt eine Liste für die Abhängigkeiten, sowie eine<br />
Liste für die Ergebnisse erstellt wird und anschließend Beide an die Serverinstanz<br />
übertragen werden.<br />
Seite 14
3.1.4.2 Betrachtungen zu der Paketgröße und dem Datenformat<br />
Änderungen an Qucs<br />
Wie bereits erläutert, sollen die Simulationsergebnisse über ein Netzwerk<br />
ausgegeben werden. Die Übertragung der Daten kann dabei im gleichen Thread wie<br />
die Berechnung erfolgen, oder in einen neuen Thread verlagert werden. Wird nur ein<br />
Thread dafür verwendet kommt es zu einer Verlangsamung der gesamten<br />
Simulation, da während der Übertragungszeit keine neuen Ergebnisse berechnet<br />
werden. Lagert man das Senden der Daten in einen extra Thread aus, benötigt man<br />
einen zusätzlichen Puffer, in dem die Simulationsergebnisse zwischengespeichert<br />
werden. Für die Übertragung würde dann ein Ergebnis aus dem Puffer entnommen<br />
und an den Server übertragen werden. Läuft die Berechnung der Ergebnisse jedoch<br />
deutlich schneller, als das Senden, kann es passieren, dass keine Elemente mehr in<br />
den Puffer geschrieben werden können. Ebenfalls muss hier dafür gesorgt werden,<br />
dass nicht gleichzeitig vom Puffer gelesen und darauf geschrieben wird (dies wird<br />
auch Threadsicherheit genannt) und ist aufwändiger zu implementieren. Aus diesem<br />
Grund wurde sich für die Verwendung eines Threads entschieden. Um die Dauer des<br />
Blockierens zu verringern muss eine Optimierung an der Paketgröße der<br />
übertragenen Daten erfolgen.<br />
Der Netzzugang im späteren Einsatzgebiet wird das Ethernet sein. Ethernet regelt<br />
den Datenaustausch zwischen zwei Endpunkten über Pakete. Um eine möglichst<br />
schnelle Ausgabe der Simulationsergebnisse zu erreichen, muss die Anzahl der<br />
übertragenen Datenpakete gering gehalten werden. Laut den Spezifikationen des<br />
Ethernet-Rahmens, stehen pro Paket für die zu sendenden Daten maximal 1.500<br />
Byte zu. 6<br />
Eine Übertragung der Ergebnisse soll nach einem Simulationsschritt erfolgen. Neben<br />
den einzelnen Ergebnissen werden auch die entsprechenden Abhängigkeiten<br />
gesendet. Dazu wird ein Datenformat entworfen, das ermöglicht, mehrere<br />
Ergebnisse, sowie die zugehörigen Abhängigkeiten abzubilden. Hier haben sich zwei<br />
Formen etabliert, auf die im Folgenden kurz eingegangen wird.<br />
XML (Extensible Markup Language)<br />
Die XML ist eine Auszeichnungssprache und kann hierarchisch strukturierte Daten in<br />
Textform darstellen. Sie wurde von dem World Wide Web Consortium (W3C)<br />
spezifiziert.<br />
JSON (JavaScript Object Notation)<br />
Die JavaScript Object Notation ist ein Datenformat. Im Gegensatz zu XML ist JSON<br />
typisiert, wobei jedoch nur grundlegende Typen unterstützt werden. Ein JSON-<br />
<strong>Dokument</strong> lässt sich mit JavaScript direkt in ein Objekt umwandeln.<br />
6 vgl. IEEE 802.3-2012-Section One, 2013, S. 53-57<br />
Seite 15
Änderungen an Qucs<br />
Über beide Formate lassen sich Daten in Textform abbilden. Sowohl XML als auch<br />
JSON werden von vielen Programmiersprachen unterstützt. Durch die<br />
unterschiedlichen Formatierungen entsteht jedoch bei beiden Formen ein<br />
unterschiedlicher Overhead. Als Overhead werden alle Daten bezeichnet, die nicht<br />
zu den Nutzdaten gehören. In Tabelle 1 sind ein XML-, sowie ein JSON-Format<br />
aufgeführt. Ebenfalls wurden die Zeichen, sowie die daraus resultierende Größe des<br />
Overheads bestimmt.<br />
Übersicht<br />
XML<br />
<br />
<br />
<br />
...<br />
<br />
...<br />
<br />
...<br />
<br />
...<br />
<br />
JSON<br />
{"qucs":<br />
{"v":"0.0.17",<br />
"i":[<br />
{"n":"...","v":"..."},<br />
...<br />
],<br />
"d":[<br />
{"n":"...","v":"..."},<br />
...<br />
]<br />
}<br />
}<br />
Overhead<br />
allgemein<br />
<br />
<br />
...<br />
<br />
{"qucs":<br />
{"v":"0.0.17",<br />
...<br />
}<br />
}<br />
Overhead pro<br />
Abhängigkeit<br />
<br />
...<br />
<br />
"i":[...],<br />
{"n":"...","v":"..."} ( )<br />
, ( )<br />
Overhead pro<br />
Ergebnis<br />
<br />
...<br />
<br />
( ) ( ( ) ( ))<br />
"d":[...]<br />
{"n":"...","v":"..."} ( )<br />
, ( )<br />
( ) ( ( ) ( ))<br />
Summe ( ) ( )<br />
Anmerkung<br />
Tabelle 1 Vergleich des Overheads bei XML und JSON<br />
Betrachtet man die Differenz beider Werte (siehe Formel 2), wird deutlich, dass der<br />
Overhead des JSON-Formates nur günstiger ist, wenn ein Ergebnis und eine<br />
Seite 16
Änderungen an Qucs<br />
Abhängigkeit vorhanden sind. Steigt die Anzahl der Ergebnisse, bzw. Abhängigkeiten<br />
ist das XML-Format besser geeignet.<br />
( ) ( ) ( ( ) (<br />
( )))<br />
( ( ))<br />
Formel 2 Differenz des Overheads von XML und JSON<br />
In Anhang 2 sind die resultierenden Paketgrößen in Abhängigkeit von der Anzahl der<br />
Simulationsergebnisse, sowie deren Abhängigkeiten tabellarisch aufgelistet. Nach<br />
der Konvertierung der Werte in eine Zeichenkette haben diese eine maximale Länge<br />
von 39 Zeichen. Aus diesem Grund wurden hier zusätzlich 39 Byte in die<br />
Berechnung eingebunden. Aus den Tabellen ist zu erkennen, dass durch die<br />
Nutzung des XML-Formates mehr Daten in einem Ethernet-Paket übertragen werden<br />
können, als bei der Verwendung des JSON-Formates.<br />
3.1.5 Nachbilden zeitintensiver Simulationen<br />
Die Betrachtungen in Kapitel 3.1.4.2 bezogen sich speziell auf Simulationen deren<br />
Zwischenergebnisse schnell berechnet werden. Es gibt jedoch auch Simulationen die<br />
eine längere Zeit benötigen um ein Zwischenergebnis zu erzeugen. Für den späteren<br />
Entwicklungsverlauf empfiehlt es sich, dieses Verhalten in dem Simulator<br />
nachzuahmen. So kann die Visualisierung der Zwischenergebnisse auch mit<br />
einfachen Beispielen getestet werden.<br />
3.1.6 Die Netzwerkkommunikation<br />
Als Grundlage für die Modifizierungen an Qucs wird die Quelltextversion verwendet,<br />
die im Rahmen der Studienarbeit aus dem fünften Semester entstanden ist. In der<br />
Studienarbeit wurde erörtert, dass die Implementierung einer<br />
Netzwerkkommunikation entweder mit dem Einbinden einer entsprechenden<br />
Klassenbibliothek oder durch die Nutzung von betriebssystem-abhängigen<br />
Funktionen vorgenommen werden kann. Im Rahmen der Studienarbeit kam es bei<br />
der Verwendung von zusätzlichen Klassenbibliotheken zu komplexen Problemen. 7<br />
Aus diesem Grunde wurde die Plattformunabhängigkeit für die Fortsetzung der Arbeit<br />
aufgegeben, und sich dafür entschieden die betriebssystem-spezifischen Funktionen<br />
zu nutzen.<br />
Um die Ausgabe der Simulationsdaten über das Netzwerk testen zu können, wurde<br />
ebenfalls in der Studienarbeit ein TCP- sowie ein UDP-Server erstellt, der<br />
eingehende Daten in der Windows-Konsole ausgibt. Die Server wurden in JavaScript<br />
7 vgl. Studienarbeit MEYER, 2013<br />
Seite 17
Änderungen an Qucs<br />
geschrieben. Zum Ausführen wird Node.js benötigt. An den Servern wurden einige<br />
Veränderungen vorgenommen. So wird nun die Systemzeit zu der eine Nachricht<br />
erhalten wurde ebenfalls mit ausgegeben. Diese Server dienen ausschließlich zum<br />
Testen der Netzwerkausgabe von Qucs. Der Quelltext der Server ist in Anhang 3<br />
aufgeführt.<br />
3.2 Implementierung<br />
3.2.1 Der Programmeinstieg<br />
Wie aus Kapitel 3.1.2 hervorgeht, soll sowohl eine Übertragung über TCP, als auch<br />
über UDP möglich sein. Ebenso wurde in Kapitel 3.1.1 erörtert, dass für das<br />
Herstellen einer Verbindung, sowohl die IP-Adresse des Servers, sowie der Port auf<br />
den die Serveranwendung gebunden ist, bekannt sein müssen. Um diese<br />
Informationen an die Anwendung zu übertragen wurden die zwei neuen<br />
Starparameter -udp und -tcp hinzugefügt. Zusätzlich wurde ein Paramter<br />
hinzugefügt, über den die in Kapitel 3.1.5 angesprochene Verzögerungszeit in<br />
Millisekunden übergeben werden kann. Die Anwendung wartet nach einem<br />
Simulationsschritt die Zeit ab, bevor der nächste Simulationsschritt folgt. Listing 3<br />
zeigt die hinzugefügten Startparameter.<br />
Usage: qucsator.exe [OPTION]...<br />
-udp IP:PORT use UDP to send the simulation results to an UDP-Server<br />
-tcp IP:PORT use TCP to send the simulation results to a TCP-Server<br />
-d DELAY set delay in [ms] between simulation steps<br />
Listing 3 Startparameter für die Netzwerkkommunikation<br />
Der Programmeinstieg befindet sich in der Datei ucs.cpp. Hier befindet sich die<br />
Bearbeitung der bereits definierten Startparameter. Die Daten, zu der Zieladresse,<br />
sowie der Verzögerungszeit, werden hier in globalen Variablen der Netzwerkklasse<br />
hinterlegt. Dadurch ist im späteren Verlauf der Programmabarbeitung ein Zugriff auf<br />
diese möglich. Die am Quelltext getätigten Änderungen finden sich in Anhang 4.<br />
3.2.2 Die Netzwerkklasse<br />
Um die in Kapitel 3.1.6 angesprochenen Probleme zu verringern wurde eine eigene<br />
Klasse entwickelt, in der die Kommunikation zu dem Server umgesetzt wird. Wird<br />
später eine plattformunabhängige Klassenbibliothek eingebunden, muss somit nur<br />
die Netzwerkklasse angepasst werden. Alle Änderungen im Simulator die auf die<br />
Klasse zugreifen müssen nicht geändert werden.<br />
Im Anhang 5 befindet sich die Header-, sowie die Quelltext-Datei für die entwickelte<br />
Netzwerkklasse. Wie zu erkennen ist, wird im Rahmen dieser Arbeit die Windowsspezifische<br />
winsock2.h eingebunden, die Methoden zur Herstellung und Verwendung<br />
einer Netzwerkkommunikation bereitstellt. Zur Zuweisung des Protokolls wurde ein<br />
Enumerationstyp definiert. Dem Konstruktor muss die Ziel-Adresse, sowie das zu<br />
nutzende Protokoll übergeben werden. Zu der Ziel-Adresse gehören die IP-Adresse,<br />
Seite 18
Änderungen an Qucs<br />
sowie der Port des Ziels. Diese werden, wie unter Listing 3 beschrieben, als<br />
Startargument dem Programm als Zeichenkette übergeben und kann über die<br />
statische Funktion getAddr(...) in IP-Adresse und Port aufgeteilt werden. Die<br />
weiteren öffentlichen Methoden stellen die Verbindung zu einem Server her und<br />
ermöglichen das Senden von Daten. Eine Methode zum Empfangen wurde nicht<br />
implementiert, da keine Daten empfangen werden müssen. 8<br />
Zusätzlich wurden in der Header-Datei Deklarationen für externe, globale Variablen<br />
getroffen. Auf diese kann in jeder Quellcode-Datei zugegriffen werden, in welcher die<br />
network.h eingebunden wird. So können bereits zum Programmstart die Daten für<br />
die Zieladresse gespeichert werden.<br />
3.2.3 Einbinden der Netzwerkklasse<br />
3.2.3.1 Die Klassenhierarchie der Simulationssoftware<br />
Für die Übertragung der Simulationsergebnisse muss der aktuelle Simulationsschritt<br />
abgeschlossen sein. Bei der Analyse des Quellcodes von Qucs hat sich gezeigt,<br />
dass für jede Simulationsart eine eigene Klasse existiert. Damit nicht in jeder dieser<br />
Klassen das Senden der Daten implementiert werden muss, wurde zunächst die<br />
Klassenhierarchie von Qucs näher untersucht. Das Resultat ist in Abbildung 6 als<br />
vereinfachtes Klassendiagramm dargestellt.<br />
object<br />
analysis<br />
...<br />
nasolver parasweep ...<br />
dcsolver acsolver trsolver<br />
Abbildung 6 Die Klassenhierarchie der Simulatoren von Qucs<br />
Zu sehen ist, dass die Klassen für die Simulationen von der Klasse nasolver<br />
abgeleitet sind. Diese ist, ebenso wie die Klasse parasweep, von der Klasse analysis<br />
abgeleitet. Die Klasse parasweep wird für den Parameterdurchlauf benötigt. Die<br />
Berechnung der Simulationsergebnisse erfolgt jedoch ausschließlich in den von<br />
nasolver abgeleiteten Klassen. Es bietet sich daher an, die Netzwerkkommunikation<br />
in die nasolver–Klasse zu implementieren.<br />
8 Bei der Entwicklung der Netzwerkklasse wurde sich an die Anleitungen von c-worker.ch zum Thema<br />
„Winsock“ gehalten – vgl c-worker.ch<br />
Seite 19
Änderungen an Qucs<br />
Dazu muss in dieser die Netzwerkklasse eingebunden werden. Zusätzlich wird eine<br />
private Membervariable vom Typ network angelegt. Über diese soll die<br />
Kommunikation zum Server realisiert werden. In Anhang 6 sind die Änderungen<br />
aufgeführt, die an der Klasse vorgenommen wurden. In den Konstruktoren wird die<br />
Verbindung zu dem Server hergestellt. Bei den Variablen networkUse, networkAddr<br />
und networkProtocol handelt es sich um die externen globalen Variablen der<br />
Netzwerkklasse. Diese wurden bereits beim Programmstart in der Datei ucs.cpp<br />
gesetzt. Das Senden der Daten erfolgt in der Funktion saveResults(). An dieser<br />
Stelle sind alle Ergebnisse der Simulation berechnet worden. Vor dem Senden wird<br />
das XML-<strong>Dokument</strong> erzeugt. In der Variable netPrint sind an diesem Punkt des<br />
Programmablaufes alle XML-Konten für die Abhängigkeiten und Ergebnisse<br />
enthalten. Auf diese Variable wird in den folgenden beiden Kapiteln näher<br />
eingegangen.<br />
3.2.3.2 Erkennen der Simulationsergebnisse<br />
Bei der Analyse des Quelltextes von Qucs hat sich gezeigt, dass in der Datei<br />
analysis.cpp unter der Funktion saveVariable(...) die Simulationsergebnisse für<br />
die spätere Verwendung gespeichert werden. Um die Simulationsergebnisse für die<br />
Übertragung vorzubereiten wurde die Funktionen getDNode(...) implementiert.<br />
Diese erzeugt einen XML-Knoten und hängt diesen an die Zeichenkette netPrint an.<br />
Übergeben wird der Funktion der Name des Ergebnisses, sowie dessen Wert. Zu<br />
Beginn wird der Wert in eine Zeichenkette umgewandelt. Anschließend erfolgt die<br />
Erzeugung und Speicherung des Knotens. In C++ werden Zeichenketten als Felder<br />
vom Typ char behandelt. Für ein Feld wird nur ein begrenzter Bereich des<br />
Arbeitsspeichers reserviert. Werden der Zeichenkette neue Elemente angefügt, kann<br />
es zu einem Zugriff auf nicht reservierten Speicher kommen. Aus diesem Grund wird<br />
hier die existierende Zeichenkette in der Hilfvariable help gespeichert. Anschließend<br />
wird der Speicherbereich der ursprünglichen Zeichenkette netPrint freigegeben. Für<br />
diese wird dann ein neuer Speicherbereich allokiert, welcher der Länge der alten<br />
Zeichenkette plus der des neuen Knotens entspricht. Wurde der Speicher allokiert<br />
kann die neue Zeichenkette erstellt werden. Zum Schluss wird der Speicherbereich,<br />
den die Hilfsvariable belegt, wieder freigegeben und der XML-Knoten als<br />
Zeichenkette übergeben. Die netPrint-Variable wird nach jedem Simulationsschritt<br />
wieder zurückgesetzt.<br />
3.2.3.3 Erkennen der Abhängigkeiten<br />
Für die spätere Verarbeitung der Simulationsergebnisse, müssen deren<br />
Abhängigkeiten mit übertragen werden. Als Abhängigkeit ist beispielsweise für eine<br />
Transientsimulation die Zeit, oder für eine Wechselstromsimulation die Frequenz<br />
anzusehen. Werden in einer Simulation ein oder mehrere Parameterdurchläufe<br />
verwendet, entstehen zusätzliche Abhängigkeiten. Die Abhängigkeiten können dabei<br />
Seite 20
Änderungen an Qucs<br />
nicht in netPrint gespeichert werden. Obwohl für jeden Simulationsschritt ein neuer<br />
Wert für beispielsweise die Frequenz vorliegt, muss beachtet werden, dass ein<br />
übergeordneter Parameterdurchlauf existieren kann. Wird der aktuelle Wert des<br />
Parameters in die Variable netPrint geschrieben, wäre dieser nur im ersten<br />
Simulationsschritt bekannt. Es müssen daher die Werte der übergeordneten<br />
Simulationen gespeichert werden.<br />
Dazu wurde die solve()-Funktion der analysis-Klasse überladen. Dieser kann nun<br />
eine Zeichenkette übergeben werden, die in die zusätzliche Membervariable Indeps<br />
kopiert wird. Zusätzlich wurde eine Funktion getINode() implementiert. Dieser<br />
werden der Name, sowie der aktuelle Wert des aktuellen Simulationsschrittes<br />
übergeben. In der Funktion wird der XML-Knoten für die Abhängigkeit erzeugt. Die<br />
Funktion gibt anschließend die Zusammenführung der bereits existieren Indeps-<br />
Variable und des neuen XML-Knotens zurück. Der zuletzt hinzugefügt Knoten steht<br />
dabei an erster Stelle. Dies ist für die spätere Auswertung des XML-<strong>Dokument</strong>es von<br />
Bedeutung. Für jeden Simulationsschritt sind somit die übergeordneten Parameter,<br />
sowie der aktuelle Wert bekannt und kann vor der Berechnung der Ergebnisse in die<br />
netPrint-Variable geschrieben werden. Die Änderungen an der analysis-Klasse<br />
sowie ein Beispiel für die Verwendung der getINode()- und getDNode()-Funktion<br />
befinden sich in Anhang 7.<br />
Seite 21
Die QucsLiveEngine<br />
4 Die QucsLiveEngine<br />
4.1 Vorbetrachtungen<br />
4.1.1 Starten der Simulation<br />
Eine Aufgabe der QucsLiveEngine besteht darin, die Simulationsanwendung zu<br />
starten. Dazu muss dieser der Speicherort der qucsator.exe-Datei bekannt sein. Der<br />
Pfadverweis soll, wie bereits in der QucsEngine, über einen Eintrag in der Windows-<br />
Registrierdatenbank zur Verfügung gestellt werden. Wäre der Speicherort im<br />
Quellcode hinterlegt, müsste die Anwendung, bei einer Änderung des Speicherortes,<br />
neu erstellt werden. Ebenfalls können durch diese Methode Startparameter einfach<br />
bearbeitet werden. Der Befehl zum Starten der Simulation ist dabei wie folgt<br />
aufgebaut:<br />
[QucsDir]\bin\qucsator.exe –i -o –tcp 127.0.0.1:<br />
{-d x}<br />
Dabei steht QucsDir für das Installationsverzeichnis von Qucs. Die Zeichenketten<br />
und werden durch die Pfadangaben zu der Netzliste und der<br />
Ausgabedatei ersetzt. Über wird der Anwendung mitgeteilt, auf welchem Port<br />
der Server die Ergebnisse entgegennimmt. Zusätzlich kann über „–d x“ eine<br />
Verzögerung zwischen den einzelnen Simulationsschritten in x Millisekunden erzeugt<br />
werden.<br />
4.1.2 Empfangen der Simulationsdaten<br />
Um die Simulationsdaten von der Simulationsanwendung entgegennehmen zu<br />
können, muss zunächst ein Server implementiert werden. Qucs bietet die Möglichkeit<br />
die Daten sowohl über TCP als auch UDP zu übertragen. Für die Wahl des<br />
entsprechenden Protokolls muss nun betrachtet werden, wie die Simulationsdaten in<br />
der QucsLiveEngine weiter verarbeitet werden. Zunächst müssen die Daten in das<br />
framework-spezifische Format konvertiert und anschließend gespeichert werden. Es<br />
ist nun die Situation denkbar, dass die Konvertierung und Speicherung der Daten<br />
mehr Zeit in Anspruch nimmt, als die Berechnung des nächsten Simulationsschrittes.<br />
Bei der Verwendung von UDP würde dies bedeuten, dass die Daten schneller in den<br />
UDP-Puffer geschrieben werden, als sie daraus gelesen werden. Da durch UDP<br />
nicht sichergestellt wird, dass alle Daten korrekt beim Empfänger eingehen, kann es<br />
zu einem Überlauf des Puffers führen. TCP hingegen stellt sicher, dass alle<br />
gesendeten Daten beim Empfänger eingehen. Das Senden der Daten über TCP<br />
wurde in Qucs über einen blockierenden Algorithmus implementiert. Dies bedeutet,<br />
dass der nächste Simulationsschritt erst erfolgt, wenn für die zuletzt übertragenen<br />
Daten eine Empfangsbestätigung (Acknowledgement) eingegangen ist. Da Qucs auf<br />
die Bestätigung des Empfängers warten muss, kann es zu einer längeren<br />
Bearbeitungszeit der Simulation kommen.<br />
Seite 22
Die QucsLiveEngine<br />
Ein weiterer Aspekt, der für die Verwendung des TCP spricht, ist die Verarbeitung<br />
der Daten. Diese werden fortlaufend in einer Datei hinterlegt. Somit ist die korrekte<br />
Reihenfolge der empfangen Daten von hoher Bedeutung. TCP stellt im Gegensatz<br />
zu UDP sicher, dass die Daten in der Reihenfolge beim Empfänger eingehen, wie sie<br />
beim Sender versandt wurden. Somit fiel die Wahl auf TCP, weil dieses sicherstellt,<br />
dass alle Daten korrekt empfangen werden. Der daraus resultierend längeren<br />
Bearbeitungszeit ist eine wesentlich niedrigere Priorität zuzuordnen, und kann daher<br />
in Kauf genommen werden.<br />
Vom Programmablauf her ist zu beachten, dass der TCP-Server gestartet wurde und<br />
sich im Listen-Modus befindet, bevor die ersten Daten übertragen werden. Da es<br />
sich bei Programmen um eine sequentielle Abarbeitung der Befehle handelt muss<br />
dementsprechend zuerst der TCP-Server gestartet werden, bevor die<br />
QucsLiveEngine die Simulation startet. Betrachtet man die Funktionsweise des TCP-<br />
Servers kommt es hier jedoch zu einem Problem. Befindet sich der Server im Listen-<br />
Modus wird die Anwendung blockiert bis eine Nachricht eingegangen ist. Da Qucs<br />
jedoch erst gestartet werden kann, nachdem der TCP-Server sich in dem Listen-<br />
Modus befindet, entsteht hier ein Blockieren der gesamten QucsLiveEngine. Eine<br />
Lösung dieses Problems bietet die Verwendung von Threads. So wird zunächst der<br />
Server gestartet. Bevor dieser in den Listen-Modus gesetzt wird, wird ein neuer<br />
Thread erstellt. In diesem wird der Server in den Listen-Modus gesetzt und kann die<br />
von Qucs gesendeten Nachrichten empfangen und weiter verarbeiten Somit kann im<br />
ursprünglichen Thread die Funktion aufgerufen werden, die schließlich Qucs startet.<br />
Anschließend muss darauf gewartet werden, dass zum einen die Anwendung<br />
geschlossen wurde, und zum anderen der zusätzliche Thread beendet wurde. Es<br />
bietet sich hier an, diesen zu beenden, sobald die TCP-Verbindung vom Client<br />
unterbrochen wurde. Anschließend kann die QucsLiveEngine wie gewohnt<br />
ausgeführt werden.<br />
4.1.3 Auswerten des XML-<strong>Dokument</strong>es<br />
Aus Kapitel 3.1.4.2 geht hervor, dass die Daten in einem XML-<strong>Dokument</strong> übertragen<br />
werden. Um die Simulationsergebnisse aus dem XML-<strong>Dokument</strong> zu erhalten wird ein<br />
XML-Parser benötigt. Für die Programmiersprache C++ existieren bereits viele XML-<br />
Parser, die sich in ihrem Umfang und der Funktionalität unterscheiden. Der Zugriff<br />
auf das <strong>Dokument</strong> erfolgt dabei üblicherweise über die DOM-API (Document Object<br />
Model – Application Programming Interface) oder die SAX-API (Simple API for XML).<br />
Bei der DOM-API wird das XML-<strong>Dokument</strong> als Baumstruktur abgebildet. Dadurch ist<br />
ein sowohl lesender, als auch schreibender Zugriff auf alle Elemente des<br />
<strong>Dokument</strong>es möglich. Dies hat jedoch den Nachteil, dass große XML-<strong>Dokument</strong>e viel<br />
Seite 23
Die QucsLiveEngine<br />
Arbeitsspeicher beanspruchen. 9 Bei der SAX-API hingegen wird das XML-<strong>Dokument</strong><br />
als Datenstrom abgebildet. Treten beim Lesen des Streams vordefinierte Ereignisse<br />
auf, werden den Ereignissen zugewiesene Funktionen aufgerufen. Der Vorteil liegt<br />
im niedrigen Speicherverbrauch, jedoch ist der Programmieraufwand deutlich größer<br />
gegenüber der DOM-API. 10 Da es sich bei den übertragenen Simulationsergebnissen<br />
um kleine XML-<strong>Dokument</strong>e handelt, wurde sich dazu entschieden, einen XML-Parser<br />
zu verwenden, der die DOM-API nutzt.<br />
Die XML-Parser unterscheiden sich zusätzlich im Umfang ihrer Funktionalität. So<br />
werden beispielsweise Namensräume oder <strong>Dokument</strong>typdefinitionen nur von einigen<br />
XML-Parsern unterstützt. Da diese Besonderheiten in dem entworfenen Datenformat<br />
jedoch nicht vorkommen, wurde sich für die Verwendung des XML-Parsers TinyXML-<br />
2 entschieden. Ebenfalls steht die Bibliothek unter der Zlib-Lizenz, was die<br />
Verwendung im kommerziellen Bereich ermöglicht. 11 Ein weiterer Vorteil besteht<br />
darin, dass die Bibliothek keine betriebssystemspezifischen Funktionen verwendet<br />
und somit plattformübergreifend verwendet werden kann.<br />
4.1.4 Konvertieren der Daten<br />
Da die Transim Technology Corportaion ein eigenes Format zur Speicherung der<br />
Simulationsdaten verwendet, müssen die Ergebnisse der Simulation in dieses<br />
Format konvertiert werden. Bisher erfolgte dies nachdem die Simulation beendet<br />
wurde und die Ausgabedatei erzeugt wurde. Um die Zwischenergebnisse dem<br />
EngineFramework zur Verfügung zu stellen, müssen jedoch diese bereits beim<br />
Eintreffen konvertiert werden und gespeichert werden. Ebenfalls müssen die Daten<br />
dem Schematic zur Verfügung gestellt werden. In den folgenden Kapiteln wird näher<br />
auf das Speichern der Daten eingegangen. Das Bereitstellen für das Schematic wird<br />
in einem späteren Kapitel näher erläutert.<br />
Aus Kapitel 3.1.4.2 geht hervor, dass für jeden Simulationsschritt ein XML-<strong>Dokument</strong><br />
übertragen wird. In diesem befinden sich neben den Ergebnissen (-Tags) auch<br />
die zugehörigen Abhängigkeiten (-Tags). Eine Besonderheit tritt ein, wenn zu den<br />
Ergebnissen mehrere Abhängigkeiten vorliegen. Darauf wird jedoch in einem<br />
späteren Kapitel näher eingegangen. Für das Konvertieren der Daten ist hier zu<br />
beachten, dass die erste übergebene Abhängigkeit den X-Wert des aktuellen<br />
Simulationsschrittes enthält. In den Ergebnis-Tags befinden sich die entsprechenden<br />
Y-Werte. Die Werte liegen zunächst als Zeichenkette vor und müssen in eine<br />
Fließkommazahl (float) umgewandelt werden. Als Attribut befindet sich in den<br />
Ergebnis-Tags der Name des entsprechenden Kurvenverlaufs (engl.: Waveform). Für<br />
9 vgl. online “Document Object Model” auf Wikipedia.org (09.08.2013)<br />
10 vgl. online “Simple API for XML” auf Wikipeida.org (09.08.2013)<br />
11 vgl online “The zlib/libpng License (Zlib)” auf Opensource.org<br />
Seite 24
Die QucsLiveEngine<br />
jeden Kurvenverlauf wird eine Datei angelegt in die die Ergebnisse geschrieben<br />
werden.<br />
4.1.5 Erzeugen der Waveform Dateien<br />
Das Speichern der Simulationsergebnisse in Dateien ist notwendig, um später auf<br />
diese zugreifen zu können. Diese Dateien werden in einem Unterordner gespeichert<br />
und müssen nach dem folgenden Schema gespeichert werden:<br />
/[UserID]/Waveform/[SimID]_[JobHandle]_group0_c[i]<br />
Die UserID stellt die Identifikationsnummer des Benutzers dar. Diese wird aus der<br />
Webanwendung übergeben. Bei der SimID handelt es sich um eine Kennzeichnung<br />
der Simulation. Diese wird beim Starten der Simulation der QucsLiveEngine<br />
übergeben. Auf diese soll hier nicht weiter eingegangen werden. Das JobHandle ist<br />
die bereits in Kapitel 2.2.2 angesprochene Identifikationsnummer der Simulation. Das<br />
i stellt eine durchlaufende Ganzzahl dar. Diese dient der Identifikation der<br />
unterschiedlichen Kurvenverläufe. Wie die Zuweisung dieser Nummer zu den<br />
Kurvenverläufen erfolgt wird im nächsten Kapitel näher erläutert. Zu beachten ist,<br />
dass die Dateien über keine Dateinamenserweiterung verfügt.<br />
In den Waveform-Dateien werden die Werte byteweise hintereinander gespeichert.<br />
Dazu müssen diese Werte zunächst von einer Fließkommazahl in ein vier Byte<br />
großes Array des Types Byte umgewandelt werden. Bei dem Speichern ist darauf zu<br />
achten, ob es sich bei dem Y-Wert um eine reelle oder komplexe Zahl handelt. Bei<br />
einer komplexen Zahl erfolgt eine Trennung zwischen Real- und Imaginärteil. Beide<br />
Anteile werden anschließend in der Datei gespeichert. Abbildung 7 zeigt dabei die<br />
Reihenfolge in der die Zahlen zu speichern sind.<br />
Start<br />
Schreibe(XValue->Real())<br />
YValue->Complex()<br />
ja<br />
Schreibe(XValue->Real())<br />
nein<br />
Schreibe(YValue->Real())<br />
YValue->Complex()<br />
ja<br />
Schreibe(YValue->Real())<br />
nein<br />
Ende<br />
Abbildung 7 Speichern der Ergebnisse in der Waveform-Datei<br />
Seite 25
Die QucsLiveEngine<br />
Ist der Y-Wert komplex, muss ebenfalls für den X-Wert ein komplexer Wert<br />
gespeichert werden. Handelt es sich um eine reale Zahl ist der Imaginärteil als „0.0“<br />
zu speichern.<br />
4.1.6 Die Manifest Datei<br />
Um eine Zuordnung der Waveform-Dateien zu den ursprünglichen Knotenpunkten zu<br />
erhalten muss eine sogenannte Manifest-Datei angelegt werden. Dabei ist der<br />
Dateiname wie folgt anzulegen:<br />
/[UserID]/Waveform/[SimID]_[JobHandle]_group0.manifest<br />
In diese Datei werden die Zuordnungen zeilen weise eingetragen. Eine Zuordnung<br />
muss nach dem folgenden Format angelegt werden:<br />
[Name],[Dateiname],[X-Typ],[Y-Typ],1,[Komplex]<br />
Der Name steht dabei für den Namen des Kurvenverlaufes. Dieser kann<br />
beispielsweise eine Ausgangsspannung, ein Eingangsstrom, oder ein beliebiger in<br />
der Schaltung angegebener Abtastpunkt (engl.: Probe) sein. Der Dateiname steht für<br />
die erzeugte Waveform-Datei und muss dem in Kapitel 4.1.5 erläuterten Format<br />
entsprechen. Anschließend folgen die Bezeichnungen für den physikalischen Typ der<br />
X- und Y-Achse. Eine Auswahl der möglichen Bezeichnungen findet sich in Tabelle<br />
2. Die vollständige Liste der Bezeichnungen befindet sich in Anhang 8.<br />
# Bezeichnungen<br />
3 amp current<br />
4 second time<br />
… …<br />
Tabelle 2 Format der Achsbezeichung in der Manifest-Datei<br />
Dabei kann in der Manifest-Datei entweder die Bezeichnung oder die zugehörige<br />
Zahl aufgeführt werden. Um einen Typ zuordnen zu können, muss eine Angabe zu<br />
diesem bereits von dem Simulator übertragen werden. Zuletzt muss angegeben<br />
werden, ob es sich bei den gespeicherten Werten um reelle, oder komplexe Zahlen<br />
handelt. Sind die Zahlen reell, wird der Wert 1 eingetragen, sind sie komplex, der<br />
Wert 2.<br />
4.1.7 Speichern von Kurvenscharen<br />
Eine gesonderte Rolle spielen Simulationen, bei denen die Ergebnisse mehrere<br />
Abhängigkeiten besitzen. Dieser Fall tritt ein, wenn für gewisse Bauteile ein<br />
Parameterdurchlauf ausgeführt wird. Das bedeutet, dass einem Bauteil ein<br />
Parameter zugewiesen wird. Dieser ist mit einem Start- und Endwert, sowie der<br />
Anzahl an Schritten definiert. Die Simulation muss für jeden dieser Parameter<br />
ausgeführt werden. Für die Ergebnisse bedeutet dies, das nicht ein Kurvenverlauf<br />
entsteht, sondern eine Kurvenschar. In diesem Fall muss jede Kurve in einer<br />
separaten Waveform-Datei gespeichert werden. Die Benennung dieser Datei erfolgt<br />
Seite 26
Die QucsLiveEngine<br />
zunächst wie in 4.1.5 angegeben, jedoch wird dem Dateinamen zusätzlich folgende<br />
Zeichenkette angehängt:<br />
/[UserID]/Waveform/[SimID]_[JobHandle]_group0_c[i]_div[j]<br />
Der Parameter j ist dabei ein fortlaufender Zähler der für die Kurve in der<br />
Kurvenschar steht. Die Manifest-Datei muss ebenfalls angepasst werden, um eine<br />
Zuordnung der einzelnen Kurven zu den Parametern zu ermöglichen. Das Format<br />
der Daten bleibt wie in 4.1.6 bestehen, jedoch muss in der ersten Zeile ein Hinweis<br />
angegeben werden, dass für einen Knotenpunkt mehrere Kurven vorliegen. Dieser<br />
Hinweis muss nach dem folgenden Format aufgebaut sein:<br />
# Division Labels (comma separated) : [Name Div1],[Name Div2], ...<br />
Die Namen für die unterteilten Kurven kann dabei beliebig erfolgen, es bietet sich<br />
jedoch an, einen Verweis auf die zugehörige Abhängigkeit anzugeben. Die Anzahl<br />
der Namen muss mit der Anzahl der einzelnen Kurven (s.o. j) übereinstimmen. Da<br />
die Namen durch Kommata getrennt werden, dürfen die Namen selbst keine<br />
Kommata enthalten. Neben der Manifest-Datei muss ebenfalls eine Division-Datei<br />
angelegt werden. Dieser ähnelt der Manifest-Datei. In der ersten Zeile befindet sich<br />
die Angabe zu den Kurvenscharen. Diese kann aus der Manifest-Datei übernommen<br />
werden. Die Angabe zu den Waveform-Dateien erfolgt anschließend wie folgt:<br />
[Name],[Dateiname],0<br />
Zur besseren Verdeutlichung befindet sich in Anhang 9 ein umfangreiches Beispiel.<br />
Hier handelt es sich um eine Transientsimulation des Tiefpass-Filters. Neben dieser<br />
Simulation wurde zusätzlich ein Parameterdurchlauf mit vier Schritten für den<br />
Wiederstand hinzugefügt. In der Ausgabe sind neben der Eingangsspannung die vier<br />
Ausgangsspannungen zu erkennen. In dem übertragenen Zwischenergebnis finden<br />
sich neben den Ergebnissen Ue.It, Ue.Vt und Ua.Vt auch deren Abhängigkeiten<br />
wieder. Dabei steht time.s für den aktuellen Zeitwert der Transientsimulation und R1<br />
für den aktuellen Widerstandswert des Parameterdurchlaufs. In den erzeugten<br />
Waveform-Dateien sind die drei Ergebnisse mit jeweils vier unterschiedlichen Kurven<br />
zu erkennen. Schließlich findet sich die Zuordnung der Waveform-Dateien in der<br />
Manifest-Datei wieder.<br />
4.2 Implementierung<br />
4.2.1 Starten des TCP-Servers und der Simulationsanwendung<br />
Für die Implementierung der Netzwerkkommunikation wird in der QucsLiveEngine<br />
die winsock2-Bibiliothek verwendet. Zum Starten eines TCP-Servers wird ein SOCKET-<br />
Objekt benötigt. Diesem wird ebenfalls als Adressfamilie „Internetadresse“<br />
zugewiesen. Als IP-Adresse wird für einen Server die Konstante INADDR_ANY<br />
zugewiesen. Anschließend muss der Server an einen Port gebunden werden. Ist<br />
Seite 27
Die QucsLiveEngine<br />
dieser Port bereits an eine andere Anwendung gebunden, wird versucht sich an den<br />
nächsten Port zu binden. Listing 4 zeigt die Implementierung zur Auswahl des Ports.<br />
// Get the minimum and maximum value for the Port<br />
int minPort, maxPort;<br />
if(!GetPort(&minPort, &maxPort))<br />
return false;<br />
// Find Free Port to bind on:<br />
int port = minPort-1;<br />
do {<br />
port++;<br />
local.sin_port=htons((u_short)port);<br />
} while(port maxPort)<br />
return false;<br />
Listing 4 Auswahl des Ports für den TCP-Server<br />
Die Funktion GetPort() liest aus der Registrierungsdatenbank einen Bereich in dem<br />
der Port liegen darf. Anschließend wird versucht den Server an den Port zu binden.<br />
Schlägt diese Funktion fehl, wird der Port um eins inkrementiert und es wird erneut<br />
versucht. Überschreitet die port-Variable den maximal zulässigen Wert, wird die<br />
Simulation abgebrochen. War die Bindung an einen Port erfolgreich, kann der Server<br />
gestartet werden. Wie bereits in Kapitel 4.1.2 erläutert, muss dieser in einem<br />
zusätzlichen Thread erzeugt werden. Für eine bessere Übersicht wurde dies in einer<br />
zusätzlichen Klasse implementiert. Zum Starten des neuen Threads wird die<br />
Funktion AfxBeginThread() verwendet. Diese wird durch das .NET-Framework<br />
bereitgestellt. Als Ergebnis liefert diese einen Zeiger auf ein Objekt vom Typ<br />
CWinThread. Ist der Thread gestartet, wird der Server in den Listen-Modus gesetzt<br />
und wartet auf eine eingehende Verbindung.<br />
Nach dem Start des neuen Threads wird in dem ursprünglichen Thread die<br />
Anwendung Qucs gestartet. Dies erfolgt wie bisher, jedoch wird ihr als zusätzlichen<br />
Startparameter die Adresse, sowie der Port des TCP-Servers übergeben. Da die<br />
Anwendung und der TCP-Server auf der gleichen Maschine ausgeführt werden, ist<br />
die IP-Adresse die 127.0.0.1. Als Port wird der ermittelte Port übergeben.<br />
4.2.2 Empfangen der Daten<br />
Wurde die Verbindung zwischen Client und Server hergestellt, können die Daten<br />
übertragen werden. Um Daten entgegen zu nehmen, muss im Server die Funktion<br />
recv(...) aufgerufen werden. Diese Funktion blockiert den Programmablauf, bis<br />
Daten eingehen. Als Parameter müssen ihr, die Client-ID, ein Zeiger auf einen<br />
Puffer, die Größe des Puffers übergeben werden. Die Größe des Puffers wurde hier<br />
anhand der Eigenschaften von TCP bestimmt. Diese sind die dem Request for<br />
Comments (RFC) 793 aufgeführt. 12 Einen Teil des TCP-Headers stellt das Window-<br />
12 vgl. RFC 793<br />
Seite 28
Die QucsLiveEngine<br />
Feld dar. Dieses Feld gibt an, wie viele Daten vom Server empfangen werden<br />
können, bevor dieser eine Bestätigung (Acknowledgement) an den Client senden<br />
muss. Bei einem kleinen Wert muss demzufolge der Client oft auf die Bestätigung<br />
des Servers warten. Somit würde das gesamte System verlangsamt. Wird ein hoher<br />
Wert gewählt, müssen weniger Bestätigungen geschickt werden, und eine schnellere<br />
Übertragung ist gewährleistet. Der Nachteil eines großen Wertes tritt ein, wenn es zu<br />
einer fehlerhaften Übertragung kommt. In diesem Fall müssen alle Daten, die nicht<br />
bestätigt wurden, erneut gesendet werden. Bei störanfälligen Netzwerken würde sich<br />
ein großer Wert negativ auf die Geschwindigkeit auswirken. Da sich der Client und<br />
der Server hier jedoch auf der gleichen Maschine befinden können fehlerhafte<br />
Übertragungen nahezu ausgeschlossen werden. Somit kann ein hoher Wert gewählt<br />
werden. Im TCP-Header sind 16 Bit für den Window-Wert vorgesehen. Dies<br />
entspricht einer maximalen Größe von 65.535 Byte, was ebenfalls als Größe für den<br />
Puffer gewählt wird.<br />
Die recv()-Funktion liefert als Rückgabewert die Anzahl der empfangen Daten. Wird<br />
die Verbindung zum Client beendet liefert sie den Wert 0. Werden mehr Daten<br />
gesendet als in den übergeben Puffer geschrieben werden, muss die recv()-<br />
Funktion erneut aufgerufen werden um die nächsten Daten zu empfangen. Daraus<br />
resultierend wurde eine Schleife entwickelt, die abgebrochen wird, wenn die<br />
Verbindung beendet wurde.<br />
4.2.3 Auslesen des TCP-Stroms<br />
Zum Übertragen der Simulationsergebnisse werden diese in einem XML-<strong>Dokument</strong><br />
gespeichert. Wurden die Daten auf dem Server empfangen befinden sie sich in dem<br />
bereits erwähnten Puffer. Bei schnellen Simulationen ist es aufgrund des großgewählten<br />
Puffers möglich, dass mehrere Datensätze in den Puffer geschrieben<br />
werden. Mithilfe der TinyXML-2-Bibliothek kann eine Zeichenkette zu einem XML-<br />
Objekt geparst werden. Dazu darf in der Zeichenkette jedoch nur ein XML-<strong>Dokument</strong><br />
aufgeführt sein. Aus diesem Grund müssen die Datensätze aus dem Puffer zunächst<br />
getrennt werden. Jedes XML-<strong>Dokument</strong> beginnt mit der XML-Deklaration und<br />
beginnt demzufolge mit der Zeichenkette „
Die QucsLiveEngine<br />
// position-variable required to split the received string.<br />
int pos1=0, pos2 = 0, size;<br />
char* pch;<br />
pch = strchr(data, '?');<br />
while (pch != NULL) {<br />
if(*(pch-1)=='
Die QucsLiveEngine<br />
Dabei muss eine Zuordnung der Ergebnisse zu den Dateien erfolgen. In 4.1.5 wurde<br />
erläutert, dass sich die Dateien in dem c-Wert unterscheiden. Es wurde eine Klasse<br />
zur Verwaltung der Ergebnisnamen und den c-Werten entwickelt. Sie bietet eine<br />
Funktion, der der Name des Ergebnisses und eine Referenz auf eine Zeichenkette<br />
übergeben werden. In der Zeichenkette wird der zum Namen zugehörige c-Wert<br />
zurückgegeben. Die Funktion überprüft, ob bereits eine Zuordnung vorhanden ist.<br />
Falls nicht wird eine neue hinzugefügt. Dabei werden diese fortlaufend<br />
durchnummeriert. Wurde ein neuer Eintrag hinzugefügt, wird von der Funktion ein<br />
true zurückgegeben. War die Zuordnung bereits vorhanden ein false. Dieser<br />
Rückgabewert wird gespeichert und gibt später an, ob eine neue Manifest-Datei<br />
erzeugt werden muss. Ist der c-Wert bekannt, kann das Ergebnis in der Waveform-<br />
Datei gespeichert werden. Dies erfolgt wie in der bisherigen QucsEngine.<br />
Wie in 4.1.7 erläutert, muss eine Unterscheidung bei Kurvenscharen erfolgen. Diese<br />
treten auf, wenn in dem vector mehr als ein Eintrag vorhanden ist. In diesem Fall<br />
erfolgt eine Zuordnung der zusätzlichen Abhängigkeiten zu dem entsprechenden<br />
div-Wert. Diese Zuordnung erfolgt ähnlich wie die Zuordnung der Ergebnisse zu den<br />
c-Werten der Waveform-Dateien. Wird eine neue Zuordnung angelegt, muss<br />
ebenfalls eine neue Manifest-Datei, sowie eine Division-Datei erzeugt werden.<br />
Wurden alle Ergebnisse in die Waveform-Dateien geschrieben muss geprüft werden,<br />
ob eine neue Manifest- und Divison-Datei erstellt werden müssen. Sind div-<br />
Zuordnungen vorhanden muss die erste Zeile der Manifest- und Division-Datei<br />
erzeugt werden. Anschließend werden die c-Zuordnungen durchlaufen und die<br />
entsprechenden Einträge in der Manifest- und Division-Datei gesetzt. Das neue<br />
Erstellen der Dateien ist notwendig, damit WebScope Zugriff auf die neu erstellen<br />
Waveform-Dateien erhält.<br />
Seite 31
Der EngineServer<br />
5 Der EngineServer<br />
5.1 Vorbetrachtungen<br />
5.1.1 Das Postprocessing<br />
Um die gespeicherten Simulationsdaten weiter verwenden zu können muss eine<br />
Nachbearbeitung (engl.: Postprocessing) stattfinden. Das Postprocessing wird dabei<br />
stets auf dem EngineServer ausgeführt. Es sorgt dabei für die Umwandlung der<br />
Simulationsergebnisse in eine verständliche Darstellung. Dazu zählt unter anderem<br />
die Darstellung der Kurvenverläufe in unterschiedlichen Diagrammen. Neben den<br />
Möglichkeiten Bild-Dateien zu erzeugen ist das Postprocessing ebenfalls dafür<br />
zuständig die Daten für die spätere Ausgabe in dem Webbrowser vorzubereiten.<br />
Dem Postprocessing können dazu unterschiedliche Befehle, sogenannte<br />
PostProcessingCommands, übergeben werden.<br />
Für die Darstellung von Kurvenverläufen bedeutet dies zunächst, dass ein<br />
entsprechendes Diagramm erzeugt wird. Es können unterschiedliche Arten von<br />
Diagrammen erzeugt werden, jedoch soll in dieser Arbeit lediglich auf das Erstellen<br />
von kartesischen Diagrammen eingegangen werden. Diese bestehen aus einer X-<br />
Achse und einer oder mehreren Y-Achsen. Der X-Achse wird der sich ändernde Wert<br />
zugewiesen. Für eine Transientsimulation wäre die beispielsweise die Zeit, bei einer<br />
Wechselstrom-Simulation die Frequenz. Auf der/den Y-Achse/n werden die<br />
Ergebnisse zugeordnet. Um solch eine Beschreibung, die später von WebScope<br />
interpretiert werden kann, zu erzeugen wird das bereits existierende Unterprogramm<br />
„FlashGen“ verwendet. Dieses Unterprogramm entstand, als die<br />
Simulationsergebnisse in dem Browser noch über den Adobe Flash Player<br />
ausgegeben wurde. Dafür musste der entsprechende Flash Player auf dem PC des<br />
Nutzers installiert sein. Um eine Unabhängigkeit von zusätzlicher Software zu<br />
erreichen hat die Transim Technology die Ausgabe der Daten über HTML und<br />
JavaScript realisiert. Dies wird von jedem Browser unterstützt und benötigt keine<br />
Installation von zusätzlicher Software. Der Name FlashGen stellt daher lediglich ein<br />
historisches Zeichen dar. Die Aufgabe von FlashGen besteht darin, die Beschreibung<br />
von allen benötigten Diagrammen zu erzeugen. Dazu müssen diesem mehrere<br />
Befehle, die sogenannten PostProcessingCommands, übergeben werden. In Listing<br />
6 ist ein Beispiel für diese Kommandos aufgeführt.<br />
oFlashGen = CreateInstance("FlashGen")<br />
oFlashGen.AddGroup(Name="Voltage", XMax="5E-4")<br />
oFlashGen.AddWaveform(Name="Ue.Vt", Group="Voltage", DisplayName="Ua.Vt",<br />
Color="#FF0000", Enabled="true", YType="Voltage",<br />
YMin="-1.1", YMax="1.1")<br />
oFlashGen.AddWaveform(Name="Ua.Vt", Group="Voltage", ...)<br />
oFlashGen.CreateFlashManifest()<br />
Listing 6 Beispiel für die PostProcessingCommands<br />
Seite 32
Der EngineServer<br />
Es muss zuerst eine Instanz von FlashGen erzeugt werden. Danach folgen die<br />
Definitionen für die Ergebnisgruppen. Hier wird eine Gruppe für die Ausgabe der<br />
Spannungen erstellt. Bisher wurden die Grenzen für die X-Achse aus den<br />
vorhandenen Waveform-Dateien ermittelt. Diese werden jetzt zur Laufzeit mit neuen<br />
Werten gefüllt. Wird ein neuer Wert in die Dateien eingetragen müsste FlashGen neu<br />
gestartet werden, um die neuen Grenzen zu ermitteln. Um dies zu verhindern wurde<br />
der Funktion AddGroup() ein neuer Parameter hinzugefügt um den Maximalwert für<br />
die X-Achse setzen zu können. 14 Nach Erstellen der Gruppen können diesen mit der<br />
Funktion AddWaveForm() Kurvenverläufe hinzugefügt werden. Der Name muss dabei<br />
dem Namen in der Manifest-Datei entsprechen. Neben dem Namen kann der Kurve<br />
ein Anzeigename, eine Farbe, sowie weitere Eigenschaften zugewiesen werden.<br />
Über den Befehl CreateFlashManifest() wird schließlich von FlashGen eine XML-<br />
Datei erzeugt, in der sich die Beschreibung aller benötigten Dateien befindet. Diese<br />
Datei wird unter dem folgenden Namen gespeichert:<br />
[SimID]_[JobHandle]_JS_VIEWER.xml<br />
In Listing 7 ist ein Beispiel für eine solche Datei aufgeführt. Zur besseren Übersicht<br />
wurden hier einige Attribute aus den Knoten entfernt.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
Listing 7 Von FlashGen erzeugte Datei zur Berschreibung der Diagramme<br />
In dem XML-<strong>Dokument</strong> befindet sich der Wurzelknoten . Dieser kann<br />
mehrere -Knoten beinhalten. Diese Knoten stehen jeweils für ein<br />
Diagramm. Innerhalb dieser Knoten befindet sich in den -Knoten die<br />
Beschreibung der jeweiligen Achsen. Zu erkennen sind die Attribute die die<br />
Maximum- und Minimumwerte der Achsen angeben. Es können ebenfalls mehrere<br />
Definitionen für Y-Achsen vorhanden sein. Dies wird benötigt, wenn mehrere Kurven<br />
unterschiedlichen Typs, oder mit unterschiedlicher Achseinteilung in einem<br />
14 Anm.: Die Änderungen an FlashGen wurden von einem Mitarbeiter der Transim Technology<br />
durchgeführt.<br />
Seite 33
Der EngineServer<br />
Diagramm dargestellt werden sollen. In den -Knoten befindet sich die<br />
Beschreibung der Kurvenverläufe. Als Attribut dieses Knotens werden der Name, die<br />
zu verwendende Farbe, sowie die Information ob die Kurve angezeigt werden soll<br />
angegeben. In dem Attribut locator befindet sich ein Uniform Resource Locator (URL)<br />
der auf die zugehörige Waveform-Datei verweist. In den -Knoten befinden<br />
sich schließlich die -Knoten, in denen die kurvenspezifischen Achsen definiert<br />
sind. Über die Attribute sind hier ebenfalls der Name sowie die Maximum- und<br />
Minimumwerte angegeben. Die Zuordnung zu der Achsengruppe erfolgt über das<br />
Attribut type.<br />
Im bisherigen Verlauf des Simulationsvorganges wurde das Postprocessing<br />
gestartet, nachdem die Simulationsanwendung beendet wurde und alle Waveform-<br />
Dateien, sowie die Manifest-Datei erzeugt wurden. Für den Start von WebScope wird<br />
jedoch die von FlashGen erzeugte XML-Datei benötigt. Diese muss demzufolge<br />
ebenfalls zur Laufzeit der Simulation erstellt werden. Die Minimum- und Maximum-<br />
Werte können FlashGen über die PostprocessingCommands übermittelt werden. Es<br />
muss daher nicht auf alle Simulationsergebnisse gewartet werden. Das Erzeugen der<br />
XML-Datei setzt die Manifest-Datei voraus. Kommt es zu einer Änderung an der<br />
Manifest-Datei, beispielsweise bei einer Simulation mit einem Parameterdurchlauf,<br />
muss eine neue XML-Datei erzeugt werden. Zusammenfassend bedeutet dies, dass<br />
FlashGen nach dem Erzeugen, sowie nach jeder Änderung der Manifest-Datei<br />
ausgeführt werden muss.<br />
5.1.2 Bereitstellen der Simulationsdaten<br />
Ist die Simulation abgeschlossen, kann WebScope auf die Ergebnisse zugreifen.<br />
Wird in WebScope in die erzeugten Diagramme herein- oder aus ihnen<br />
herausgezoomt, werden die Simulationsdaten erneut vom EngineServer abgefragt.<br />
Somit wäre ein erneuter Zugriff auf die Waveform-Dateien nötig. Es müssten die<br />
Werte ebenso aus der Byte-Darstellung in ein Ganzzahlen-Objekt konvertiert<br />
werden. Aus diesem Grund werden die Simulationsergebnisse nur bei der ersten<br />
Anfrage ausgelesen und konvertiert. Anschließend werden die Ergebnisse im<br />
Arbeitsspeicher in einem Zwischenspeicher, folgend Map genannt, hinterlegt. Es wird<br />
dadurch ein Geschwindigkeitsvorteil erreicht, da zum Einen die Ergebnisse nicht<br />
erneut konvertiert werden müssen, und zum Anderen die Zugriffzeiten auf den<br />
Arbeitsspeicher, im Vergleich zu Festplatten, deutlich niedriger sind.<br />
Für die Darstellung der Simulationsergebnisse zur Laufzeit ergibt sich hier das<br />
Problem, dass neue Ergebnisse nicht angezeigt werden, da diese zwar in die<br />
Waveform-Dateien geschrieben werden, der EngineServer für die Darstellung jedoch<br />
die Werte aus dem Arbeitsspeicher verwendet. Die Ergebnisse müssen demzufolge<br />
sowohl in den Waveform-Dateien auf der Festplatte, als auch in der Map im<br />
Arbeitsspeicher gespeichert werden.<br />
Seite 34
Der EngineServer<br />
5.2 Implementierung<br />
5.2.1 Anpassen der QucsLiveEngine<br />
In Kapitel 4 wurde bereits auf die Entwicklung der QucsLiveEngine eingegangen. In<br />
dieser werden die Simulationsdaten von der Anwendung Qucs entgegen genommen<br />
und in die Waveform-Dateien geschrieben. Nach einem Zugriff auf die<br />
Simulationsdaten über den EngineServer müssen die Simulationsergebnisse<br />
ebenfalls in den, in Kapitel 5.1.2 beschriebenen, Zwischenspeicher geschrieben<br />
werden. Dazu ist eine Kommunikation zwischen EngineServer und der<br />
QucsLiveEngine erforderlich. In der bereits bestehenden generischen Klasse, von<br />
der alle Engines erben, sind bereits Rückruffunktionen definiert, die der Engine eine<br />
Kommunikation zum EngineServer ermöglicht. Um die Simulationsdaten zur<br />
Verfügung zu stellen, wird hier eine zusätzliche Rückruffunktion definiert. In Listing 8<br />
sind die Änderungen an der generischen Klasse aufgeführt.<br />
/* ALRESULT - AddLiveResult */<br />
PROC_ALRESULT_CALLBACK g_pfnALResultCallBack = NULL;<br />
/* ... */<br />
extern "C" __declspec( dllexport )<br />
void SetALResultCallBack(PROC_ALRESULT_CALLBACK pfnCallBack ) {<br />
g_pfnALResultCallBack = pfnCallBack;<br />
}<br />
/* ... */<br />
bool CGenericEngine::CallALResultCallback(const char* WFFilename,<br />
float XData, float YData, float XDataImag,<br />
float YDataImag, bool IsComplex) {<br />
if ( g_pfnALResultCallBack == NULL)<br />
return false;<br />
g_pfnALResultCallBack(WFFilename, XData, YData, XDataImag, YDataImag,<br />
IsComplex);<br />
return true;<br />
}<br />
Listing 8 Änderungen an der generischen Klasse<br />
Die Funktion SetALResultCallBack() wird als extern definiert, damit der<br />
EngineServer diese nutzen kann. Über diese Funktion registriert sich der<br />
EngineServer für den Rückruf. Damit der EngineServer die Daten verarbeiten kann,<br />
muss hier ein Zeiger auf eine Funktion übergeben werden. Die Funktion<br />
CallALResultCallback() wird von der QucsLiveEngine benötigt. Wurde ein neues<br />
Zwischenergebnis berechnet, muss diese Funktion aufgerufen werden. In dieser wird<br />
geprüft, ob eine Rückruffunktion definiert wurde. Ist dies der Fall wird diese<br />
ausgeführt. Da die Definition der Funktion in einem anderen Programm erfolgt, ist<br />
diese dem Compiler hier nicht bekannt. Es muss daher auf die korrekte<br />
Parameterliste geachtet werden. Die Parameter bestehen hier aus dem Dateinamen<br />
der zugehörigen Waveform-Datei, die aktuellen X- und Y-Werte, inklusive reeller und<br />
imaginärer Anteil, sowie die Angabe, ob es sich um komplexe Werte handelt.<br />
Seite 35
Der EngineServer<br />
Um den Rückruf in der QucsLiveEngine auszuführen, muss in dieser noch der<br />
entsprechende Aufruf eingebunden werden. In Listing 9 ist der entsprechende<br />
Funktionsaufruf aufgeführt.<br />
CallALResultCallback(waveFormFilename,<br />
curIndep.getValue()->getReal(), numb->getReal(),<br />
curIndep.getValue()->getImag(), numb->getImag(),<br />
numb->isComplex()<br />
);<br />
Listing 9 Aufruf der Rückruffunktion in der QucsLiveEngine<br />
Der Aufruf der Funktion erfolgt dabei an der Stelle im Programmablauf, an der die<br />
Ergebnisse in die Waveform-Datei geschrieben werden.<br />
5.2.2 Speichern der Daten im Zwischenspeicher des EngineServers<br />
Nachdem die Daten von der QucsLiveEngine bereitgestellt werden muss der<br />
EngineServer modifiziert werden, um die Simulationsdaten gegebenenfalls in den<br />
Zwischenspeicher zu schreiben. In Listing 10 sind die Änderungen an der<br />
EngineServer-Klasse aufgeführt.<br />
// EngineServerWSClass.h:<br />
public delegate void ALResultCallBackDel(const char* WFFilename,<br />
float XData, float YData, float XDataImag,<br />
float YDataImag, bool IsComplex);<br />
namespace EngineServerWS {<br />
/* ... */<br />
private:<br />
static ALResultCallBackDel ^c_ALResultDelegate;<br />
void ALResultCallBack(const char* WFFilename, float XData,<br />
float YData, float XDataImag, float YDataImag, bool IsComplex);<br />
/* ... */<br />
}<br />
// EngineServerWSClass.cpp:<br />
[DllImportAttribute("GenericEngine.dll", CharSet=CharSet::Ansi)]<br />
extern "C" void SetALResultCallBack(ALResultCallBackDel ^d);<br />
Int32 EngineServerWSClass::Start() {<br />
/* ... */<br />
c_ALResultDelegate = gcnew ALResultCallBackDel(this,<br />
&EngineServerWSClass::ALResultCallBack);<br />
SetALResultCallBack( c_ALResultDelegate );<br />
/* ... */<br />
}<br />
Int32 EngineServerWSClass::StopServer() {<br />
/* ... */<br />
c_ALResultDelegate = nullptr;<br />
/* ... */<br />
}<br />
void EngineServerWSClass::ALResultCallBack(const char* WFFilename,<br />
float XData, float YData, float XDataImag,<br />
float YDataImag, bool IsComplex) {<br />
/* ... */<br />
}<br />
Listing 10 Änderungen an der EngineServer-Klasse<br />
Seite 36
Der EngineServer<br />
In der Header-Datei der Klasse befinden sich neue Deklarationen. Es wird ein<br />
Methodenzeiger (engl.: delegate) deklariert. Anschließend wird eine globale Variable<br />
von dem neuen Typ, sowie ein Funktionskopf deklariert.<br />
In der Quelltext-Datei muss zunächst der Verweis auf die Rückruffunktion der<br />
generischen Klasse angegeben werden. Wird der EngineServer gestartet wird ein<br />
neuer Methodenzeiger angelegt, der auf die Funktion ALResultCallBack() verweist.<br />
Anschließend wird diese der Rückruffunktion der generischen Klasse zugewiesen.<br />
Wird eine von der generischen Klasse abgeleitete Engine gestartet, kann in dieser<br />
nun die Rückruffunktion aufgerufen werden. Anschließend wird die<br />
ALResultCallBack()-Funktion im EngineServer aufgerufen. Somit ist die<br />
Kommunikation zwischen dem EngineServer und der Engine hergestellt. Wird der<br />
Server gestoppt, wird der Methodenzeiger zurückgesetzt.<br />
Der Zwischenspeicher wird bei der ersten Anfrage der Simulationsergebnisse<br />
angelegt. Die Daten werden in der Struktur WFData hinterlegt, die aus vier Feldern<br />
vom Typ float besteht. In der Funktion ALResultCallBack() müssen die<br />
Simulationsdaten in den Zwischenspeicher geschrieben werden. In Anhang 10<br />
befindet sich die Struktur- sowie die Funktionsdefinition. Bei dem Erstellen des<br />
Zwischenspeichers werden die Felder, in denen die Werte gespeichert werden,<br />
angelegt. Dies bedeutet, dass die Größe der Felder auf die Anzahl, der zu diesem<br />
Zeitpunkt, vorhandenen Ergebnisse gesetzt wird. Durch die Echtzeitkommunikation<br />
werden jedoch neue Werte empfangen, die in den Zwischenspeicher geschrieben<br />
werden müssen. Da ein Feld nicht beliebig erweitert werden kann, wurde eine neue<br />
Struktur WFLiveData entworfen. Diese ähnelt der WFData, jedoch werden hier die<br />
Daten in Listen gespeichert. Diese können beliebig erweitert werden. Das Erstellen<br />
des Zwischenspeichers musste an die neue Struktur angepasst werden. Die<br />
Änderungen zum Lesen der Daten wird in Kapitel 6 näher erläutert. Das Speichern<br />
der Simulationsergebnisse in den Zwischenspeicher ist in Anhang 10 aufgeführt.<br />
Zunächst muss in allen vorhandenen Simulationen die aktuell benötigte gesucht<br />
werden. Diese hat den Typ ESJobObject. In dieser ist eine Hashtable aller<br />
vorhandenen Waveform-Daten enthalten. In den entsprechenden Eintrag der<br />
Hashtable werden schließlich die neuen Simulationsergebnisse hinzugefügt.<br />
5.2.3 Starten des Postprocessing<br />
Wie in Kapitel 5.1.1 erläutert, muss nach einer Änderung an der Manifest-Datei das<br />
Postprocessing gestartet werden. Im bisherigen Ablauf wird das Postprocessing<br />
einmalig nach Abschluss der Simulations ausgeführt. Dazu ist in der generischen<br />
Klasse eine Rückruffunktion definiert. Ist die Simulation beendet wird diese von der<br />
Engine aufgerufen. Anschließend wird auf dem EngineServer das Postprocessing<br />
ausgeführt. In der QucsLiveEngine muss nun der Funktionsaufruf lediglich nach dem<br />
Erstellen der Manifest-Datei erfolgen.<br />
Seite 37
Anzeige der Simulationsergebnisse in WebScope<br />
6 Anzeige der Simulationsergebnisse in WebScope<br />
6.1 Vorbetrachtung<br />
6.1.1 Anfrage der Simulationsdaten<br />
Die Ausgabe der Simulationsergebnisse in WebScope muss in zwei Bereiche<br />
unterteilt werden. Zum einen müssen die Ergebnisse zur Laufzeit dargestellt werden.<br />
Darauf wird im folgenden Kapitel näher eingegangen. Zum anderen können beim<br />
Laden von WebScope bereits Simulationsergebnisse vorliegen. Diese vorhandenen<br />
Ergebnisse müssen separat geladen werden. Eine direkte Darstellung der<br />
Simulationsdaten im Webbrowser ist nicht möglich. Stattdessen muss die Größe des<br />
Ausgabebereiches an den Webserver übergeben werden. Dieser leitet, über einen<br />
Proxy, die Werte an den EngineServer weiter. Hier werden anhand der<br />
Simulationsergebnisse die darzustellenden Punkte berechnet. Der Grund für diese<br />
Darstellung liegt in der Anzahl der Simulationsergebnisse. Würde die Berechnung<br />
der Darstellung im Webbrowser erfolgen müssen alle Simulationsergebnisse an<br />
diesen übertragen werden. Bei Simulationen mit einer großen Anzahl an<br />
Ergebnissen würde dies zu einem erheblichen Datentransfer führen. Durch die<br />
Berechnung, der auszugebenden Punkte, im EngineServer bleibt die Größe der zum<br />
Browser zu übertragenden Daten konstant. Dies bedeutet jedoch, dass beim hereinund<br />
herauszoomen in Webscope die Daten erneut vom Server geladen werden<br />
müssen. Ebenfalls führt ein verschieben des angezeigten Bereiches zu einem<br />
Neuladen der Daten. Für die Anfrage muss daher neben der Größe des<br />
Ausgabebereiches auch die Skalierung der X- und Y-Achsen übergeben werden.<br />
In Kapitel 5 wurde bereits erläutert, dass die Simulationsergebnisse auf dem<br />
EngineServer nach der ersten Anfrage zwischengespeichert werden. Um die<br />
Simulationsergebnisse zur Laufzeit in den Zwischenspeicher schreiben zu können,<br />
musste dieser vom Aufbau her modifiziert werden. Werden über den Webserver<br />
neue Daten angefordert, wird auf den Zwischenspeicher lesend zugegriffen.<br />
Dementsprechend muss die Funktion zum Auslesen der Daten an den neuen Aufbau<br />
angepasst werden.<br />
6.1.2 Ausgabe der Simulationsdaten zu Laufzeit<br />
6.1.2.1 Allgemein<br />
Die Anzeige der Simulationsergebnisse erfolgt bei dem Endnutzer im Webbrowser<br />
über ein HTML-<strong>Dokument</strong> (Hypertext Markup Language). Bei HTML handelt es sich<br />
um eine textbasierte Auszeichnungssprache. Sie dient der Strukturierung von<br />
Bildern, Text und anderen Elementen in einem <strong>Dokument</strong>. HTML trägt aktuell die<br />
Versionsnummer 4.01, wobei an HTML5 entwickelt wird, und dies bereits von vielen<br />
Seite 38
Anzeige der Simulationsergebnisse in WebScope<br />
Layout-Engines unterstütz wird. 15 HTML bietet keine Möglichkeit zur Darstellung von<br />
dynamischen Elementen. Um dies zu erreichen müssen zusätzliche Techniken<br />
verwendet werden. Hier hat sich die Skriptsprache JavaScript etabliert. Sie wird von<br />
allen gängigen Webbrowser unterstützt. Es existieren viele JavaScript-Bibliotheken<br />
die das Erstellen von dynamischen Inhalten auf Webseiten vereinfachen. Als<br />
Konzept für die asynchrone Datenübertragung hat sich Ajax (Asynchronous<br />
JavaScript and XML) in der Webentwicklung durchgesetzt 16 und soll auch hier für die<br />
Übertragung der Simulationsergebnisse genutzt werden. Zur Darstellung von<br />
Echtzeitdaten auf einer Webseite haben sich unterschiedliche Methoden entwickelt.<br />
Im Folgenden wird auf einige dieser Methoden näher eingegangen.<br />
6.1.2.2 Erneutes Abfragen aller Ergebnisse (Polling)<br />
Eine Möglichkeit, Daten von einem Webserver abzufragen, besteht darin, in einem<br />
bestimmten Zeitabstand sämtliche Daten erneut vom Server abzufragen. In der<br />
Antwort befinden sich die alten, sowie die neuen Daten. Dieses Verfahren wird<br />
Polling genannt. Der Vorteil besteht in einer einfachen Implementierung. Jedoch<br />
kommt es so zu einem hohen Datenverkehr, da die alten Daten jedes Mal neu<br />
übertragen werden. Um zu verhindern, dass der EngineServer stets die neuen<br />
Ausgabedaten berechnet, kann hier ein Puffer eingerichtet werden, in dem die<br />
Ausgabedaten hinterlegt werden. Bei Eintreffen neuer Simulationsergebnisse<br />
müssten diese sowohl in den Zwischenspeicher als auch in den Puffer übernommen<br />
werden.<br />
6.1.2.3 Abfragen nach neuen Ergebnissen (Long Polling)<br />
Eine Abwandlung des in Kapitel 6.1.2.2 beschriebenen Verfahrens stellt das<br />
sogenannte Long Polling dar. Hier werden nicht alle Daten vom Webserver<br />
angefragt. Stattdessen wird eine Anfrage gesendet, ob neue Daten vorliegen. Liegen<br />
diese nicht vor, wird die Anfrage zunächst nicht beantwrotet. Gehen neue Ergebnisse<br />
ein, kann die Anfrage bestätigt werden. Da lediglich neue Daten übertragen werden,<br />
wird hier der Datenverkehr im Vergleich zum herkömmlichen Polling stark reduziert.<br />
Hier ist jedoch ein höherer Implementierungsaufwand nötig. So muss eine<br />
Differenzierung erfolgen, welche Daten auf dem Server als neu, und welche als alt<br />
eingestuft werden. Des Weiteren geschieht das Abfragen der Ergebnisse über eine<br />
HTTP-Anfrage. Erhält diese nach einer gewissen Zeitüberschreitung (engl. timeout)<br />
keine Antwort wird eine Ausnahme geworfen, die entsprechend behandelt werden<br />
muss.<br />
15 vgl. online: Hypertext Markup Language auf Wikipedia.org (22.08.2013)<br />
16 vgl. online: Ajax (Programmierung) auf Wikipedia.org (22.08.2013)<br />
Seite 39
Anzeige der Simulationsergebnisse in WebScope<br />
6.1.2.4 Websocket-Protokoll<br />
Bei dem Websocket-Protokoll handelt es sich um ein Netzwerkprotokoll, das auf TCP<br />
basiert. Mit diesem kann eine bidirektionale Verbindung zwischen einem Webserver<br />
und einer Webanwendung hergestellt werden. Durch das Herstellen einer<br />
Verbindung kann der Server aktiv Daten an den Client schicken, und muss nicht, wie<br />
bei einer HTTP-Verbindung, auf eine Anfrage des Clients warten. Ebenfalls entfallen<br />
bei WebSockets zusätzliche Daten, die durch den HTTP-Header verursacht<br />
werden. 17 Der Nachteil dieser Methode besteht jedoch darin, dass sowohl Browser<br />
als auch Webserver das WebSocket-Protokoll unterstützen müssen. Die Transim<br />
Technology Corporation verwendet als WebServer die Internet Information Services<br />
(IIS) von Microsoft in den Versionen bis 7.5. Das WebSocket-Protokoll wird jedoch<br />
erst ab der IIS-Version 8.0 unterstützt. Ältere Versionen des IIS können durch die<br />
Integration von beispielsweise Node.js erweitert werden, wodurch das WebSocket-<br />
Protokoll genutzt werden kann. Das WebSocket-Protokoll wird ebenfalls nur von<br />
neuen Webbrowsern unterstützt. Da die Webanwendungen der Transim Technology<br />
Corporation jedoch auch auf älteren Webbrowser ausgeführt werden soll, müssen<br />
hier zusätzliche JavaScript-Bibliotheken eingebunden werden, damit das Protokoll<br />
verwendet werden kann.<br />
6.1.2.5 Senden der Ergebnisse vom Webserver<br />
Neben den bisher genannten, besteht die Möglichkeit, Daten vom Webserver direkt<br />
an den Webbrowser zu senden. Diese Technik ist unter dem Begriff „Server-Sent<br />
Events“ bekannt. Im Gegensatz zu WebSockets wird keine bidirektionale Verbindung<br />
hergestellt. Nachdem eine Verbindung vom Client hergestellt wurde, können<br />
anschließend vom Server Daten an den Client übertragen werden. Diese Methode<br />
wird ebenfalls nicht vom IIS unterstütz, jedoch existierten Bibliotheken, die die<br />
Verwendung von „Server-Sent Events“ im IIS ermöglichen. Größere Probleme gibt es<br />
hier bei den Webbrowsern. Der Internet Explorer von Microsoft unterstützt bis zu der<br />
aktuellen Version 10 keine „Server-Sent Events“. Auch liegen derzeit keine<br />
JavaScript-Bibliotheken vor, über die die Funktion in den Browser eingebunden<br />
werden kann. 18<br />
6.2 Implementierung<br />
6.2.1 Auslesen des Zwischenspeichers im EngineServer<br />
In Kapitel 5.2.2 wurde beschrieben, wie die Simulationsergebnisse zur Laufzeit in<br />
den Zwischenspeicher geschrieben werden. Sendet WebScope eine Anfrage an den<br />
EngineServer, müssen die Ergebnisse aus dem Zwischenspeicher gelesen werden.<br />
In Anhang 11 befindet sich der Quelltext zum Auslesen der Simulationsergebnisse.<br />
17 vgl. online: WebSocket auf Wikipedia.org (25.08.2013)<br />
18 vgl. online: Server-Sent Events auf Wikipedia.org (25.08.2013)<br />
Seite 40
Anzeige der Simulationsergebnisse in WebScope<br />
Hier wird davon ausgegangen, dass bereits das entsprechende Objekt für die<br />
Simulation (ESJobObject) bereits geladen wurde. Zu Beginn werden die Waveform-<br />
Daten geladen. Für die erste Anfrage sind diese Daten leer. In diesem Fall werden<br />
die Listen für die Werte angelegt. Anschließend werden die Daten aus dem bisher<br />
verwendeten Zwischenspeicher (vgl. wfdat) in den neuen kopiert. Da es sich um die<br />
erste Anfrage handelt, befinden sich in diesem alle bisherigen Ergebnisse.<br />
Anschließend werden die Daten aus den Listen in die Ausgabevariablen<br />
geschrieben. Die Ausgabedaten müssen dabei als Feld vorliegen. Hier bietet jedoch<br />
das .NET-Framework die ToArray()-Methode, mit der eine Liste in ein Feld<br />
konvertiert werden kann.<br />
6.2.2 Ausgabe der Simulationsdaten<br />
In Kapitel 6.1.2 wurden mehrere Möglichkeiten aufgezeigt, wie die<br />
Simulationsegergebnisse im Webbrowser zur Laufzeit dargestellt werden können. Da<br />
die Transim Technology Corporation eine Kompatibilität für ältere Webbrowser<br />
anbietet entfällt als Methode die Verwendung der „Server-Sent Events“. Die<br />
Verwendung von Polling würde zu einem erhöhten Datenverkehr kommen und sollte<br />
daher ebenfalls nicht verwendet werden. Somit bleiben als Alternative das Long-<br />
Polling, sowie die Verwendung des WebSocket-Protokolls übrig. Beide Varianten<br />
haben ihre Vor- und Nachteile.<br />
Für die Verwendung von WebSockets müssen Änderungen an den Webservern der<br />
Transim Technology Corporation vorgenommen werden. Ebenfalls muss eine<br />
zusätzliche JavaScript-Bibliothek in WebScope eingebunden werden, um ältere<br />
Webbrowser zu unterstützen. Der Vorteil liegt jedoch in der bidirektionalen<br />
Verbindung zwischen Webbrowser und Webserver. Es ist gegenüber Long-Polling<br />
schneller, und erzeugt einen niedrigeren Datenverkehr. Ebenfalls ist es möglich,<br />
dass mehrere Client-Verbindungen zu dem Webserver hergestellt werden. So kann<br />
die Ausgabe der Simulationsergebnisse auch von anderen Modulen, wie<br />
beispielsweise dem WebSchematic zur Laufzeit genutzt werden. Long Polling<br />
dagegen kann ohne Änderungen am Webserver implementiert werden. Da die<br />
Anfragen an den Webserver über HTTP versandt werden, entsteht durch den HTTP-<br />
Header ein höherer Datenverkehr. An dieser Stelle der Entwicklung müssen weitere<br />
firmeninterne Besprechungen getroffen werden, welche Methode verwendet werden<br />
soll. Da es zum Zeitpunkt der Fertigstellung dieser Bachelor-Thesis noch keine<br />
Entscheidung vorlag ist die finale Implementierung hier nicht aufgeführt.<br />
Um dennoch die Ausgabe der Simulationsdaten testen zu können, wurde zunächst<br />
die Polling-Methode implementiert. Hierzu wurde eine neue Schaltfläche in<br />
WebScope implementiert, über die die Aktualisierung angehalten, oder gestartet<br />
werden kann. In Listing 11 ist der JavaScript-Quelltext aufgeführt, über den das<br />
Polling realisiert wird.<br />
Seite 41
Anzeige der Simulationsergebnisse in WebScope<br />
function liveRef(idx) {<br />
checkBounds(idx);<br />
SimProxy.Graph(createJSON(idx), responseLiveGraph, null, 0);<br />
}<br />
function responseLiveGraph(data) {<br />
// save the results<br />
/* ... */<br />
if (liveRefresh) {<br />
setTimeout(function () { liveRef(0); }, 500);<br />
}<br />
}<br />
Listing 11 Polling in WebScope<br />
Die Funktion liveRef() startet über den Aufruf SimProxy.Graph() die Ajax-Anfrage<br />
an den Webserver. Über den Proxy wird die Anfrage an den EngineServer<br />
weitergeleitet. Dieser berechnet alle darzustellenden Punkte. Wurde in WebScope<br />
keine Skalierung angegeben, werden als Minima- und Maxima-Werte die aus der<br />
erzeugten *_JS_VIEWER.xml-Datei genommen. Wurden die Daten generiert werden<br />
diese an den Webbrowser zurück geschickt. Hier wird die Funktion<br />
responseLiveGraph() aufgerufen. In dieser Werden die übertragenen Daten in dem<br />
Ausgabebereich der Webseite ausgegeben. Ist der Knopf für die Echtzeitausgabe<br />
aktiviert, wird 500 Millisekunden gewartet, bevor eine neue Anfrage an den Server<br />
geschickt wird. Diese Methode ist in der Praxis nicht zu verwenden, da ein sehr<br />
hoher Datenverkehr entsteht. Er wurde hier lediglich implementiert, um die Ausgabe<br />
der Simulationsergebnisse zu testen.<br />
Seite 42
Bewertung und Ausblick<br />
7 Bewertung und Ausblick<br />
Im Rahmen dieser Arbeit wurden mehrere Programme und Unterprogramme<br />
modifiziert, um eine Ausgabe von Simulationsergebnissen zur Laufzeit der<br />
Simulation zu ermöglichen. Als Übertragung der Daten von dem Simulator zu der<br />
Engine wurde sich für die Verwendung von Datenströmen entschieden. Im Verlauf<br />
der Arbeit hat sich gezeigt, dass TCP gegenüber UDP als Protokoll besser geeignet<br />
ist. Werden mehrere Simulationen auf einem Simulationsserver gleichzeitig<br />
ausgeführt, muss eine Zuweisung der Simulationsdaten zu der ursprünglichen<br />
Simulation erfolgen. Diese Zuordnung wird nun erreicht, indem für jede Simulation<br />
ein eigener TCP-Server erstellt wird, an den die Simulationsanwendung die<br />
Ergebnisse sendet. Für die Kommunikation zwischen Anwendung und Engine<br />
musste ein Format entworfen werden, indem die Ergebnisse abgebildet werden. Hier<br />
wurde XML-Format entworfen, das einen minimalen Overhead erzeugt und somit die<br />
Netzwerkauslastung verringert. Die Implementierung eines TCP-Servers in der<br />
Engine hat ergeben, dass es zu einem Blockieren der Engine kommt, wenn keine<br />
Daten empfangen werden. Dies wurde umgangen, indem das Empfangen der Daten<br />
in einen zusätzlichen Thread ausgelagert wurde. Um die empfangenen Daten in dem<br />
Webbrowser zugänglich zu machen, musste die bestehende Lösung zur Ausgabe<br />
der Daten modifiziert werden. Um einen Geschwindigkeitsvorteil zu erreichen,<br />
werden die Ausgabedaten in einen Zwischenspeicher geschrieben. Der<br />
EngineServer wurde so modifiziert, dass neue Simulationsergebnisse in diesen<br />
Zwischenspeicher eingetragen werden. Um die Ergebnisse aus der Engine an den<br />
EngineServer weiterzuleiten wurden Rückruffunktionen verwendet. Die eigentliche<br />
Ausgabe der Simulationsergebnisse in WebScope greift auf die Daten in dem<br />
Zwischenspeicher des EngineServers zu. Um die Simulationsergebnisse WebScope<br />
zur Laufzeit zur Verfügung zu stellen, wurden mehrere Methoden vorgestellt. Dabei<br />
hat sich die Verwendung von Long-Polling oder die Nutzung des WebSocket-<br />
Protokolls als günstig erwiesen. Die Entscheidung für eine der Methoden muss in<br />
weiteren firmeninternen Besprechungen erfolgen. Aus diesem Grund ist die<br />
Implementierung aufgrund des zeitlich begrenzten Rahmens in dieser Arbeit nicht<br />
möglich gewesen.<br />
Seite 43
Quellenverzeichnis<br />
Quellenverzeichnis<br />
Ajax (Programmierung) auf Wikipedia.org<br />
http://de.wikipedia.org/wiki/Ajax_(Programmierung) (22.08.2013)<br />
c-worker.ch Winsock Tutorial<br />
http://www.c-worker.ch/tuts.php (08.07.2013)<br />
Document Object Model auf Wikipedia.org<br />
http://de.wikipedia.org/wiki/Document_Object_Model (09.08.2013)<br />
Hypertext Markup Language auf Wikipedia.org<br />
http://de.wikipedia.org/wiki/Hypertext_Markup_Language (22.08.2013)<br />
IEEE 802.3-2012 – Section One, 2012<br />
Interprozesskommunikation auf Wikipedia.org<br />
http://de.wikipedia.org/wiki/Interprozesskommunikation (15.07.2013)<br />
MEYER, Martin: Studienarbeit – Integration des „Quite Universal Circuit Simulator“ in<br />
das Engine Framework der Transim Technology Corporation. <strong>Glauchau</strong>, 2013<br />
Qucs auf Sourceforge.net:<br />
http://sourceforge.net/p/qucs/git/ci/master/tree/ (12.07.2013)<br />
RFC 793 – Transmission Control Protocol<br />
http://tools.ietf.org/html/rfc793 (13.08.2013)<br />
Server-sent events auf Wikipedia.org<br />
http://en.wikipedia.org/wiki/Server-sent_events (25.08.2013)<br />
Simple API for XML auf Wikipedia.org<br />
http://de.wikipedia.org/wiki/Simple_API_for_XML (09.08.2013)<br />
Seite 44
Quellenverzeichnis<br />
The zlib/libpng License (Zlib) auf opensource.org<br />
http://opensource.org/licenses/Zlib (09.08.2013)<br />
Tiefpass auf Wikipedia.org<br />
http://de.wikipedia.org/wiki/Tiefpass (07.08.2013)<br />
WebSocket auf Wikipedia.org<br />
http://de.wikipedia.org/wiki/WebSocket (25.08.2013)<br />
Seite 45
Anhangverzeichnis<br />
Anhangverzeichnis<br />
Anhang 1<br />
Anhang 2<br />
Anhang 3<br />
Anhang 4<br />
Anhang 5<br />
Anhang 6<br />
Anhang 7<br />
Anhang 8<br />
Anhang 9<br />
Anhang 10<br />
Anhang 11<br />
Qucs-Ausgabedatei<br />
Größe der Ergebnisdaten bei XML und JSON<br />
Node.js Quelltext für den TCP- und UDP-Testserver<br />
Änderungen am Programmeinstieg „ucs.cpp“<br />
Die Header- und Quelltext-Dateien der Qucs-Netzwerkklasse<br />
Erweiterung der nasolver-Klasse<br />
Speichern der Ergebnisse und Abhängigkeiten in Qucs<br />
Typen<br />
Beispiel für eine Kurvenschar<br />
Änderungen an der EngineServer-Klasse<br />
Auslesen der Simulationsergebnisse aus dem Zwischenspeicher<br />
Seite 46
Qucs-Ausgabedatei Anhang 1<br />
Anhang 1 Qucs-Ausgabedatei<br />
<br />
<br />
+1.00000000000e+000<br />
+1.00000000000e+005<br />
+1.00000000000e+010<br />
<br />
<br />
+5.00000000000e+000<br />
+5.00000000000e+006<br />
<br />
<br />
+5.00000000000e-010<br />
+1.50000000000e-009<br />
<br />
<br />
-4.93480220054e-017-j3.14159265359e-009<br />
-4.93479002439e-007-j3.14158490204e-004<br />
-1.99991894634e-001-j1.27318794437e-003<br />
-4.93358488727e-011-j3.14081768789e-009<br />
-1.99999918943e-007-j1.27323902871e-010<br />
-2.00000000000e-007-j1.27323954474e-015<br />
-4.44089209850e-016-j9.42477796077e-009<br />
-4.44122335597e-006-j9.42456867305e-004<br />
-1.99999099371e-001-j4.24411270385e-004<br />
-4.43148116314e-010-j9.40389509777e-009<br />
-1.99999990994e-007-j4.24413162466e-011<br />
-2.00000000000e-007-j4.24413181578e-016<br />
<br />
<br />
+1.00000000000e+000-j1.57079632679e-008<br />
+9.99997532605e-001-j1.57079245102e-003<br />
+4.05268309663e-005-j6.36593972186e-003<br />
+9.99753320756e-001-j1.57040884394e-002<br />
+4.05284570314e-007-j6.36619514355e-004<br />
+4.05284734569e-017-j6.36619772368e-009<br />
+1.00000000000e+000-j4.71238898038e-008<br />
+9.99977793883e-001-j4.71228433652e-003<br />
+4.50314343905e-006-j2.12205635192e-003<br />
+9.97784259418e-001-j4.70194754888e-002<br />
+4.50316351465e-008-j2.12206581233e-004<br />
+4.50316371744e-018-j2.12206590789e-009<br />
<br />
<br />
+1.00000000000e+000<br />
+1.00000000000e+000<br />
+1.00000000000e+000<br />
+1.00000000000e+000<br />
+1.00000000000e+000<br />
+1.00000000000e+000<br />
+1.00000000000e+000<br />
+1.00000000000e+000<br />
+1.00000000000e+000<br />
+1.00000000000e+000<br />
+1.00000000000e+000<br />
+1.00000000000e+000<br />
Größe der Ergebnisdaten bei XML und JSON Anhang 2<br />
Anhang 2 Größe der Ergebnisdaten bei XML und JSON<br />
( ) ( ( ) ( )) ( ( ) ( ))<br />
d<br />
XML<br />
in Byte<br />
i<br />
1 2 3 4 … 23 24 25 26 27 28<br />
1 147 198 249 300 … 1269 1320 1371 1422 1473 1524<br />
2 198 249 300 351 … 1320 1371 1422 1473 1524 1575<br />
3 249 300 351 402 … 1371 1422 1473 1524 1575 1626<br />
4 300 351 402 453 … 1422 1473 1524 1575 1626 1677<br />
… … … … … … … … … … … …<br />
23 1269 1320 1371 1422 … 2391 2442 2493 2544 2595 2646<br />
24 1320 1371 1422 1473 … 2442 2493 2544 2595 2646 2697<br />
25 1371 1422 1473 1524 … 2493 2544 2595 2646 2697 2748<br />
26 1422 1473 1524 1575 … 2544 2595 2646 2697 2748 2799<br />
27 1473 1524 1575 1626 … 2595 2646 2697 2748 2799 2850<br />
28 1524 1575 1626 1677 … 2646 2697 2748 2799 2850 2901<br />
( ) ( ( ) ( )) ( ( ) ( ))<br />
d<br />
JSON<br />
in Byte<br />
i<br />
1 2 3 4 … 23 24 25 26 27 28<br />
1 145 200 255 310 … 1355 1410 1465 1520 1575 1630<br />
2 200 255 310 365 … 1410 1465 1520 1575 1630 1685<br />
3 255 310 365 420 … 1465 1520 1575 1630 1685 1740<br />
4 310 365 420 475 … 1520 1575 1630 1685 1740 1795<br />
… … … … … … … … … … … …<br />
23 1355 1410 1465 1520 … 2565 2620 2675 2730 2785 2840<br />
24 1410 1465 1520 1575 … 2620 2675 2730 2785 2840 2895<br />
25 1465 1520 1575 1630 … 2675 2730 2785 2840 2895 2950<br />
26 1520 1575 1630 1685 … 2730 2785 2840 2895 2950 3005<br />
27 1575 1630 1685 1740 … 2785 2840 2895 2950 3005 3060<br />
28 1630 1685 1740 1795 … 2840 2895 2950 3005 3060 3115
Node.js Quelltext für den TCP- und UDP-Testserver Anhang 3<br />
Anhang 3 Node.js Quelltext für den TCP- und UDP-Testserver<br />
Der TCP-Server:<br />
var net = require('net');<br />
var host = '127.0.0.1';<br />
var port = 12346;<br />
net.createServer(function(sock) {<br />
sock.name = sock.remoteAddress + ":" + sock.remotePort<br />
console.log(Date.now() + ' | CONNECTED: ' + sock.name);<br />
sock.on('data', function(data) {<br />
console.log(Date.now() + ' | DATA ' + sock.name + ' - ' + data);<br />
});<br />
sock.on('error', function(data) {<br />
console.log(Date.now() + ' | ERROR ' + sock.name + ' - ' + data + '\n');<br />
});<br />
sock.on('end', function() {<br />
console.log(Date.now() + ' | END: ' + sock.name + '\n');<br />
});<br />
}).listen(port, host);<br />
console.log('Server listening on ' + host + ':' + port + '\n');<br />
Der UDP-Server:<br />
var port = 12345;<br />
var host = '127.0.0.1';<br />
var dgram = require('dgram');<br />
var server = dgram.createSocket('udp4');<br />
server.on('listening', function () {<br />
var address = server.address();<br />
console.log('UDP Server listening on ' + address.address + ':' + address.port<br />
+ '\n');<br />
});<br />
server.on('message', function(message, remote) {<br />
console.log(Date.now() + ' | DATA ' + remote.address + ':' + remote.port<br />
+ ' - ' + message);<br />
});<br />
server.bind(port, host);
Änderungen am Programmeinstieg „ucs.cpp“ Anhang 4<br />
Anhang 4<br />
Änderungen am Programmeinstieg „ucs.cpp“<br />
/* ... */<br />
#include "network.h"<br />
/* ... */<br />
int main (int argc, char ** argv) {<br />
}<br />
/* ... */<br />
// check program arguments<br />
for (int i = 1; i < argc; i++) {<br />
/* ... */<br />
if (!strcmp (argv[i], "-d")) {<br />
i++;<br />
msDelay = atoi(argv[i]);<br />
}<br />
else if (!strcmp (argv[i], "-udp")) {<br />
// Look for valid Arguments<br />
if(i+1 >= argc)<br />
{<br />
fprintf (stdout,<br />
"Usage: %s [OPTION]...\n\n"<br />
" -udp IP:PORT using UDP to send the simulation results "<br />
"to a server\n", argv[0]);<br />
return 1;<br />
}<br />
i++;<br />
networkAddr = (char *)malloc(strlen(argv[i])+1);<br />
strcpy(networkAddr, argv[i]);<br />
networkUse = true;<br />
networkProtocol = network::UDP;<br />
}<br />
else if (!strcmp (argv[i], "-tcp")) {<br />
// Look for valid Arguments<br />
if(i+1 >= argc)<br />
{<br />
fprintf (stdout,<br />
"Usage: %s [OPTION]...\n\n"<br />
" -tcp IP:PORT using TCP to send the simulation results"<br />
" to a server\n", argv[0]);<br />
return 1;<br />
}<br />
i++;<br />
networkAddr = (char *)malloc(strlen(argv[i])+1);<br />
strcpy(networkAddr, argv[i]);<br />
networkUse = true;<br />
networkProtocol = network::TCP;<br />
}<br />
/* ... */<br />
}<br />
/* ... */
Die Header- und Quelltext-Dateien der Qucs-Netzwerkklasse Anhang 5<br />
Anhang 5<br />
Die Header-Datei:<br />
#ifndef __NETWORK_H__<br />
#define __NETWORK_H__<br />
Die Header- und Quelltext-Dateien der Qucs-Netzwerkklasse<br />
#include <br />
#pragma comment(lib,"ws2_32.lib")<br />
class network<br />
{<br />
public:<br />
// Enumeration Declarations<br />
enum Protocol {UDP, TCP};<br />
// Consturctor & Destructor Declarations<br />
network(char const* str, network::Protocol protocol);<br />
network(char const* ip, int port, network::Protocol protocol);<br />
~network(void);<br />
// static member function Declarations<br />
static int getAddr(char const* str, char* ip, int* port);<br />
// member function Declarations<br />
int netStartSocket(void);<br />
int netConnect(void);<br />
int netSend(char* str);<br />
int netClose(void);<br />
private:<br />
// private member function Declarations<br />
int startWinsock(void);<br />
// private member variables Declaration<br />
Protocol m_protocol;<br />
char m_strIP[15];<br />
int m_iPort;<br />
long m_lRC;<br />
SOCKET m_s;<br />
};<br />
// externalize global variable<br />
extern bool networkUse;<br />
extern char * networkAddr;<br />
extern network::Protocol networkProtocol;<br />
extern int msDelay;<br />
#endif /* __NETWORK_H__ */
Die Header- und Quelltext-Dateien der Qucs-Netzwerkklasse Anhang 5<br />
Die Quelltext-Datei:<br />
#include "network.h"<br />
// Global variables.<br />
bool networkUse = false;<br />
char * networkAddr = NULL;<br />
network::Protocol networkProtocol = network::UDP;<br />
int msDelay = 0;<br />
network::network(void)<br />
{<br />
strcpy(m_strIP, "127.0.0.1");<br />
m_iPort = 12345;<br />
m_protocol = UDP;<br />
}<br />
network::network(char const* str, network::Protocol protocol)<br />
{<br />
char ip[15];<br />
int port;<br />
if(getAddr(str, ip, &port) == 0)<br />
{<br />
strcpy(m_strIP, ip);<br />
m_iPort = port;<br />
m_protocol = protocol;<br />
}<br />
}<br />
network::network(char const* ip, int port, network::Protocol protocol)<br />
{<br />
strcpy(m_strIP, ip);<br />
m_iPort = port;<br />
m_protocol = protocol;<br />
}<br />
network::~network(void)<br />
{<br />
}<br />
int network::startWinsock()<br />
{<br />
WSADATA wsa;<br />
return WSAStartup(MAKEWORD(2,0),&wsa);<br />
}<br />
/* Page 1 of 3 */
Die Header- und Quelltext-Dateien der Qucs-Netzwerkklasse Anhang 5<br />
int network::getAddr(char const* str, char* ip, int* port)<br />
{<br />
// copy str to temporary variable<br />
char* tmp = (char*)malloc(strlen(str)+1);<br />
strcpy(tmp, str);<br />
// split the string at ':'<br />
int cnt = 0;<br />
char* pch = strtok(tmp, ":");<br />
while (pch != NULL)<br />
{<br />
if(cnt == 0)<br />
strcpy(ip, pch);<br />
if(cnt == 1)<br />
*port = atoi(pch);<br />
cnt++;<br />
pch = strtok(NULL, ":");<br />
} free(tmp);<br />
if(cnt != 2)<br />
return 1;<br />
}<br />
return 0;<br />
int network::netStartSocket(void)<br />
{<br />
// start WinSock<br />
m_lRC = startWinsock();<br />
if(m_lRC != 0)<br />
return m_lRC;<br />
// create socket<br />
if(m_protocol == UDP)<br />
m_s = socket(AF_INET, SOCK_DGRAM, 0);<br />
else if(m_protocol == TCP)<br />
m_s = socket(AF_INET, SOCK_STREAM, 0);<br />
else<br />
return -1;<br />
if(m_s == INVALID_SOCKET)<br />
return WSAGetLastError();<br />
}<br />
return 0;<br />
/* Page 2 of 3 */
Die Header- und Quelltext-Dateien der Qucs-Netzwerkklasse Anhang 5<br />
int network::netConnect(void)<br />
{<br />
// connect to server<br />
SOCKADDR_IN addr;<br />
memset(&addr, 0, sizeof(SOCKADDR_IN));<br />
addr.sin_family = AF_INET;<br />
addr.sin_port = htons(m_iPort);<br />
addr.sin_addr.s_addr = inet_addr(m_strIP);<br />
m_lRC = connect(m_s, (SOCKADDR*)&addr, sizeof(SOCKADDR_IN));<br />
if(m_lRC == SOCKET_ERROR)<br />
return WSAGetLastError();<br />
}<br />
return 0;<br />
int network::netSend(char* str)<br />
{<br />
char * buf = (char *)malloc(strlen(str)+1);<br />
strcpy(buf, str);<br />
m_lRC = send(m_s, buf, strlen(buf), 0);<br />
if(m_lRC == SOCKET_ERROR)<br />
return WSAGetLastError();<br />
free(buf);<br />
return 0;<br />
}<br />
int network::netClose()<br />
{<br />
closesocket(m_s);<br />
WSACleanup();<br />
return 0;<br />
}<br />
/* Page 3 of 3 */
Erweiterung der nasolver-Klasse Anhang 6<br />
Anhang 6<br />
Erweiterung der nasolver-Klasse<br />
// Constructor creates an unnamed instance of the nasolver class.<br />
template <br />
nasolver::nasolver () : analysis () {<br />
/* ... */<br />
if(networkUse)<br />
{<br />
myNet = network(networkAddr, networkProtocol);<br />
myNet.netStartSocket();<br />
myNet.netConnect();<br />
}<br />
}<br />
// Constructor creates a named instance of the nasolver class.<br />
template <br />
nasolver::nasolver (char * n) : analysis (n) {<br />
/* ... */<br />
if(networkUse)<br />
{<br />
myNet = network(networkAddr, networkProtocol);<br />
myNet.netStartSocket();<br />
myNet.netConnect();<br />
}<br />
}<br />
// Destructor deletes the nasolver class object.<br />
template <br />
nasolver::~nasolver () {<br />
/* ... */<br />
if(networkUse)<br />
myNet.netClose();<br />
}<br />
/* ... */<br />
/* This function saves the results of a single solve() functionality<br />
into the output dataset. */<br />
template <br />
void nasolver::saveResults (const char * volts, const char * amps,<br />
int saveOPs, vector * f) {<br />
/* ... */<br />
//Send Result to Server:<br />
if(networkUse)<br />
{<br />
// Create XML-Node:<br />
const char vers[] = {PACKAGE_VERSION};<br />
int len;<br />
len = strlen(netPrint)+strlen(vers)+39;<br />
char * xnode = (char *)malloc(len + 1);<br />
sprintf(xnode, "%s", vers,<br />
netPrint);<br />
xnode[len] = '\0';<br />
}<br />
myNet.netSend(xnode);<br />
}<br />
// Add delay to simulation:<br />
usleep(msDelay*1000);
Speichern der Ergebnisse und Abhängigkeiten in Qucs Anhang 7<br />
Anhang 7 Speichern der Ergebnisse und Abhängigkeiten in Qucs<br />
Änderungen an der analysis.cpp:<br />
int analysis::solve (char const * str)<br />
{<br />
netPrint = (char*)malloc(strlen(str)+1);<br />
strcpy(netPrint, str);<br />
return solve();<br />
}<br />
Aufruf der getDNode()-Funktion:<br />
void analysis::getDNode (char const * n, nr_complex_t val)<br />
{<br />
char * out;<br />
char * tmp;<br />
double creal = real(val);<br />
double cimag = imag(val);<br />
int len = 0;<br />
if(cimag != 0) {<br />
tmp = (char *)malloc(40);<br />
char sign = '+';<br />
if(cimag < 0) {<br />
sign = '-';<br />
cimag = -cimag;<br />
}<br />
sprintf(tmp, "%+.11e%cj%.11e", creal, sign, cimag);<br />
}<br />
else {<br />
tmp = (char *)malloc(20);<br />
sprintf(tmp, "%+.11e", creal);<br />
}<br />
if(netPrint2==NULL) {<br />
len = strlen(n)+strlen(tmp)+12;<br />
netPrint2 = (char *)malloc(len+1);<br />
sprintf(netPrint2, "%s", n, tmp);<br />
}<br />
else {<br />
len = strlen(netPrint2);<br />
char * help = (char*)malloc(len+1);<br />
strcpy(help, netPrint2);<br />
free(netPrint2);<br />
len = strlen(help)+strlen(n)+strlen(tmp)+12;<br />
netPrint2 = (char *)malloc(len+1);<br />
sprintf(netPrint2, "%s%s", help, n, tmp);<br />
free(help);<br />
}<br />
free(tmp);<br />
}<br />
return;
Speichern der Ergebnisse und Abhängigkeiten in Qucs Anhang 7<br />
Beispiel für die Verwendung der getINode()-Funktion:<br />
char * analysis::getINode (char const * IName, char const * unit, nr_double_t v)<br />
{<br />
char * out;<br />
char* tmpName;<br />
int len;<br />
char val[20];<br />
sprintf(val, "%+.11e", v);<br />
if(strlen(unit)>0) {<br />
len = strlen(IName)+strlen(unit)+1;<br />
tmpName = (char *)malloc(len+1);<br />
sprintf(tmpName, "%s.%s", IName, unit);<br />
}<br />
else {<br />
len = strlen(IName);<br />
tmpName = (char *)malloc(len+1);<br />
sprintf(tmpName, "%s", IName);<br />
}<br />
tmpName[len] = '\0';<br />
if(netPrint == NULL) {<br />
len = strlen(tmpName)+strlen(val)+12;<br />
out = (char *)malloc(len+1);<br />
sprintf(out, "%s", tmpName, val);<br />
}<br />
else {<br />
len = strlen(tmpName)+strlen(val)+strlen(netPrint)+12;<br />
out = (char*)malloc(len+1);<br />
sprintf(out, "%s%s", netPrint, tmpName, val);<br />
}<br />
out[len] = '\0';<br />
return out;<br />
}
Typen Anhang 8<br />
Anhang 8<br />
Typen<br />
# Bezeichnungen<br />
0 UNKNOWN<br />
1 NONE<br />
2 volt voltage<br />
3 amp current<br />
4 second time<br />
5 hertz frequency<br />
6 ohm resistance<br />
7 siemens conductance<br />
8 farad capacity<br />
9 henry inductance<br />
10 joule energy<br />
11 watt power<br />
12 coulomb charge<br />
13 weber magnetic flux<br />
14 volt_squared<br />
15 volt_squared_over_hertz<br />
16 volt_over_square_root_of_hertz<br />
17 amp_squared<br />
18 amp_squared_over_hertz<br />
19 amp_over_square_root_of_hertz<br />
20 volt_over_second<br />
21 constant<br />
22 centigrad<br />
23 digital<br />
24 tesla<br />
25 amp_turns_over_meter<br />
26 temperature<br />
27 length<br />
28 angle
Beispiel für eine Kurvenschar Anhang 9<br />
Anhang 9 Beispiel für eine Kurvenschar<br />
Die Schaltung:<br />
Ausgabe der Simulation:<br />
Auszug der übertragenen Zwischenergebnisse:<br />
<br />
<br />
<br />
+6.07902735562e-006<br />
<br />
<br />
+1.00000000000e+001<br />
<br />
<br />
-2.57362469582e-002<br />
<br />
<br />
+4.59512013373e-001<br />
<br />
<br />
+2.02149543791e-001<br />
<br />
Beispiel für eine Kurvenschar Anhang 9<br />
Liste der Waveform-Dateien:<br />
Die Manifest-Datei:<br />
# Division Labels (comma separated) : P1 +1.00e+000, P1 +3.16e+001, P1 +1.00e+003<br />
Ua.Vt,Sim1_6373452_group0_c0_div_0,4,2,1,1<br />
Ua.Vt,Sim1_6373452_group0_c0_div_1,4,2,1,1<br />
Ua.Vt,Sim1_6373452_group0_c0_div_2,4,2,1,1<br />
Ue.Vt,Sim1_6373452_group0_c1_div_0,4,2,1,1<br />
Ue.Vt,Sim1_6373452_group0_c1_div_1,4,2,1,1<br />
Ue.Vt,Sim1_6373452_group0_c1_div_2,4,2,1,1<br />
Ue1.It,Sim1_6373452_group0_c2_div_0,4,3,1,1<br />
Ue1.It,Sim1_6373452_group0_c2_div_1,4,3,1,1<br />
Ue1.It,Sim1_6373452_group0_c2_div_2,4,3,1,1
Änderungen an der EngineServer-Klasse Anhang 10<br />
Anhang 10 Änderungen an der EngineServer-Klasse<br />
ESTypes.h:<br />
public ref struct WFData : public Object<br />
{<br />
public:<br />
array ^m_XDataReal;<br />
array ^m_YDataReal;<br />
array ^m_XDataImag;<br />
array ^m_YDataImag;<br />
};<br />
public ref struct WFLiveData : public Object<br />
{<br />
public:<br />
List ^m_XDataReal;<br />
List ^m_YDataReal;<br />
List ^m_XDataImag;<br />
List ^m_YDataImag;<br />
};<br />
EngineServerWSClass.cpp:<br />
void EngineServerWSClass::ALResultCallBack(const char* WFFilename, float XData,<br />
float YData, float XDataImag, float YDataImag,<br />
bool IsComplex)<br />
{<br />
IDictionaryEnumerator ^DicEnum = c_JobHandleToJobObject->GetEnumerator();<br />
while ( DicEnum->MoveNext() )<br />
{<br />
ESJobObject ^jo = safe_cast(DicEnum->Value);<br />
if ( jo != nullptr ) {<br />
String ^WaveFormFilename = gcnew String(WFFilename);<br />
if(!String::IsNullOrEmpty(jo->m_strFileTag)) {<br />
if(jo->m_mapWFLiveData == nullptr) {<br />
jo->m_mapWFLiveData = gcnew Hashtable();<br />
}<br />
WFLiveData ^wfldat = (WFLiveData ^)jo->m_mapWFLiveData[WaveFormFilename];<br />
if(wfldat != nullptr) {<br />
wfldat->m_XDataReal->Add(XData);<br />
wfldat->m_YDataReal->Add(YData);<br />
if(IsComplex) {<br />
wfldat->m_XDataImag->Add(XDataImag);<br />
wfldat->m_YDataImag->Add(YDataImag);<br />
}<br />
jo->m_mapWFLiveData[WaveFormFilename] = wfldat;<br />
}<br />
}<br />
}<br />
}<br />
}
Auslesen der Simulationsergebnisse aus dem Zwischenspeicher Anhang 11<br />
Anhang 11 Auslesen der Simulationsergebnisse aus dem Zwischenspeicher<br />
WFLiveData ^wfldat = nullptr;<br />
if(jo->m_mapWFLiveData != nullptr)<br />
wfldat = (WFLiveData ^)jo->m_mapWFLiveData[WFFileName];<br />
if(wfldat == nullptr)<br />
{<br />
wfldat = gcnew WFLiveData();<br />
wfldat->m_XDataReal = gcnew List();<br />
wfldat->m_YDataReal = gcnew List();<br />
if(bComplex)<br />
{<br />
wfldat->m_XDataImag = gcnew List();<br />
wfldat->m_YDataImag = gcnew List();<br />
}<br />
else<br />
{<br />
wfldat->m_XDataImag = nullptr;<br />
wfldat->m_YDataImag = nullptr;<br />
}<br />
for(int i=0; i< wfdat->m_XDataReal->Length; i++)<br />
{<br />
wfldat->m_XDataReal->Add(wfdat->m_XDataReal[i]);<br />
wfldat->m_YDataReal->Add(wfdat->m_YDataReal[i]);<br />
}<br />
}<br />
if(bComplex)<br />
{<br />
wfldat->m_XDataImag->Add(wfdat->m_XDataImag[i]);<br />
wfldat->m_YDataImag->Add(wfdat->m_YDataImag[i]);<br />
}<br />
XData = wfldat->m_XDataReal->ToArray();<br />
YData = wfldat->m_YDataReal->ToArray();<br />
if(bComplex)<br />
{<br />
XDataImag = wfldat->m_XDataImag->ToArray();<br />
YDataImag = wfldat->m_YDataImag->ToArray();<br />
}<br />
return ES_ERROR_NOERROR;
Ehrenwörtliche Erklärung<br />
Ehrenwörtliche Erklärung<br />
„Ich erkläre hiermit ehrenwörtlich“,<br />
1. dass ich meine Bachelor Thesis mit dem Thema<br />
Erweiterung des Engine Framework und der Web-Schematic Lösung der<br />
Transim Technology Corporation um eine Echtzeitkommunikation zwischen<br />
Simulator und Schematic<br />
ohne fremde Hilfe angefertigt habe.<br />
2. dass ich die Übernahme wörtlicher Zitate aus der Literatur sowie die<br />
Verwendung der Gedanken anderer Autoren an den entsprechenden Stellen<br />
innerhalb der Arbeit gekennzeichnet habe und<br />
3. dass ich meine Bachelor Thesis bei keiner anderen Prüfung vorgelegt habe.<br />
Ich bin mir bewusst, dass eine falsche Erklärung rechtliche Folgen haben wird.<br />
____________________<br />
Ort, Datum<br />
____________________<br />
Unterschrift
Thesen<br />
Thesen<br />
1. Die Kommunikation zwischen der Simulationsanwendung Qucs und der<br />
entsprechenden Engine des EngineServers erfolgt mithilfe von Datenströmen.<br />
2. Als Protokoll für die Kommunikation zwischen Simulationsanwendung und<br />
Engine ist das sicherere TCP bevorzugt gegenüber dem schnelleren UDP.<br />
3. Bei mehreren gestarteten Simulationen muss eine Zuweisung der<br />
übertragenen Daten zu den Simulationen erfolgen. Diese Zuweisung erfolgt,<br />
indem für jede Simulation ein eigener TCP-Server erstellt wird, der an einen<br />
eigenen Port gebunden ist.<br />
4. Als Format für die Übertragung der Daten von Qucs an das EngineFramework<br />
ist XML, aufgrund eines kleineren Overhead, gegenüber JSON zu verwenden.<br />
5. Zum Empfangen der Simulationsergebnisse in der Engine wird ein TCP-<br />
Server benötigt. Um ein blockieren der Engine zu verhindern, muss dieser in<br />
einem zusätzlichen Thread ausgeführt werden.<br />
6. Um die Simulationsergebnisse von der Engine an den EngineServer zur<br />
Laufzeit weiterzuleiten eignet sich die Verwendung von Rückruffunktionen.