Entwicklung und Implementierung einer Schnittstelle zwischen

Transcription

Entwicklung und Implementierung einer Schnittstelle zwischen
Entwicklung und Implementierung einer Schnittstelle zwischen
Prozessmodellen in Simulink und in C / C++ implementierten
Steuerungen/Regelungen
Seminararbeit im Rahmen des Studienganges „Scientific Programming“
Aachen, den 15.12.2011
Verfasser:
Daniel Schnitzler
Matrikel-Nr.: 836360
Betreuer:
Prof. Dr. Andreas Terstegge, FH Aachen
Dr.-Ing. Michael Mlynski, EUtech Scientific Engineering GmbH
Inhaltsverzeichnis
1.
Einleitung...................................................................................................................... 4
2.
Verwendete Technologien ........................................................................................... 5
2.1
Dynamic Link Libraries ....................................................................................... 5
2.2
MATLAB/Simulink ............................................................................................. 5
2.2.1
Durchführung einer Simulation ......................................................................6
2.2.2
S-Functions.....................................................................................................7
2.3
3.
Regelungstechnik .................................................................................................8
Die Anforderungen ....................................................................................................10
3.1
Anbindung der Regelung in der EshSim ............................................................ 10
3.2
Anbindung der Regelung an einen Evaluierungsprototypen .............................. 11
4.
Analyse und Umsetzung ............................................................................................ 11
4.1
Der Energiemanager ........................................................................................... 11
4.1.1
Implementierung einer C/C++ DLL............................................................. 12
4.1.2
Implementierung des Energiemanagers ....................................................... 12
4.2
S-Function ..........................................................................................................13
4.2.1
Verwendung einer C/C++-DLL ...................................................................13
4.2.2
Implementierung der S-Function .................................................................15
4.3
5.
Einbettung in C#.................................................................................................16
4.3.1
Einbettung einer unmanaged DLL in C# ..................................................... 17
4.3.2
C++ Strings in C#......................................................................................... 18
4.3.3
Einbettung des Energiemanagers in C# ....................................................... 19
Test .............................................................................................................................. 21
5.1
Das Testmodell ...................................................................................................21
5.2
Die Implementierung der neuen Schnittstelle .................................................... 23
5.3
Laufzeitanalyse ...................................................................................................24
6.
Fazit/Ergebnisse .........................................................................................................25
7.
Ausblick Bachelorarbeit ............................................................................................ 25
8.
Quellenverzeichnis .....................................................................................................26
8.1
Literaturverzeichnis ............................................................................................ 26
8.2
Tabellenverzeichnis ............................................................................................ 26
8.3
Abbildungsverzeichnis ....................................................................................... 27
2
9.
Eidesstaatliche Erklärung ......................................................................................... 28
10.
Anhang .............................................................................................................. 29
10.1
Energiemanager.h ............................................................................................... 29
10.2
Energiemanager.cpp ........................................................................................... 30
10.3
EM_Aux.h ..........................................................................................................31
10.4
EM_Aux.cpp ......................................................................................................32
10.5
S-Function Energiemanager_SFun.c ..................................................................33
10.6
EnergieManagerWrapper.cs ............................................................................... 38
10.7
Kompilierung unter 64bit mit Visual Studio Express Edition ........................... 39
10.8
Verwenden von unmanaged Code mit Visual Studio Express Edition .............. 40
3
1.
Einleitung
Das Softwareprodukt EshSim (Energy supply for homes) der Firma EUtech Scientific Engineering GmbH simuliert auf Basis physikalischer Modelle Gebäude mit ihren Energieversorgungsanlagen, insbesondere Heizungsanlagen.
Der Anwender kann das Gebäude (Größe, Wärmedämmstandard, Standort, Verbraucherverhalten etc.), die Energieversorungsanlage (Typ, Aufbau, Dimension etc.) und die Randbedingungen (Tarife, Investitionskosten etc.) in der Benutzeroberfläche vorgeben. Auf Basis dieser
Informationen liefert die Simulation innerhalb weniger Minuten ein detailliertes Bild über das
Betriebsverhalten der Energieversorgungsanlage. So können u.a. Verbrauch, entstandene Kosten, Emissionen etc. für ein Jahr berechnet werden [1].
Bezüglich der Regelung kann der Anwender zwar eine Regelparametrierung vorgeben, eine
Änderung der Regellogik ist jedoch nicht möglich.
Die EshSim besteht aus zwei Komponenten. Die Benutzeroberfläche (in C# implementiert)
und die Simulation (in Simulink implementiert). In der Simulation ist auch die Regellogik
implementiert. Änderungen an den Simulationsmodellen können zurzeit nur von der Firma
EUtech durchgeführt werden. Der Anwender hat keine Möglichkeit, seine eigene Regellogik
zu implementieren. Daraus resultierte die Anforderung, dass der Anwender die Regellogik
seiner Energieversorgungsanlage selber implementieren kann. Es soll über eine Schnittstelle
möglich sein, diese Regellogik an das Simulationsmodell anzudocken. Darüber hinaus soll es
möglich sein, diese Regellogik auch in einen Prototyp einer realen Anlage einzusetzen.
Das Ziel dieser Seminararbeit ist daher die Entwicklung und Implementierung einer Schnittstelle zwischen der Regelung des Anwenders und der Simulation. Da die Regelung auch intelligent sein kann, wird nachfolgend von einem Energiemanager gesprochen.
Im folgenden Kapitel wird der Begriff „Dynamic Link Library“ und die verwendete Entwicklungsumgebung Simulink näher erläutert. Das Kapitel 3 beschäftigt sich mit den Anforderungen an die Schnittstelle. Die Analyse und Implementierung sind Thema des Kapitels 4; im
Kapitel 5 wird der Test der neuen Schnittstelle dargestellt. Abschließend werden ein Fazit und
ein Ausblick auf die darauf aufbauende Bachelorarbeit gegeben.
4
2.
Verwendete Technologien
2.1 Dynamic Link Libraries
Der Begriff Dynamic Link Libraries stammt aus der Microsoft Windows Umgebung. Dynamic Link Libraries (DLLs) sind Bibliotheken (Libraries), die im Gegensatz zu statischen Bibliotheken (*.lib) nicht zur Kompilierzeit, sondern beim Start einer Applikation an diese angebunden werden. Wird bereits ein anderes Programm ausgeführt, das diese DLL ebenfalls verwendet, so braucht das Betriebssystem diese DLL nicht erneut zu laden [2].
Beim Kompilieren des Programmes wird eine „Import Address Table“ (IAT) erstellt. In dieser
sind alle benötigten externen Methoden aufgeführt. Wird das erste Programm gestartet, das
die DLL verwendet, so wird die IAT ausgelesen und die DLLs werden in den Speicher nachgeladen. Bei jedem Programm, das die DLL verwendet, wird ein Zähler inkrementiert und
beim Beenden des Programmes dekrementiert. Dadurch wird sichergestellt, dass die DLL erst
aus dem Speicher entladen wird, wenn kein Programm mehr auf die Funktionalität angewiesen ist [3].
Die Vorteile für die Verwendung von DLLs liegen klar auf der Hand [4]:




Man spart Arbeitsspeicher, da die DLL nur einmal im Speicher liegt
Man kann die Funktionalität mit einer anderen Programmiersprache verwenden, falls
diese Programmiersprache DLLs einbinden kann
Programmfunktionalität kann beim Anwender leichter ausgetauscht werden
Man spart Zeit, da die Funktionalität nur einmal implementiert, kompiliert und getestet werden muss.
Die Verwendung von DLLs führt allerdings auch Nachteile mit sich. Wenn verschiedene Programme verschiedene Versionen der DLL verwenden, so kann es zu einem Konflikt kommen,
da bereits eine Version der DLL im Speicher liegt, jedoch eine andere Version benötigt wird.
2.2 MATLAB/Simulink
Das Softwarepaket MATLAB (MATrix LABoratory) der Firma Mathworks dient zur numerischen Berechnung von naturwissenschaftlich-technischen/mathematischen Problemen unter
der Zuhilfenahme von Matrizen. Es wird hauptsächlich zur Modellierung, der Datenerfassung,
-analyse und -auswertung verwendet.
Auf MATLAB baut Simulink auf. Simulink dient der graphischen Modellierung und der Simulation dynamischer Systeme. Es integriert sich nahtlos in die MATLAB IDE1. Die Idee
von Simulink ist es, das gegebene System in Form eines Signalflussplanes darzustellen. Mit
Simulink können beispielsweise Prozessschaltbilder in Computermodelle abgebildet werden.
Dabei muss das Prozessschaltbild nicht zuerst in eine textuelle Form transformiert werden.
1
Integrierte Entwicklungsumgebung (integrated development environment)
5
Abbildung 2-1 Simulink Library Browser
Abbildung 2-1 zeigt den Simulink Library Browser, eine Bibliothek mit bereits vorimplementierten Blöcken. Alle Blöcke sind nach einem einfachen Prinzip aufgebaut. Ein Block kann
mehrere Ein- bzw. Ausgänge haben. Die Blöcke werden über Leitungen verbunden. Über
diese Leitungen werden die Daten von einem Block in einen anderen Block transportiert. Die
Art der Daten (Matrizen, Vektoren oder skalare Werte) ist flexibel handhabbar. Durch diese
flexible Behandlung von Daten lässt sich Simulink sehr gut nutzen, um z.B. Regelkreise zu
simulieren.
Mehrere Blöcke können zu einem Subsystem zusammengefasst werden. Dies entspricht
Funktionen in textuellen Programmiersprachen. Dadurch können komplexe Zusammenhänge
überschaubarer implementiert werden. Diese Subsysteme können in einem Modell auch wiederverwendet werden [5].
2.2.1 Durchführung einer Simulation
Eine Simulation eines z.B. physikalischen Modells führt immer dazu, dass eine Vielzahl von
Gleichungen berechnet werden müssen. Meistens sind dies keine einfachen, linearen Gleichungssysteme, die leicht gelöst werden können. Zudem sind nur die Differentialgleichungen
bekannt und nicht die genauen Funktionen. Daher müssen diese numerisch gelöst werden.
Dies führt dazu, dass das System in jedem Zeitschritt neu berechnet werden muss. Hierzu gibt
es unterschiedliche numerische Verfahren.
6
Abbildung 2-2 Ablauf einer Simulation [6]
Die Abbildung 2-2 zeigt den Ablauf einer Simulation. Zunächst wird das gesamte Modell
initialisiert. Hierbei wird auch festgelegt, in welcher Reihenfolge die Blöcke im Modell ausgeführt werden. Dann wird der erste Zeitschritt ausgeführt und die Ausgänge jeden Blocks
und ihre Ableitungen berechnet. Wird ein diskreter Solver verwendet – das bedeutet, das Modell wird in festgelegten Schrittweiten berechnet – so ist ein Simulationsschritt abgearbeitet.
Verwendet der Solver hingegen variable Schrittweiten, also ein adaptives Verfahren zur Lösung der Differentialgleichungen, so muss zur genaueren Bestimmung der optimalen Schrittweite und damit des genaueren Verhaltens des Blockes noch einmal die Ausgangswerte des
Blockes, sowie deren Ableitungen bestimmt werden. Dies passiert so lange, bis die Werte als
gut angenommen werden können, das heißt, dass sie nicht zu stark vom richtigen Wert abweichen.
2.2.2 S-Functions
Wie bereits beschrieben, lässt sich die Simulink Standardbibliothek um individuelle Blöcke
erweitern. Dies können zum Beispiel die oben beschriebenen Subsysteme sein. Daneben gibt
es aber auch die Möglichkeit, so genannte S-Functions zu verwenden. Der Begriff SFunctions steht für system-functions. Hierbei wird ein Block textuell implementiert. Der Code
kann in FORTRAN, C/C++ oder in MATLAB geschrieben werden. In MATLAB programmierte S-Functions können direkt im Modell verwendet werden. S-Functions aus C/C++ bzw.
FORTRAN müssen zunächst kompiliert werden und werden dann während der Ausführung
des Modells dynamisch verwendet, analog zu den DLLs.
7
S-Functions verwenden eine bestimmte API2, die es erst möglich macht, kompilierte Funktionen zu verwenden. Daher müssen diese Funktionen einem klar definierten Interface entsprechen. Dieses Interface spiegelt den in Abbildung 2-1 angesprochenen Ablauf einer Simulation
wider. Für jeden Zwischenschritt eines Simulationsschrittes gibt es eine Funktion in der SFunction (vgl. Tabelle 2-1).
Simulation Stage
S-Function Routine
Initialization
mdlInitializeSizes
Calculation of next sample hit (variable sample mdlGetTimeOfNextVarHit
time block only)
Calculation of outputs
mdlOutputs
Update of discrete states
mdlUpdate
Calculation of derivatives
mdlDerivatives
End of simulation tasks
mdlTerminate
Tabelle 2-1- Methodenübersicht S-Function [6]
2.3 Regelungstechnik
Die Regelungstechnik ist ein Teilgebiet der Ingenieurswissenschaften. Aufgabe einer Regelung ist es, eine zu regelnde Größe unabhängig von Störeinflüssen zu halten. Es soll stets ein
bestimmten Wert (Sollwert) eingehalten werden [7]. Dazu wird in jedem Zeitschritt der aktuelle Wert bestimmt. Mit der Differenz aus dem aktuellen Wert und dem Sollwert werden Eingriffe auf die Eingangsgrößen des Prozesses ermittelt.
Dieses Vorgehen lässt sich gut am Beispiel eines Heizungsthermostat illustrieren (vgl. Abbildung 2-3). Aufgabe der Heizung ist es, die Temperatur in einem Zimmer auf einem bestimmten Temperaturniveau zu halten. Sinkt die Zimmertemperatur, so soll die Heizung heizen. Ist
es dagegen zu warm, soll die Heizung abschalten. Damit der Regler weiß, ob der Wärmebedarf des Zimmers steigt oder sinkt, wird zu jedem Zeitpunkt eine Differenz zwischen der eingestellten Wunschtemperatur (Sollwert) und der gemessenen Temperatur (Istwert) berechnet.
Der Regler leitet aus der Differenz ab, wie er die Aktoren 3 zu steuern hat. Dabei muss er beachten, dass sich die Erhöhung der Wärmemenge der Heizung nur mit starker Verzögerung
auf die Zimmertemperatur auswirkt.
2
Application Programming Interface
3
Aktoren formen Stromsignale in andere physikalische Größen (z.B. die Stellung eines Ventils) um [12].
8
Abbildung 2-3 Temperaturregelung
9
3.
Die Anforderungen
In der aktuellen Implementierung der EshSim gibt es zwei Komponenten (vgl. Abbildung
3-1). Der Anwender gibt die Parametrierung der Simulation in einer Benutzeroberfläche ein.
Diese ist in C# programmiert. Die Benutzeroberfläche ruft das Simulationsmodell auf. Das
Simulationsmodell gibt nach erfolgter Simulation die Ergebnisse an die Benutzeroberfläche
zurück.
Abbildung 3-1 Aktuelle Implementierung der EshSim
In der neuen Version der EshSim wird die Regelung als eigene Komponente implementiert.
Die Anbindung des Energiemanagers soll in zwei unterschiedlichen Umgebungen möglich
sein:


Anbindung der Regelung in eine Simulationsumgebung (EshSim)
Anbindung der Regelung an einen Evaluierungsprototypen
Diese beiden Verwendungsszenarien des Energiemanagers werden im folgenden Kapitel näher erläutert.
3.1 Anbindung der Regelung in der EshSim
Der Energiemanager sollte an das Simulationsmodell der EshSim angebunden werden. Dazu
werden S-Functions verwendet. Sowohl die S-Function als auch der Energiemanager werden
in C/C++ implementiert. Der Energiemanager wird der Simulation als DLL zur Verfügung
gestellt, sodass er vom Anwender durch eigene Regellogik ersetzt werden kann.
Abbildung 3-2 Konzept der Schnittstelle in der EshSim
Abbildung 3-2 zeigt das neue Konzept bezüglich der Anbindung des Energiemanagers in die
EshSim. Der Energiemanager ist nicht mehr Teil der Simulation, sondern wird als unabhängige Komponente von der Simulation aufgerufen.
10
3.2 Anbindung der Regelung an einen Evaluierungsprototypen
Neben der Anbindung an die EshSim sollte es auch möglich sein, den Energiemanager als
Regelung zur Evaluierung eines Prototypens einer Energieversorgungsanlage zu verwenden.
Dazu wird ein Computer verwendet, auf dem eine C#-Applikation läuft. Sie übernimmt die
Kommunikation zwischen Energiemanager und der realen Energieversorgungsanlage. Die
Kommunikation kann über verschiedene Protokolle stattfinden, z.B. ModbusTCP.
Abbildung 3-3 Konzept der Schnittstelle mit einer realen Energieversorgungsanlage
Im folgenden Kapitel wird erläutert, welche Schritte notwendig sind, wie der Energiemanager
zu implementieren ist, und wie er in einer S-Function bzw. einer C#-Applikation verwendet
werden kann.
4.
Analyse und Umsetzung
4.1 Der Energiemanager
Wie in Kapitel 3 beschrieben, sollte der Energiemanager in C/C++ implementiert werden.
Eine wichtige Anforderung war, dass die Trennung von Simulation und Regelung die Rechenzeit nicht beeinträchtigt. Daher wurde die Implementierung in unmanaged Code vorgenommen.
In der Windows-Umgebung mit dem Visual Studio als Entwicklungsumgebung für C/C++
stehen 2 Arten von Code zur Verfügung:


managed Code
unmanaged Code
11
Bei managed Code wird die Instanz der Applikation in einer virtuellen Maschine ausgeführt,
die vom .NET-Framework bereitgestellt wird. Der Code wird daher gemanaged (verwaltet).
Die Ausführung in einer virtuellen Maschine bietet zwar eine hohe Sicherheit für die Applikation, doch erhöht dies die Ausführungszeit. Daher wurde für den Energiemanager die Variante
des unmanaged Codes verwendet. Unmanaged Code, auch als nativer Code bezeichnet, ist die
ursprüngliche Version von C/C++-Code, wie er auch im ISO-Standard festgehalten wurde.
Dieser C/C++-Code ist unabhängig von der Windows-Umgebung und ist daher portabel.
Wie man mit dem Visual Studio Express Edition ein Projekt für unmanaged Code anlegt, sowie die Kompilierung einer Applikation für eine 64bit-Umgebung sind dem Anhang zu entnehmen.
4.1.1 Implementierung einer C/C++ DLL
Der Energiemanager sollte als C/C++-DLL implementiert werden. Daher wird zunächst erläutert, wie eine allgemeine Implementierung einer DLL aussieht.
Im Quelltext einer DLL muss jede Funktion, die außerhalb der DLL aufgerufen werden kann,
als exportierte Funktion explizit deklariert werden:
__declspec(dllexport) double MyFunction(int a, int b);
Die Bezeichnung __declspec(dllexport) beschreibt zum einen, dass diese Funktion außerhalb
der DLL verwendet werden kann. Zum anderen wird über __declspec aber auch dem Compiler mitgeteilt, wie eine Übergabe der Parameter beim Aufruf dieser Funktion stattfinden
soll[8].
4.1.2 Implementierung des Energiemanagers
Die Defintion des Energiemanagers sieht wie folgt aus:
// returns the current version of the DLL
__declspec(dllexport) void GetVersion(short *m, short *s, short *b, short *i);
// returns the last date the DLL has changed
__declspec(dllexport) void GetDate(short *day, short *month, short *year);
// returns the DLL's author
__declspec(dllexport) void GetAuthor(char* test, size_t buffer_size);
// returns the DLL author's Company
__declspec(dllexport) void GetCompany(char* test, size_t buffer_size);
// initializes the Energy manager
__declspec(dllexport) int EM_Initialize(…);
// sets the Configuration of the Energy manager
__declspec(dllexport) void EM_SetConfig(…);
// executes one step of the controlling
12
__declspec(dllexport) void EM_Execute(…);
Die
Funktionen
GetVersion(…),
GetDate(…),
GetAuthor(…)und
GetCompany(…) liefern jeweils Informationen über die vorliegende Version der DLL. So
kann überprüft werden, in welcher Version die DLL vorliegt, und wer die Kontaktperson bezüglich dieser DLL ist.
Die Funktionen EM_Initialize(…) überprüft zum einen die Kompatibilität zur aufrufenden Applikation und initialisiert zum anderen Variablen, die zur Speicherung der historischen
Daten verwendet werden. In der DLL werden Arrays einer definierten Länge übergeben. Zur
Laufzeit muss sichergestellt werden, dass die übergebenen Arrays mindestens die von der
DLL vorausgesetzte Länge haben. Sonst kann es bei Zugriffen auf den Speicher zu Fehlern
kommen. Dazu wird der Methode EM_Initialize die Länge der zu übergebenden Arrays
mitgeteilt. Widersprechen die angegebenen Längen sich, so wird eine Fehlernummer als Integer-Wert zurückgegeben. Anhand der Fehlernummer kann man erkennen, bei welchem Feld
die Länge nicht stimmt. Für die Auswertung dieser Fehlernummer ist jedoch der Aufrufer der
DLL zuständig. Um die Länge der Felder zumindest zwischen DLL und S-Function von Anfang an konsistent zu halten, wurden Defines verwendet. Defines sind Texterkersetzungen,
die vom Präprozessor vor dem eigentlichen Kompilier-Vorgang ausgeführt werden. Diese
Definitionen wurden in einer separaten Header-Datei implementiert, die sowohl in der DLL
als auch in der S-Function inkludiert ist.
Die Funktion EM_SetConfig parametriert die Regelung, indem sie alle Parameter speichert, die sich zur Laufzeit nicht ändern können. (z.B. Minimale und Maximale Temperaturangeben). Dadurch kann die Anzahl der Parameter der Methode EM_Execute stark verringert werden.
Der Kern der DLL ist die Funktion EM_Execute. Sie beinhaltet die eigentliche Regelung.
Sie berechnet mithilfe der aktuellen Sensorwerte, der Konfiguration und den historischen Daten die auszugebenden Aktorwerte. Dafür sind natürlich aufgrund der Komplexität des Reglers mehrere Unterfunktionen nötig, die von EM_Execute aufgerufen werden. Diese müssen
allerdings nicht nach außen sichtbar sein und werden nur von der Funktion EM_Execute
verwendet.
Eine exemplarische Implementierung der DLL befindet sich im Anhang.
4.2 S-Function
Die S-Function (vgl. Abbildung 3-2) ist die Verknüpfung zwischen der Simulation in Simulink und dem Energiemanager in C++. Sie ist in C implementiert. Zunächst wird erläutert,
welche Möglichkeiten es gibt, eine C/C++-DLL in einer C/C++-Applikation zu verwenden.
Im Kapitel 4.2.2 wird auf die konkrete Implementierung dieser S-Function eingegangen.
4.2.1 Verwendung einer C/C++-DLL
Für die Einbindung einer C/C++-DLL in einer C/C++-Applikation gibt es 2 Möglichkeiten:
13


Explizite Bindung
Implizite Bindung
Diese werden in den folgenden Unterkapiteln näher erläutert.
4.2.1.1 Explizite Bindung
Bei der expliziten Bindung wird die DLL erst zur Laufzeit programmatisch geladen. Lediglich
die Implementierung der aufrufenden Applikation ändert sich. Der Aufruf sieht folgendermaßen aus[8]:
typedef int (MYFUNCTION) (int, int);
HINSTANCE hInstance;
MYFUNCTION *myfunc;
VERIFY(hInstance = ::LoadLibrary(“C:\MyDLL.dll”));
VERIFY(pFunction = (MYFUNCTION*)::GetProgAddress(hInstance, “MyFunction));
int d = (*pFunction)(5, 4); //Aufruf der eigentlichen DLL
Das in diesem Beispiel verwendete Makro VERIFY ist ähnlich dem Makro ASSERT. Es ist
nicht Teil des Ansi C/C++-Standards. VERIFY liefert einen Fehler, wenn die Applikation im
Debug-Modus kompiliert wurde und die Zuweisung für hInstance bzw. pFunction zu Fehlern
führte. Im kompilierten Release-Modus wird VERIFY ignoriert.
Wie man an diesem Beispiel sieht, ist der Aufwand relativ hoch, bis eine Funktion der DLL
verwendet werden kann. Allerdings erhöht sich auch die Flexibilität. Zur Laufzeit kann entschieden werden, welche Implementierung von MyFunction man aus welcher DLL importieren möchte. Bei dieser Lösung müssen jedoch der absolute bzw. relative Pfad und der Name der DLL bekannt sein.
4.2.1.2 Implizite Bindung
Bei der impliziten Bindung wird die DLL beim Start der Applikation geladen und kann danach ohne Aufwand verwendet werden. Methoden aus der DLL können genauso verwendet
werden, wie Methoden, die innerhalb der Applikation implementiert wurden.
So muss eine Applikation, die z.B. die Methode MyFunction(int a, int b) verwenden will, lediglich die Signatur dieser Methode bekannt gemacht werden. Dies geschieht über
folgende Funktionssignatur:
extern __declspec(dllimport) double MyFunction(int a, int b);
Das Schlüsselwort extern weist darauf hin, dass die Funktion außerhalb der Applikation
deklariert wurde. Mit __declspec(dllimport) wird beschrieben, dass diese Funktion
aus einer DLL importiert wird.
Diese Zeilen weisen den Compiler, dass diese Funktionalität von einer anderen Datei zur Verfügung gestellt wird. Erst der Linker löst diese Abhängigkeit auf. Beim Linken der Applikation wird eine *.lib-Datei der DLL zugelinkt. Diese *.lib-Datei hält die Funktionssignaturen
bereit. Sie enthält keinerlei Informationen über die Implementierung der Funktionen.
14
Zur Laufzeit wird die DLL dann beim Start der Applikation geladen. Da zum Zeitpunkt des
Kompilierens noch kein Pfad zur DLL angegeben werden kann, kann dieser erst beim Start
der Applikation gesucht werden. Dazu werden 5 verschiedene Orte durchsucht:
1. Das Verzeichnis, in dem sich die Applikation befindet.
2. Das Verzeichnis, in dem die Applikation ausgeführt wird.
3. Das Windows-Systemverzeichnis
4. Das Windows-Verzeichnis
5. Alle Verzeichnisse, die sich in der Path-Variablen von Windows befinden.
Diese 5 Orte werden in der hier angegebenen Reihenfolge durchsucht. Wird die DLL in einem
der Orte gefunden, so wird die Suche abgebrochen. Dies kann natürlich zu einem Problem
führen. Befindet sich die DLL in einem Verzeichnis, das dem System über die Path-Variable
bekannt ist, und man möchte diese DLL einbinden, so wird über alle 4 vorherigen Orte gesucht. Dabei besteht das Risiko, dass eine DLL mit gleichen Namen (vielleicht sogar eine alte
Version der DLL) in einem Verzeichnis mit höherer Suchpriorität liegt. Bei Inkompatibilität
zwischen der DLL und der Applikation kann dies zu einem Absturz der Applikation führen.
Der gängige Weg ist es, daher die DLL neben die Applikation zu legen, da dieser Pfad die
Suchpriorität 1 hat.
In diesem Konzept konnte auf die Variante der impliziten Bindung zurückgegriffen werden.
Für die S-Function ist eine flexible Handhabung der DLL, wie sie die explizie Bindung ermöglicht, nicht notwendig.
4.2.2 Implementierung der S-Function
Für die S-Function wurde die Vorlage von Mathworks verwendet.
Wie in Kapitel 2.2.1 beschrieben, muss für jeden Schritt innerhalb des Solvers eine Methode
in der S-Function vorhanden sein. Wird die Methode nicht benötigt, so bleibt die Funktionsdefinition leer.
Da die EshSim ein Modell mit einer festen Schrittweite verwendet, kann der als „Integration
(minor time step)“ beschriebene Teil in Abbildung 2-1 außer Acht gelassen werden. Es müssen nur die folgenden Methoden implementiert werden:

mdlInitializeSizes(SimStruct *S)

mdlStart(SimStruct *S)

mdlOutputs(SimStruct *S, int_T tid)
Die Funktion mdlInitializeSizes(…) initialisiert die S-Function. Sie beinhaltet die
Informationen über Anzahl der Eingänge, sowie Anzahl der Ausgänge des durch die SFunction beschriebenen Blockes. In dieser Funktion wird für jedes Ein- bzw. Ausgangssignal
wird der Datentyp und die Anzahl der Elemente festgelegt.
In der Funktion mdlStart(…) wird die Funktion EM_Initialize(…) der DLL aufgerufen. Liefert die Funktion EM_Initialize einen Wert ungleich 0 zurück, so ist davon aus15
zugehen, dass die vorliegende Version der DLL nicht kompatibel ist. In diesem Fall wird die
Funktion ssGetError(…) aus der S-Function API aufgerufen. Diese beendet die Simulation direkt. Der Anwender wird über die Simulink-internen Fehlerbehandlungen auf den Fehler
aufmerksam gemacht. Die Fehlermeldung kann in der S-Function parametriert werden.
In jedem Simulationsschritt wird die Funktion mdlOutputs(…) aufgerufen. Diese Methode
erhält von Simulink Zeiger auf die Ein- bzw. Ausgangssignale des Blockes und soll die Ausgangswerte des Blockes berechnen. Dazu wird die Funktion EM_Execute des Energiemanagers aufgerufen. Im ersten Zeitschritt wird zusätzlich die Konfiguration des Reglers vorgenommen, indem die Funktion EM_SetConfig aufgerufen wird.
Abbildung 4-1 Anbindung der DLL
Die Abbildung 4-1 zeigt, Verbindung der Simulation zum Energiemanager.
EM_Initialize(…)
wird
einmalig
zum
Simulationsstart
ausgeführt.
EM_SetConfig(…) verwendet ein Teil der Eingänge des Blockes und wird daher in der
mdlOutputs(…) der S-Function aufgerufen. Da sich die Konfiguration jedoch nicht während der Simulation ändert, wird diese Methode nur im ersten Zeitschritt aufgerufen. Jedoch
wird in jedem Zeitschritt die Methode EM_Execute von der S-Function aufgerufen. Diese
berechnet die Ausgangssignale des Blockes.
4.3 Einbettung in C#
Um den Energiemanager in C# einzubinden, muss auf die Technik des Platform Invokation
Services (P/Invoke) zurückgegriffen werden. P/Invoke ist ein Dienst, über den unmanaged
Code in C# eingebunden werden kann.
In diesem Kapitel wird zu nächst auf die allgemeine Einbindung einer unmanaged DLL in C#
eingegangen und wie die Übergabe von C#-String im Speziellen durchgeführt werden kann.
Danach wird die Einbettung des Energiemanagers in eine C#-Applikation behandelt.
16
4.3.1 Einbettung einer unmanaged DLL in C#
Abbildung 4-2 Aufruf einer unmanaged DLL aus C# [9]
Abbildung 4-2 beschreibt, wie der Aufruf einer unmanaged DLL aus C# aussieht. P/Invoke
regelt hierbei den Aufruf der Methode aus der unmanaged DLL. Der Standard marshaling
service (Marshaler) übernimmt die Analyse der Funktionsparameter. Es muss nämlich sichergestellt werden, dass ein Wert sowohl in der unmanaged DLL als auch in C# gleich interpretiert wird. Dieser Sachverhalt wird in Kapitel 4.3.2 am Beispiel von Strings näher erläutert.
Um eine unmanaged DLL zu verwenden, muss in C# der Namespace System.Runtime.InteropServices eingebunden werden, da dieser das Attribut
DLLImport enthält. Das Attribut DLLImport kann parametriert werden (vgl. Tabelle 4-1).
Parameter
Beschreibung
Public bool
Aktiviert oder deaktiviert das Best-Fit Mapping Verhalten bei der Konvertierung eines Unicode Zeichens in ein
ANSI Zeichen. Der Marshaller sucht nach der besten
Übereinstimmung falls das Zeichen nicht eindeutig abgebildet werden kann. Standardmäßig ist dieser Wert
auf true gesetzt.
BestFitMapping
Public CallingConvention Spezifiziert die Aufrufkonvention. Standardmäßig ist
das WinAPI, was bei 32-Bit Intel-basierten Plattformen
CallingConvention
__stdcall entspricht.
Public CharSet
CharSet
Gibt an wie das Marshaling von String-Argumenten der
Methode durchgeführt wird und kontrolliert das Name
Mangling.
17
Public string
EntryPoint
Public bool
ExactSpelling
Public bool
PreserveSig
Public bool
SetLastError
Public bool
ThrowOnUnmappableChar
Gibt den Namen des aufzurufenden DLL-Eintrittspunkts
(der Methode) an. Falls kein Argument übergeben wurde wird der Funktionsname verwendet.
Kontrolliert ob das DllImportAttribute.CharSet Feld
verursacht das die CLR nach einem anderen als dem
spezifizierten Namen als Eintrittspunkt sucht. Erlaubt
der CLR also, auf Basis des Wissens das die CLR über
Namenskonventionen hat, nach übereinstimmenden
Methoden mit leicht abweichenden Namen in der DLL
zu suchen.
Zeigt an ob die Signatur eine direkte Übersetzung des
unverwalteten Eintrittspunktes ist.
Wenn dieser Parameter auf true gesetzt ist, besteht die
Möglichkeit, Marshal-GetLastWin32 Error aufzurufen
und dadurch zu prüfen ob beim Aufruf der Methode ein
Fehler aufgetreten ist.
Aktiviert oder deaktiviert das Auslösen einer Exception
bei einem nicht einzuordnenden Unicode Zeichen das in
das ANSI Zeichen "?" konvertiert wird.
Public
Der Name der aufzurufenden DLL.
DLLImportAttribut(string
dllName)
Tabelle 4-1 Parametrierung eines DLLImports [9]
Für eine beispielhafte Funktion MyFunction(int a, int b) könnte eine Einbindung
in C# wie folgt aussehen:
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern void MyFunction(int a, int b);
4.3.2 C++ Strings in C#
Ruft man aus einer C#-Methode eine Funktion der C/C++-DLL auf, so muss sichergestellt
werden, dass die übergebenen Parameter in beiden Funktionen konsistent sind. So muss zum
Beispiel bei Integer-Werten klar sein, ob diese in beiden Programmiersprachen als „Little
Endian“ oder als „Big Endian“4 interpretiert werden. Bei den meisten Datentypen, insbesondere bei den primitiven Datentypen, übernimmt der Marshaler diese Aufgabe. Bei einigen
4
Little bzw. Big Endian gibt an, in welcher Reihenfolge die Bytes gespeichert werden. Bei Little Endian wird
das Byte mit der niedrigsten Stelle zuerst gespeichert; bei Big Endian das Byte mit der größten Stelle [13].
18
Datentypen, z.B. Objekten ist diese Interpretation nicht ohne eigenen Implementierungsaufwand möglich.
String ist kein primitiver Datentyp. Im Grunde sind Strings Felder von Zeichen. In C++ gibt
es verschiedene Implementierungen für Strings. Da es in der Programmiersprache C keine
Klassen gibt (C implementiert keine Objektorientierung), werden hier Strings als Felder (Arrays) von Zeichen (character) angelegt, auf die ein Zeiger (char *) verweist. Nun stellt sich
die Frage, wie man einen String in C# an einer Funktion in C übergeben kann, die keine
Strings kennt.
Diese Problematik kann über die Klasse StringBuilder in C# gelöst werden. Dazu muss
die Funktion in C folgende Definition haben:
// returns a C-String
void ReturnString( char* mystr,
size_t buffer_size)
{
strcpy_s(mystr, buffer_size, "This is just a test.");
}
Die Funktion erwartet also einen char* und die Länge des Bereichs, auf den der Zeiger deutet. Der erfahrene C-Entwickler wird bemerkt haben, dass diese Funktion die Methode
strcpy_s anstatt der strcpy-Methode verwendet. Das Visual Studio warnt vor der Verwendung von strcpy. Die Methode kann zu Speicherzugriffsfehlern führen, falls der zu kopierende String mehr Zeichen besitzt als das Ziel. Speicherzugriffsfehler können nicht von der
Fehlerbehandlung in C# gefangen werden [10].
Die Signatur in C# sieht wie folgt aus:
private unsafe static extern void ReturnString(StringBuilder author, UIntPtr length)
Die Methode wird wie folgt verwendet:
StringBuilder strb = new StringBuilder(255);
UIntPtr length = new UIntPtr((uint)strb.Capacity);
ReturnString(strb, length);
Console.WriteLine(strb.ToString());
Hierbei ist darauf zu achten, dass die Länge des StringBuilders mindestens der Länge des zu
übergebenden String entspricht. Ansonsten kommt es zu einer Exception.
4.3.3 Einbettung des Energiemanagers in C#
Für die Einbindung des Energiemanagers in C# wurden alle Funktionssignaturen der C#Applikation bekannt gemacht:
[DllImport("Energiemanager.dll", CallingConvention = CallingConvention.Cdecl, CharSet
= CharSet.Ansi)]
private unsafe static extern void GetVersion(short *m, short *s, short *b, short *i);
19
[DllImport("Energiemanager.dll", CallingConvention = CallingConvention.Cdecl, CharSet
= CharSet.Ansi)]
private unsafe static extern void GetDate(short* day, short* month, short* year);
[DllImport("Energiemanager.dll", CallingConvention = CallingConvention.Cdecl, CharSet
= CharSet.Ansi)]
private unsafe static extern void GetCompany(StringBuilder company, UIntPtr length);
[DllImport("Energiemanager.dll", CallingConvention = CallingConvention.Cdecl, CharSet
= CharSet.Ansi)]
private unsafe static extern void GetAuthor(StringBuilder author, UIntPtr length);
[DllImport("Energiemanager.dll", CallingConvention = CallingConvention.Cdecl, CharSet
= CharSet.Ansi)]
public unsafe static extern void EM_Initialize(...);
[DllImport("Energiemanager.dll", CallingConvention = CallingConvention.Cdecl, CharSet
= CharSet.Ansi)]
public unsafe static extern void EM_SetConfig(...);
[DllImport("Energiemanager.dll", CallingConvention = CallingConvention.Cdecl, CharSet
= CharSet.Ansi)]
public unsafe static extern void EM_Execute(...);
Die vollständige Implementierung kann dem Anhang entnommen werden.
20
5.
Test
5.1 Das Testmodell
Abbildung 5-1 Regelkreismodell
Abbildung 5-1 zeigt ein einfaches Regelkreismodell mit einem PI-Regler. Der Regler erhält
den Sollwert (w), den aktuellen Messwert (x), den Gewichtungsfaktor für den proportionalen
Anteil (Kp) und den Gewichtungsfaktor für den integralen Anteil (TI), so wie eine Beschränkung der Stellgröße (y) auf y_max und y_min. Der Regler wirkt auf das Prozessmodell, das in
diesem Fall durch eine einfache Differentialgleichung dargestellt werden kann. Im Modell
ändert sich dadurch die Regelgröße und der neue Stellwert muss berechnet werden.
Das Regelkreismodell aus Abbildung 5-1 könnte z.B. die Regelung eines Heizkörpers sein. In
diesem Fall stellt die Regelung das Thermostat der Heizung ein. Das Thermostat kann auf
beliebige Werte zwischen 0 und 5 gestellt werden.
Der Block „Regler“ in der Abbildung 5-1 ist Subsystem. Seine Implementierung kann der
Abbildung 5-2 entnommen werden.
21
Abbildung 5-2 Implementierung eines PI-Reglers
Abbildung 5-3 Plot des Regelkreises (Simulink)
Das Diagramm in Abbildung 5-3 zeigt die Veränderung des Sollwerts, der Regelgröße und
der Stellgröße im Zeitintervall [0, 10].
22
5.2 Die Implementierung der neuen Schnittstelle
In der Abbildung 5-1 wurde das Subsystem „Regler“ durch das in dieser Seminararbeit entwickelte Konzept ersetzt.
Abbildung 5-4 Implementierung des Reglers als S-Function
Ein Diagramm dieser Simulation lieferte das Resultat in Abbildung 5-5.
23
Abbildung 5-5 Plot des Regelkreises (S-Function)
Die Unterschiede zwischen Abbildung 5-3 und Abbildung 5-5 konnten auf unterschiedliche
numerische Integratoren zurückgeführt werden. Diese beeinflussen maßgeblich den TIParameter des PI-Reglers.
5.3 Laufzeitanalyse
Eine Anforderung an die Schnittstelle war die Laufzeitgeschwindigkeit. Die neue Schnittstelle
durfte die Ausführungszeit des Modells nicht erheblich vergrößern. Bei einem Test wurde das
Modell sowohl mit der Simulink-Implementierung als auch mit der Implementierung des neuen Konzeptes 1000-mal ausgeführt. Dabei wurde gezeigt, dass die neue Schnittstelle nicht
mehr Laufzeit in Anspruch nahm, sondern sogar weniger. Die Zeit konnte durchschnittlich
um ca. 40% gesenkt werden.
Abbildung 5-6 Zeitvergleich der beiden Implementierungen
24
6.
Fazit/Ergebnisse
Aufgabe war es, für das Softwareprodukt EshSim eine klar definierte Schnittstelle zwischen
der eigentlichen Simulation und der Regelung zu schaffen. Die Schwierigkeit bestand darin,
dass die Simulation in einer graphischen Programmiersprache (Simulink) implementiert wurde, die Regelung jedoch in einer textuellen Programmiersprache (C/C++) implementiert werden muss. Diese Seminararbeit legt das Konzept der Schnittstelle dar und beschreibt die technische Ausarbeitung dieser Schnittstelle. Ebenfalls wurde die praktische Umsetzung der
Schnittstelle getestet.
Änderungen an der Regelung können nun auch durch den Anwender getätigt werden. Des
Weiteren kann diese Regelung auch prototypisch an einer realen Energieversorgungsanlage
getestet werden. Hier stellt sich der Mehrwert des Produktes EshSim durch diese Seminararbeit da.
Zusätzlich zur Konzeption der Schnittstelle konnte in der praktischen Phase der Seminararbeit
bereits mit der Reimplementierung des aktuellen Energiemanagers aus dem Simulink-Modell
in eine C/C++-DLL begonnen werden. Die Implementierung wird in den folgenden Wochen
fertig gestellt, da sie die Ausgangslage der Bachelorarbeit ist.
7.
Ausblick Bachelorarbeit
Die aktuelle Regellogik besteht aus verschiedenen Reglern, die der Anwender in der Benutzeroberfläche parametrieren kann. Die Parametrierung stellt für viele Anwender eine Herausforderung dar, da sie nicht über die notwendigen reglungstechnischen Grundkenntnisse verfügen.
Im Rahmen der Bachelorarbeit soll ein „virtueller Idealregler“ entwickelt und implementiert
werden, der die Parametrierung der Regler überflüssig macht. Dieser „virtuelle Idealregler“
stellt zu jedem Zeitpunkt selbstständig das korrekte Betriebsverhalten ein, so wie es ein optimal eingestellter PID-Regler einer echten Anlage ebenfalls machen würde. Für den Anwender
entfällt damit die Notwendigkeit, die bisherigen Regler der Simulationsmodelle zu konfigurieren.
Der „virtueller Idealregler“ soll in einer C/C++ DLL implementiert werden und über die in
der Seminararbeit geschaffene Schnittstelle an das Simulinkmodell angedockt werden.
Schwerpunkt der Realisierung ist dabei, ein stabiles Betriebsverhalten des „virtuellen Idealreglers“ unter allen Randbedingungen (unterschiedliche Gebäude, unterschiedliche Anlagen,
unterschiedliche Medienflüsse, unterschiedliche Simulationsschrittweite, etc.) zu schaffen.
25
8.
8.1
Quellenverzeichnis
Literaturverzeichnis
[1] EUtech Scientific Engineering GmbH, Benutzerhandbuch EshSim, Aachen, 2011.
[2] „Dynamic Link Library,“ Wikipedia, 16. Oktober 2011. [Online]. Available:
http://de.wikipedia.org/wiki/Dynamic_Link_Library. [Zugriff am 15. November 2011].
[3] A. Voss, Vorlessungsskript IT-Systeme, Aachen, 2011.
[4] Microsoft, „Vorteile der Verwendung von DLLs,“ Microsoft, [Online]. Available:
http://msdn.microsoft.com/de-de/library/dtba4t8b%28v=vs.80%29.aspx. [Zugriff am 5.
Dezember 2011].
[5] K. Taubert, Theorie und Numerik von Differentialgleichungen mit MATLAB und
SIMULINK, Hamburg: Universität Hamburg, 2008.
[6] Mathworks, Developing S-Functions, Natick: Mathworks, Inc., 2011.
[7] M. Reuter und Z. Serge, Regelungstechnik für Ingenieure, Vieweg+Teubner Verlag,
2011.
[8] D. Kruglinski, Inside Visual C++ 6.0, Microsoft Press, 2005.
[9] CodePlanet, „P/Invoke Grundlagen,“ CodePlanet, 5. Mai 2006. [Online]. Available:
http://www.codeplanet.eu/tutorials/csharp/6-pinvoke-grundlagen.html. [Zugriff am 7.
Dezember 2011].
[10] Microsoft, „strcpy_s, wcscpy_s, _mbscpy_s,“ [Online]. Available:
http://msdn.microsoft.com/de-de/library/td1esda9%28v=vs.80%29.aspx. [Zugriff am 13.
Dezember 2011].
[11] F. Eller, Visual C# 2005, Addison-Wesley Verlag, 2006.
[12] ITWissen, „Aktor,“ [Online]. [Zugriff am 01. Dezember 2011].
[13] ITWissen, „Little-Endian-Format,“ [Online]. Available:
http://www.itwissen.info/definition/lexikon/little-endian-Little-Endian-Format.html.
[Zugriff am 14. November 2011].
8.2 Tabellenverzeichnis
Tabelle 2-1- Methodenübersicht S-Function.........................................................................8
Tabelle 4-1 Parametrierung eines DLLImports [9] .............................................................. 18
26
8.3 Abbildungsverzeichnis
Abbildung 2-1 Simulink Library Browser .............................................................................6
Abbildung 2-2 Ablauf einer Simulation [6] ...........................................................................7
Abbildung 2-3 Temperaturregelung ....................................................................................... 9
Abbildung 3-1 Aktuelle Implementierung der EshSim........................................................ 10
Abbildung 3-2 Konzept der Schnittstelle in der EshSim ..................................................... 10
Abbildung 3-3 Konzept der Schnittstelle mit einer realen Energieversorgungsanlage .......11
Abbildung 4-1 Anbindung der DLL..................................................................................... 16
Abbildung 4-2 Aufruf einer unmanaged DLL aus C# [9] .................................................... 17
Abbildung 5-1 Regelkreismodell ......................................................................................... 21
Abbildung 5-2 Implementierung eines PI-Reglers............................................................... 22
Abbildung 5-3 Plot des Regelkreises (Simulink) .................................................................22
Abbildung 5-4 Implementierung des Reglers als S-Function .............................................. 23
Abbildung 5-5 Plot des Regelkreises (S-Function) .............................................................. 24
Abbildung 5-6 Zeitvergleich der beiden Implementierungen .............................................. 24
27
9.
Eidesstaatliche Erklärung
Hiermit versichere ich, dass ich die Seminararbeit mit dem Thema Entwicklung und Implementierung einer Schnittstelle zwischen Prozessmodellen in Simulink und in C / C++ implementierten Steuerungen/Regelungen selbstständig verfasst und keine anderen als die angegebenen Quellen und Hilfsmittel benutzt habe, alle Ausführungen, die anderen Schriften wörtlich oder sinngemäß entnommen wurden, kenntlich gemacht sind und die Arbeit in gleicher
oder ähnlicher Fassung noch nicht Bestandteil einer Studien- oder Prüfungsleistung war.
Name:
Aachen, den
Unterschrift der Studentin / des Studenten
28
10. Anhang
10.1 Energiemanager.h
#ifndef _ENERGIEMANAGER_H_
#define _ENERGIEMANGER_H_
#include <iostream>
extern "C"
{
typedef struct
{
double Kp;
double TI;
double y_max;
double y_min;
} SConfiguration;
// returns the current version of the DLL
__declspec(dllexport) void GetVersion( short
short
short
short
*m,
*s,
*b,
*i);
// returns the last date the DLL has changed
__declspec(dllexport) void GetDate(
short *day,
short *month,
short *year);
// returns the DLL's author
__declspec(dllexport) void GetAuthor(
char* test,
size_t buffer_size);
// returns the DLL author's Company
__declspec(dllexport) void GetCompany( char* test,
size_t buffer_size);
// initializes the Energymanager
__declspec(dllexport) void EM_Initialize();
// sets the configuration for the Energymanager
__declspec(dllexport) void EM_SetConfig( double
double
double
double
Kp,
TI,
y_max,
y_min);
// executes one step of the controlling
__declspec(dllexport) void EM_Execute(
double
double
double
double
T,
w,
x,
*y);
}
#endif
29
10.2 Energiemanager.cpp
#include "Energiemanager.h"
#include "EM_Aux.h"
extern "C"
{
// static PID Members
// static configuration
SConfiguration
config;
CIntegral
integral;
double
prev_T;
// returns the current version of the DLL
void GetVersion(
short *m,
short *s,
short *b,
short *i)
{
*m = 1;
*s = 0;
*b = 0;
*i = 1;
}
void
// returns the last
GetDate(
short
short
short
date the DLL has changed
*day,
*month,
*year)
{
*day
=
27;
*month =
10;
*year = 2011;
}
// returns the DLL's author
void GetAuthor(
char* author,
size_t buffer_size)
{
strcpy_s(author, buffer_size, "Daniel Schnitzler");
}
// returns the DLL author's Company
void GetCompany(
char* company,
size_t buffer_size)
{
strcpy_s(company, buffer_size, "EUtech Scientific");
}
// initializes the Energymanager
void EM_Initialize()
{
integral.Initialize(0);
prev_T = 0;
}
void EM_SetConfig(
double
double
double
double
Kp,
TI,
y_max,
y_min)
{
config.Kp
= Kp;
30
config.TI
= TI;
config.y_max = y_max;
config.y_min = y_min;
}
// executes one step of the controlling
void EM_Execute(
double T,
double w,
double x,
double *y)
{
double e
= w - x;
double delta_T
= T - prev_T;
double integ
= integral.Int(e, delta_T);
double output
= config.Kp*(e + (config.TI * integ));
if
(output > config.y_max)
{ output = config.y_max; }
else if
(output < config.y_min)
{ output = config.y_min; }
y[0] = output;
}
}
10.3 EM_Aux.h
#ifndef _EM_AUX_H_
#define _EM_AUX_H_
///////////////////////////////////////////////////////////////////////////////
///
///
///
CIntegral
///
///
///
///////////////////////////////////////////////////////////////////////////////
class CIntegral
{
//Constructor and Destructor
public:
CIntegral();
virtual ~CIntegral();
// Variablen
public:
double pre_y_value;
double pre_i_value;
// Methoden: Initialisierung und Hilfsmethoden
public:
void Initialize(double int_value);
double Int( double y, double Ts);
double getValue();
};
#endif
31
10.4 EM_Aux.cpp
#include "EM_Aux.h"
CIntegral::CIntegral()
{
pre_y_value = 0;
pre_i_value = 0;
}
CIntegral::~CIntegral()
{
}
void CIntegral::Initialize(double int_value)
{
pre_y_value = 0;
pre_i_value = int_value;
}
double CIntegral::Int(
double y,
double Ts)
{
// Variables
double integral_value;
// Operation
integral_value = pre_i_value + Ts * pre_y_value + Ts * (pre_y_value+y)/2.0;
//Store values and results to memories
pre_y_value = y;
pre_i_value = integral_value;
return integral_value;
}
double CIntegral::getValue()
{
return pre_i_value;
}
32
10.5 S-Function Energiemanager_SFun.c
//========================================================================
// 1. Standard defines and includes
//========================================================================
#define S_FUNCTION_NAME Energiemanager_SFun
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
// include
simstruc.h for the definition of the SimStruct and its associated macro definitions
//========================================================================
// DLL import
//========================================================================
extern __declspec(dllimport)
extern __declspec(dllimport)
void
void
EM_Initialize();
EM_SetConfig( double Kp,
void
EM_Execute(
double TI,
double y_max,
double y_min);
extern __declspec(dllimport)
double T,
double w,
double x,
double *y);
//========================================================================
// Parameters, constants, global variables, etc.
//========================================================================
//========================================================================
// S-function methods
//========================================================================
/* Function: mdlInitializeSizes ===============================================
* Abstract:
*
The sizes information is used by Simulink to determine the S-function
*
block's characteristics (number of inputs, outputs, states, etc.).
*/
static void mdlInitializeSizes(SimStruct *S)
{
// Parameters
ssSetNumSFcnParams(S, 0);
// Expected number of parameters
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S))
{
return;
// Return
if number of expected parameter != number of actual parameters
}
// States
ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 0);
33
// Inputs: general definition
if (!ssSetNumInputPorts(S, 6)) return;
// Number of inputs
ssSetInputPortWidth
(S, 0, 1);
// w
ssSetInputPortDataType
(S, 0, SS_DOUBLE);
// float
ssSetInputPortRequiredContiguous(S, 0, true);
// direct input signal access
ssSetInputPortDirectFeedThrough (S, 0, 1);
// direct feedthrough required, i.e. the signal is used in mdlStart
and/or mdlUpdate
ssSetInputPortWidth
(S, 1, 1);
// x
ssSetInputPortDataType
(S, 1, SS_DOUBLE);
// float
ssSetInputPortRequiredContiguous(S, 1, true);
// direct input signal access
ssSetInputPortDirectFeedThrough (S, 1, 1);
// direct feedthrough required, i.e. the signal is used in mdlStart
and/or mdlUpdate
ssSetInputPortWidth
(S, 2, 1);
// Kp
ssSetInputPortDataType
(S, 2, SS_DOUBLE);
// float
ssSetInputPortRequiredContiguous(S, 2, true);
// direct input signal access
ssSetInputPortDirectFeedThrough (S, 2, 1);
// direct feedthrough required, i.e. the signal is used in mdlStart
and/or mdlUpdate
ssSetInputPortWidth
(S, 3, 1);
// TI
ssSetInputPortDataType
(S, 3, SS_DOUBLE);
// float
ssSetInputPortRequiredContiguous(S, 3, true);
// direct input signal access
ssSetInputPortDirectFeedThrough (S, 3, 1);
// direct feedthrough required, i.e. the signal is used in mdlStart
and/or mdlUpdate
ssSetInputPortWidth
(S, 4, 1);
// y_max
ssSetInputPortDataType
(S, 4, SS_DOUBLE);
// float
ssSetInputPortRequiredContiguous(S, 4, true);
// direct input signal access
ssSetInputPortDirectFeedThrough (S, 4, 1);
// direct feedthrough required, i.e. the signal is used in mdlStart
and/or mdlUpdate
ssSetInputPortWidth
(S, 5, 1);
// y_min
ssSetInputPortDataType
(S, 5, SS_DOUBLE);
// float
ssSetInputPortRequiredContiguous(S, 5, true);
// direct input signal access
ssSetInputPortDirectFeedThrough (S, 5, 1);
// direct feedthrough required, i.e. the signal is used in mdlStart
and/or mdlUpdate
// Outputs: general definitions
34
if (!ssSetNumOutputPorts(S, 1)) return;
// Number of outputs
ssSetOutputPortWidth (S, 0, 1);
// y
ssSetOutputPortDataType
(S, 0, SS_DOUBLE);
// float (32 bit)
// Miscellaneous
ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
// no "static" real
ssSetNumIWork(S, 0);
// no "static" integers
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
// No mexFunctions and no problematic mxFunctions are called so the exception free
code option safely gives faster simulations.
// => Don't use "mexErrMsgTxt(...)"
//
If an error must be indicated, "ssSetErrorStatus(S,msg)" shall be used!
ssSetOptions(S,SS_OPTION_EXCEPTION_FREE_CODE |
// Increase performance - NO code throwing exceptions may be used!!
SS_OPTION_DISCRETE_VALUED_OUTPUT);
// Only
discrete outputs are used
}
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
*
This function is used to specify the sample time(s) for your
*
S-function. You must register the same number of sample times as
*
specified in ssSetNumSampleTimes.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
// Inherit
the sample time
ssSetOffsetTime(S, 0, FIXED_IN_MINOR_STEP_OFFSET);
// Do NOT
change in minor integration steps
//
ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
// Continuos sample time (since block has no inputs)
}
#define MDL_INITIALIZE_CONDITIONS
/* Change to #undef to remove function */
#if defined(MDL_INITIALIZE_CONDITIONS)
/* Function: mdlInitializeConditions ========================================
* Abstract:
*
In this function, you should initialize the continuous and discrete
*
states for your S-function block. The initial states are placed
*
in the state vector, ssGetContStates(S) or ssGetRealDiscStates(S).
*
You can also perform any other initialization activities that your
*
S-function may require. Note, this routine will be called at the
*
start of simulation and if it is present in an enabled subsystem
*
configured to reset states, it will be call when the enabled subsystem
*
restarts execution to reset the states.
*/
static void mdlInitializeConditions(SimStruct *S)
{
}
35
#endif /* MDL_INITIALIZE_CONDITIONS */
#define MDL_START /* Change to #undef to remove function */
#if defined(MDL_START)
/* Function: mdlStart =======================================================
* Abstract:
*
This function is called once at start of model execution. If you
*
have states that should be initialized once, this is the place
*
to do it.
*/
static void mdlStart(SimStruct *S)
{
// Variables: input data
// ... not necessary
// Variables: output data
// ... not necessary
// Operation
EM_Initialize();
}
#endif /*
MDL_START */
/* Function: mdlOutputs =======================================================
* Abstract:
*
In this function, you compute the outputs of your S-function
*
block. Generally outputs are placed in the output vector, ssGetY(S).
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
// Variables: input data
double *w
= (double *) ssGetInputPortSignal(S, 0);
double *x
= (double *) ssGetInputPortSignal(S, 1);
double *Kp
= (double *) ssGetInputPortSignal(S, 2);
double *TI
= (double *) ssGetInputPortSignal(S, 3);
double *y_max
= (double *) ssGetInputPortSignal(S, 4);
double *y_min
= (double *) ssGetInputPortSignal(S, 5);
//current Timestep
double T
= ssGetT(S);
// Variables: output data
double *y
= ssGetOutputPortSignal(S, 0);
// If T = 0 (=> First Timestep, we have to set the configuration)
if (T == 0)
EM_SetConfig(Kp[0], TI[0], y_max[0], y_min[0]);
// Operation
EM_Execute(T, w[0], x[0], y);
}
#define MDL_UPDATE /* Change to #undef to remove function */
#if defined(MDL_UPDATE)
/* Function: mdlUpdate ======================================================
* Abstract:
*
This function is called once for every major integration time step.
*
Discrete states are typically updated here, but this function is useful
*
for performing any tasks that should only take place once per
*
integration step.
*/
36
static void mdlUpdate(SimStruct *S, int_T tid)
{
}
#endif /* MDL_UPDATE */
#define MDL_DERIVATIVES /* Change to #undef to remove function */
#if defined(MDL_DERIVATIVES)
/* Function: mdlDerivatives =================================================
* Abstract:
*
In this function, you compute the S-function block's derivatives.
*
The derivatives are placed in the derivative vector, ssGetdX(S).
*/
static void mdlDerivatives(SimStruct *S)
{
}
#endif /* MDL_DERIVATIVES */
/* Function: mdlTerminate =====================================================
* Abstract:
*
In this function, you should perform any actions that are necessary
*
at the termination of a simulation. For example, if memory was
*
allocated in mdlStart, this is the place to free it.
*/
static void mdlTerminate(SimStruct *S)
{
}
//========================================================================
// Required S-function trailer
//========================================================================
#ifdef MATLAB_MEX_FILE
#include "simulink.c"
#else
#include "cg_sfun.h"
#endif
/* Is this file being compiled as a MEX-file? */
/* MEX-file interface mechanism */
/* Code generation registration function */
37
10.6 EnergieManagerWrapper.cs
using
using
using
using
System;
System.Collections.Generic;
System.Text;
System.Runtime.InteropServices;
namespace CEnergiemanager_TestApp
{
class EnergieManagerWrapper
{
[DllImport("Energiemanager.dll", CallingConvention =
CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private unsafe static extern void GetVersion(
short*
short*
short*
short*
m,
s,
b,
i);
[DllImport("Energiemanager.dll", CallingConvention =
CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private unsafe static extern void GetDate(
short* day,
short* month,
short* year);
[DllImport("Energiemanager.dll", CallingConvention =
CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private unsafe static extern void GetCompany(
StringBuilder
company,
UIntPtr length);
[DllImport("Energiemanager.dll", CallingConvention =
CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private unsafe static extern void GetAuthor(
StringBuilder
author,
UIntPtr length);
[DllImport("Energiemanager.dll", CallingConvention =
CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public unsafe static extern int EM_Initialize();
[DllImport("Energiemanager.dll", CallingConvention =
CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public unsafe static extern void EM_SetConfig( double
double
double
double
Kp,
TI,
y_max,
y_min);
[DllImport("Energiemanager.dll", CallingConvention =
CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public unsafe static extern void EM_Execute(
double T,
double w,
double x,
double* y);
public unsafe static string GetVersion()
{
short m;
short s;
short b;
short i;
GetVersion(&m, &s, &b, &i);
38
return m.ToString() + "." + s.ToString() + "." + b.ToString() +
"." + i.ToString();
}
public unsafe static string GetDate()
{
short day;
short month;
short year;
GetDate(&day, &month, &year);
return day.ToString() + "." + month.ToString() + "." +
year.ToString();
}
public static string GetCompany()
{
StringBuilder strb = new StringBuilder(255);
UIntPtr length = new UIntPtr((uint)strb.Capacity);
GetCompany(strb, length);
return strb.ToString();
}
public unsafe static string GetCompany2()
{
StringBuilder strb = new StringBuilder(255);
UIntPtr length = new UIntPtr((uint)strb.Capacity);
char* a = GetCompany2(strb, length);
return strb.ToString();
}
public static string GetAuthor()
{
StringBuilder strb = new StringBuilder(255);
UIntPtr length = new UIntPtr((uint)strb.Capacity);
GetAuthor(strb, length);
return strb.ToString();
}
}
}
10.7 Kompilierung unter 64bit mit Visual Studio Express Edition
Mit der Standard-Installation des Visual Studio Express Edition ist es nicht möglich, eine
64bit-Applikation zu erstellen. Der dafür notwendige Compiler, so wie die vorkompilierten
Header-Dateien werden nur in den kommerziellen Versionen des Visual Studios ausgeliefert.
Microsoft bietet für die Express Edition ein Software Development Kit (SDK) an, das den
Compiler so wie die vorkompilierten Header-Dateien nachinstalliert. Für das Visual Studio
2010 muss die Version 7.1 des SDKs installiert werden.
Danach muss im Konfigurations-Manager eine neue Projektmappenplattform erzeugt werden.
Den Konfigurations-Manager findet man unter „Erstellen“  „Konfigurations-Manager“. In
dem Konfigurations-Manager wird die neue Projektmappenplattform mit der Bezeichnung
„x64“ angelegt. Die Einstellungen können von der Projektmappenplattform „Win32“ übernommen werden.
39
Danach muss in den Projekt-Eigenschaften („Projekt“ -> „Eigenschaften“) der Compiler gewechselt werden. Dazu muss unter „Konfigurationseigenschaften“  „Allgemein“ in dem
Feld „Plattformtoolset“ die Option „Windows7.1 SDK“ gewählt werden.
10.8 Verwenden von unmanaged Code mit Visual Studio Express Edition
Legt man in der C++-Version des Visual Studio Express Edition ein neues Projekt an, so ist
dies bei den Standardeinstellungen ein Projekt für managed Code. Die Entscheidung, ob ein
Projekt in managed oder unmanaged Code implementiert werden soll, kann nur bei der Erstellung des Projekts angegeben werden.
Dazu muss über „Datei“  „Neues Projekt“ ein neues Projekt angelegt werden. Im folgenden
Dialog kann dann unter „Visual C++“  „Win32“ die Option “Win32-Projekt“ ausgewählt
werden. Bei den weiteren Fenstern ist es wichtig, die Option „Leeres Projekt“ unter „Zusätzliche Optionen“ zu aktivieren. Dadurch wird sichergestellt, dass keine .NET-spezifischen Dateien mit geladen werden.
40