\section{Implementierung} \label{sec:demo} Anhand der in Kapitel \ref{sec:demoapp} vorgestellten Machbarkeitsstudie wird gezeigt, welche Schritte n\"otig sind, um eine bidirektionale Kommunikation zwischen einer in Mono entwickelten GTK\#\ Anwendung und einer Real-time CORBA Applikation aufzubauen. Des weiteren wird die Klasse \emph{CPX} vorgestellt, welche in allen Versuchen zum Zugriff auf die digitalen Ein- und Ausgabeports verwendet wurde. Es wird gezeigt, wie in ACE/TAO mittels MakeProjectGenerator Projekte verwaltet werden. \subsection{Zugriff auf die digitalen Ein- und Ausg\"ange der CPX} Es wurde eine Klasse implementiert, welche den Zugriff auf die digitalen Ein- und Ausg\"ange der CPX erleichtert. Bei Instanzierung der Klasse, wird ein Memory Mapping des Speicherbereichs der digitalen Ein- und Ausg\"ange auf Membervariablen durchgef\"uhrt und der Interrupt bei \"Anderungen an einem digitalen Eingang auf ein Signal gemapped. Eine setMethode besitzt als Parameter Wert und Port. Es wird der \"ubergebene Wert auf den entsprechenden Port geschrieben. Der getMethode wird als Parameter der Port mitgeteilt, welcher ausgelesen werden soll. Der aktuelle Wert des Portes wird von der Methode zur\"uckgeliefert. \begin{figure} \begin{center} \includegraphics[width=0.3\textwidth]{./img/cpx_classdia.jpg} \caption{UML Klassendiagramm: Zugriff auf digitale E/A Ports einer CPX} \end{center} \hrule \end{figure} \newpage \subsection{Projektverwaltung mit MakeProjectCreator} Zur Projektverwaltung wird in ACE/TAO der MakeProjectCreator (mpc) verwendet. Dieser ist in der ACE/TAO Distribution mit enthalten, denn das ACE/TAO Framework selbst verwendet den mpc zur Verwaltung der Quellen. In einer .mpc-Datei wird die Struktur eines Projektes definiert: \lstinputlisting{./cdrom/quellcode/versuch3/benchmark.mpc} \begin{description} \item[Zeile 1 und 13] In Klammer: Name des Projektes, hinter Doppelpunkt: Projekte von denen dieses Projekt abh\"angig ist (z.B. rt\_ server aus dem ACE/TAO Framework) \item[Zeile 2 und 14] zus\"atzlich ben\"otigte Features \item[Zeile 4 - 6 und 16 - 18] ben\"otigte Quelldateien, Reihenfolge definiert Abh\"angigkeiten \item[Zeile 9 und 21] zus\"atzlich ben\"otigte header Files \end{description} Mit dem Befehl: \begin{lstlisting} mpc.pl -type gnuace bench.mpc \end{lstlisting} werden aus der mpc-Datei GNUMakefiles generiert. Durch eine andere Parametrisierung k\"onnten auch andere, zum Beispiel Visual Studio, Projekte erstellt werden. Mit den Befehlen: \begin{lstlisting} make -f GNUMakefile.Benchmark_Receiver make -f GNUMakefile.Benchmark_Supplier \end{lstlisting} werden die ausf\"uhrbaren Dateien erstellt. \subsection{Kommunikation zwischen C\# und C++ mittels IIOP.NET und CORBA} \label{sec:impldemo1} \begin{figure} \includegraphics[width=\textwidth]{./img/demo1.jpg} \caption{UML Klassendiagramm: C\#\ - C++ Demoapplikation} \label{img:demo1uml} \end{figure} Es wird auf die Implementierung, Codierung und den Start der in Kapitel \ref{sec:demoapp} beschriebenen Anwendung eingegangen. Als Hilfe beim Erstellen dieser Anleitung diente \cite{egiiop} Die drei Applikationen Controller, Executor und Receiver wurden nach Abbildung \ref{img:demo1uml} entwickelt. Beschreibung der einzelnen Programme und den zugeh\"origen Klassen: \begin{description} \item[Controller] C\# Applikation mit GTK\# Oberfl\"ache zur Bedienung, der Benutzer kann das Versenden eines Kommandos an den Executor veranlassen \begin{description} \item[Main] instanziert MainWindow \item[MainWindow] instanziert CorbaHandler; implementiert die Funktionalit\"at der grafischen Oberfl\"ache \item[CorbaHandler] hostet ein CORBA Objekt mit einer Methode zur Darstellung eines Strings auf der grafischen Oberfl\"ache; baut die Verbindung zum ExecCmd Interface des Executors auf; bedient das ExecCmd Interface \item[Disp] implementiert das Display Interface \end{description} \item[Executor] C++ Applikation, empf\"angt Kommandos der grafischen Oberfl\"ache, kreiert und sendet entsprechende Prozessabbilder via RTCORBA an den Receiver \begin{description} \item[Executor] instanziert ORB und ExecCmd\_ i; bindet ExecCmd\_ i Objekt an ORB, registriert es bei einem Naming\_ Service \item[ExecCmd\_ i] stellt Verbindung zum Display Interface des Controllers her und bedient es; stellt Verbindung zum Put Interface des Receivers her und sendet die generierten Prozessabbilde in Echtzeit an dieses Interface \end{description} \item[Receiver] C++ Applikation, empf\"angt Prozessabbilder des Executors und setzt entsprechend den digitalen Ausgang. \begin{description} \item[Receiver] instanziert ORB und Put\_ i; bindet Put\_ i Objekt an ORB, registriert es bei einem Naming\_ Service \item[Put\_ i] instanziert CPX; stellt Funktionen zum Setzen des digitalen Ausgangs zur Verf\"ugung \item[CPX] erm\"oglicht mit Memory Mapping und set- und get-Funktionen einen komfortablen Zugriff auf die digitalen Ein- und Ausg\"ange der CPX \end{description} \end{description} \subsubsection{Codierung} \begin{enumerate} \item Ordnerstruktur anlegen: \begin{lstlisting} mkdir demo1 cd demo1 mkdir Executor mkdir Receiver \end{lstlisting} \item Receiver erstellen \begin{lstlisting} cd Receiver \end{lstlisting} \begin{enumerate} \item Das Interface Put wird in der Datei Receiver.idl definiert. Es besitzt Funktionen zum Verbindungsaufbau, Setzen aller digitalen Ausg\"ange und Setzen eines einzelnen digitalen Ausgangs: \lstinputlisting{./cdrom/quellcode/demo1/Receiver/Receiver.idl} \item Interface in C++ Code \"ubersetzen (Stub, Skeleton, Implementation und ServerTemplate). Da die Implementation editiert wird, empfiehlt es sich diese Datei umzubennen, damit bei einem erneuten IDL-Compiler Aufruf die \"Anderungen nicht verloren gehen. \begin{lstlisting} tao_idl -GI Receiver.idl mv ReceiverI.cpp Receiver_ i.cpp mv ReceiverI.h Receiver_ i.h \end{lstlisting} \item Die Dateien Receiver\_ i.cpp und Receiver\_ i.h repr\"asentieren das Objekt, welches vom Receiver zur Verf\"ugung gestellt wird. Es m\"ussen folgende Schritte durchgef\"uhrt werden: \begin{enumerate} \item Receiver\_ i.cpp: Include anpassen (ReceiverI.h in Receiver\_ i.h) \item Receiver\_ i.h: cpx.h inkludieren, private member cpx von Typ CPX* anlegen \item Receiver\_ i.cpp: im Konstruktor der Membervariable cpx eine neue Instanz von CPX zuweisen \item Receiver\_ i.cpp: im Destruktor Speicher der Membervariable cpx wieder freigeben \item Receiver\_ i.cpp: Code zum Port Setzen in Funktionen an der Stelle (\emph{\\your implementation here}) erg\"anzen \end{enumerate} \item MakeProjectCreator Projektbeschreibung Receiver.mpc anlegen: \lstinputlisting{./cdrom/quellcode/demo1/Receiver/Receiver.mpc} \item GNUMakefiles erzeugen: \begin{lstlisting} mpc.pl -type gnuace Receiver.mpc \end{lstlisting} \item Receiver bauen: \begin{lstlisting} make -f GNUMakefile.Receiver \end{lstlisting} \end{enumerate} \item Controller anlegen \begin{enumerate} \item monodevelop starten \item In Ordner \emph{demo1} ein neues GTK\#\ 2.0 Project \emph{Controller} anlegen. Da die Solution nur ein Projekt beinhalten wird, kein Unterverzeichnis f\"ur die Solution anlegen. \item Die automatisch generierte Datei MainWindow.cs in der Designer Ansicht \"offnen \item Eine grafische Oberfl\"ache mit den Komponenten und Bezeichnungen wie in Abbildung \ref{img:demo1GUIDesign} dargestellt erstellen \begin{figure}[!thbp] \includegraphics[width=0.8\textwidth]{./img/demo1GUI.jpg} \caption{Bedienoberfl\"ache} \label{img:demo1GUIDesign} \hrulefill \end{figure} \item Signale einrichten: entryHost - Changed, buttonConnect - Clicked, buttonSubmit - Clicked, comboBoxMode - Changed \item Der comboBoxMode folgende Items hinzuf\"ugen: Blink, Move, Flash \item In MainWindow.cs die Funktionalit\"at der Bedienoberfl\"ache implementieren: \lstinputlisting{./cdrom/quellcode/demo1/Controller/MainWindow.cs} \item Display Interface erstellen (Controller.idl): \lstinputlisting{./cdrom/quellcode/demo1/Controller/Controller.idl} \item Aus dem Interface eine dll erstellen, welche die Interfacefunktionalit\"aten in IIOP.NET zur Verf\"ugung stellt \begin{lstlisting} mono /opt/iiop.net/IDLToCLSCompiler/IDLCompiler/bin/IDLToCLSCompiler.exe Display Controller.idl \end{lstlisting} \item Aus dem Interface C++ Code erstellen (Stub, Skeleton, ServerTemplates) \begin{lstlisting} tao_idl Controller.idl \end{lstlisting} \item Display.dll und /opt/iiop.net/IIOPChannel/bin/IIOPChannel.dll mit Rechtsklick auf References, Edit References, .NET Assembly zum Projekt hinzuf\"ugen \end{enumerate} \item Executor implementieren \begin{lstlisting} cd ../Executor \end{lstlisting} \begin{enumerate} \item Interface ExecCmd in der Datei Executor.idl definieren. Die Funktion changeMode dient zum Wechseln des Modus (blink - 1, move - 2, flash - 3). Die Funktion setPorts setzt die Ausgansports der verbundenen CPX (wird momentan nicht verwendet, f\"ur Erweiterungen gedacht). \lstinputlisting{./cdrom/quellcode/demo1/Executor/Executor.idl} \item C++ Code aus dem Interface generieren (Stub, Skeleton, ServerTemplates und Implementation) \begin{lstlisting} tao_idl -GI Executor.idl mv ExecutorI.h Executor_i.h mv ExecutorI.cpp Executor_i.cpp \end{lstlisting} \item Include in Executor\_ i.cpp anpassen \item Folgende Includes in Executor\_ i.h hinzuf\"ugen \begin{lstlisting} #include "../Controller/ControllerC.h" #include "../Receiver/ReceiverC.h" \end{lstlisting} \item Die in Executor\_ i.h definierte Klasse Executor\_ ExecCmd\_ i um folgende privaten Membervariablen erweitern: \begin{lstlisting} Receiver::Put_var put; Controller::Display_var display; \end{lstlisting} \item In Executor\_ i.cpp folgende Includes hinzuf\"ugen \begin{lstlisting} #include "orbsvcs/CosNamingC.h" #include #include \end{lstlisting} \item Der Klasse Executor\_ ExecCmd\_ i einen weiteren Konstruktor hinzuf\"ugen: \begin{lstlisting} Executor_ExecCmd_i (int argc, char* argv[], CORBA::ORB_var orb); \end{lstlisting} \item Im soeben hinzugef\"ugten Konstruktor Referenzen auf Display und Receiver Objekte vom NamingService abrufen und in entsprechenden Membervariablen speichern: \begin{lstlisting} try{ // get RTORB CORBA::Object_var rtorb = orb->resolve_initial_references("RTORB"); RTCORBA::RTORB_var rtORB = RTCORBA::RTORB::_narrow(rtorb.in()); // NameService CORBA::Object_var namingObject = orb->resolve_initial_references("NameService"); CosNaming::NamingContext_var namingContext = CosNaming::NamingContext::_narrow(namingObject.in()); CosNaming::Name name(1); name.length(1); // Connect to Receiver name[0].id = CORBA::string_dup("Receiver"); CORBA::Object_var benchObj = namingContext->resolve(name); put = benchmark::Put::_narrow(benchObj.in()); // Connect to Display name.length(2); name[0].id = CORBA::string_dup("manut.Controller"); name[1].id = CORBA::string_dup("Disp"); benchObj = namingContext->resolve(name); display = Controller::Display::_narrow(benchObj.in()); display->show("ready!!!"); // Signal for ready2use put->connect(); put->allPorts(0, 0, 0); sleep(1); put->onePort(1, 255); sleep(1); put->allPorts(0, 0, 0); display->show("ready2use"); }catch(CORBA::Exception &e){ std::cerr<show("moving..."); std::cout<<"move"<allPorts(i,i,i); break; case 2: display->show("blinking..."); std::cout<<"blink"<allPorts(255,255,255); else put->allPorts(0,0,0); } break; case 3: display->show("flashing..."); std::cout<<"flash"<allPorts(255,255,255); else put->allPorts(0,0,0); } break; default: std::cerr<<"Mode not implemented"<allPorts(one, two, three); return true; \end{lstlisting} \item CORBA Server, welcher das ExecCmd Objekt hostet, in Executor.cpp implementieren: \lstinputlisting{./cdrom/quellcode/demo1/Executor/Executor.cpp} \item MakeProjectCreator Datei f\"ur den Executor erstellen: \lstinputlisting{./cdrom/quellcode/demo1/Executor/Executor.mpc} \item GNUMakefile erstellen und Executor bauen: \begin{lstlisting} mpc.pl -type gnuace Executor.mpc make -f GNUMakefile.Executor \end{lstlisting} \end{enumerate} \item C\# CorbaHandler implementieren \begin{lstlisting} cd ../Controller \end{lstlisting} \begin{enumerate} \item Executor.dll aus Executor.idl erstellen: \begin{lstlisting} mono /opt/iiop.net/IDLToCLSCompiler/IDLCompiler/bin/IDLToCLSCompiler Executor ../Executor/Executor.idl \end{lstlisting} \item In monodevelop Rechtsklick auf \emph{References}, \emph{add References}, \emph{.Net Assembly}, Executor.dll ausw\"ahlen und mit \emph{add} zur Solution hinzuf\"ugen \item Display Interface in der Datei Display.cs in die Klasse Disp implementieren. Die Funktion show stellt den \"ubergebenen String auf der grafischen Bedienoberfl\"ache dar: \lstinputlisting{./cdrom/quellcode/demo1/Controller/Display.cs} \item CorbaHandler.cs besitzt Funktionen zum Hosten des Display Objekts und zum Connecten zum ExecCmd Interface des Executors: \lstinputlisting{./cdrom/quellcode/demo1/Controller/CorbaHandler.cs} \item in monodevelop mit \emph{F8} Controller Solution \"ubersetzen \end{enumerate} \end{enumerate} \subsubsection{Start der verteilten Anwendung} Auf einer beliebigen CPX den NamingService starten: \begin{lstlisting} $TAO_ROOT/orbsvcs/Naming_Service/Naming_Service -ORBEndpoint iiop://:2809 \end{lstlisting} Auf CPX 1 Receiver starten: \begin{lstlisting} ./Receiver -ORBInitRef NameService=corbaloc:iiop::2809/NameService \end{lstlisting} Auf CPX 2 Controller starten: \begin{lstlisting} mono Controller.exe \end{lstlisting} Nun wird in der grafischen Oberfl\"ache im Feld Host die IP der CPX mit dem NamingService eingetragen und auf Connect geklickt. Wird auf Connect geklickt, ohne dass eine IP-Adresse eingegeben wurde, so wird der NamingService an der eincodierten IP-Adresse gesucht. Auf CPX 3 Executor starten: \begin{lstlisting} ./Executor -ORBInitRef NameService=corbaloc:iiop::2809/NameService \end{lstlisting} Dieser connected automatisch zum Display Interface des Controllers und signalisiert den Erfolg mit der Ausgabe von \emph{ready}. Durch einen erneuten Klick auf Connect im Controller wird die Verbindung zwischen Controller und ExecCmd Interface aufgebaut. Danach kann ein Mode ausgew\"ahlt werden und mit Submit entsprechend auf dem digitalen Ausgang von CPX 1 dargestellt werden.