diff --git a/images/actionlib_garage.png b/images/actionlib_garage.png new file mode 100644 index 0000000000000000000000000000000000000000..78503a74c9fa0e2210ee0bae6e5ca54a3bd788ab Binary files /dev/null and b/images/actionlib_garage.png differ diff --git a/literatur.bib b/literatur.bib index cf2867a7278cca6bf9d30a629d3a8f6fdca809e5..a0ddc5d4d0237b6d0f527ad8a3667e0d3ffc54fc 100644 --- a/literatur.bib +++ b/literatur.bib @@ -379,4 +379,14 @@ school = {Aalborg University}, } +@Misc{Actionlib, + author = {{Open Source Robotics Foundation Inc.}, {Huiwu Luo}}, + title = {Actionlib Detailed Description}, + year = {2021}, + publisher = {Open Source Robotics Foundation Inc.}, + ranking = {rank3}, + readstatus = {read}, + url = {http://wiki.ros.org/actionlib/DetailedDescription}, +} + @Comment{jabref-meta: databaseType:bibtex;} diff --git a/sections/ausfuehrung.tex b/sections/ausfuehrung.tex new file mode 100644 index 0000000000000000000000000000000000000000..8d1b4c3fb83faf56833f47269475992d97963fe3 --- /dev/null +++ b/sections/ausfuehrung.tex @@ -0,0 +1,43 @@ +\chapter{Ausführung} +\section{Integration mit actionlib} +Das hier verwendete Szenario entspricht einem Garagen-Tor, welches präemptiert werden kann. Der implementierte Action-Server nimmt einen Boolean entgegen, der festlegt, ob das Tor geöffnet oder geschlossen werden soll, und gibt dann zufällig Feedback in Form eines Integers zurück, welcher die aktuelle Position des Tores repräsentiert. Dabei steht der Wert 0 für ein geschlossenes sowie 1000 für ein offenes Tor. Nach Abschluss des aktuellen Vorgangs wird der Status zurück zum Client übertragen. Dieser kann jedoch auch während der Ausführung das Wechseln des Ziels anfragen, woraufhin der Server das alte Ziel verwirft.\\ +Das entsprechend erstellte Uppaal-Modell ist in \cref{integration:uppaal} zu sehen. Auffällig dabei sind Kanten denen die Channel \textit{fertig} und \textit{position} zugewiesen sind. Bei diesen wird vom System die aktuelle Position übermittelt. Dazu wird eine zufällige Zahl zwischen 0 und 1000 ausgewählt und zur Position addiert beziehungsweise subtrahiert. Außerdem wird garantiert, dass die Grenzen des Wertebereiches nicht überschritten werden. +\begin{figure}[h] + \centering + \includegraphics[scale=.2]{./actionlib_garage.png} + \caption{Testmodell des Action-Servers} + \label{integration:uppaal} +\end{figure} +Der Einsatz des Adapters erfolgt unter Verwendung von eigens für die Nachrichtentypen implementierten Callbacks statt der Zuweisung von Bytes. Dadurch können andere Bestandteile des Action-Protokolls wie die Header vernachlässigt werden. Weiterhin ist zu beachten, dass im Adapter die Nachrichten verwendet werden, die \textbf{Action} im Namen tragen (z.B. TriggerActionGoal, nicht TriggerGoal). Diese Nachrichten beinhalten neben den Daten auch Header- und Statusinformationen und müssen verwendet werden, wenn der Adapter selbst weder Client noch Server des Systems ist, sondern ersteren nur emuliert oder die Kommunikation beider Parteien abhört. Eine Implementierung des Adapters, welcher selbst einen Client darstellt wäre allerdings auch möglich. Die Namen der Topics sind aus dem Namen des Servers (wird bei Konstruktion übergeben) und dem Inhalt der Nachricht zusammengesetzt und können daher ohne Probleme verwendet werden. Die (nicht vollständige) Konfigurationsphase für den Adapter des in \cref{integration:uppaal} gezeigten Modells sieht wie folgt aus: +\begin{lstlisting}[tabsize=1] + // oeffnen und schliessen stellt jeweils ein Ziel dar + Mapping map = Mapping("/garage_server/goal", "oeffnen", true); + map.input_callback = send_goal_open; + mappings.push_back(map); + + map = Mapping("/garage_server/goal", "schliessen", true); + map.input_callback = send_goal_close; + mappings.push_back(map); + + // wichtig ist, dass die Queue ausreichend gross ist, falls TRON + // viele Inputs sendet (hier relativ unwichtig) + input_publishers.push_back(nh.advertise<actionlib_example::TriggerActionGoal>("/garage_server/goal", 100)); + + // aktuelle Position wird per feedback zureckgegeben + // (wie bei result-Topic an Channel 'fertig') + map = Mapping("/garage_server/feedback", "position", false); + map.add_var_to_mapping("akt_position", 0); + mappings.push_back(map); + output_subscribers.push_back(nh.subscribe("/garage_server/feedback", 100, + feedback_callback)); +\end{lstlisting} +Die hier verwendeten Callbacks \textit{send\_goal\_open(/close)} rufen die Funktion \textit{send\_goal} auf: +\begin{lstlisting}[tabsize=1] + void send_goal (Mapping& map, int32_t* val, bool open){ + auto shared_ptr = boost::make_shared<actionlib_example::TriggerActionGoal>(); + shared_ptr->goal.open = open; + publish_to_topic<actionlib_example::TriggerActionGoal>(map.topic, shared_ptr); + } +\end{lstlisting} +Es lässt sich hinzufügen, dass beim Versenden eines Ziels (oder einer sonstigen Nachricht) vom (hier emulierten) Client keine Felder im Header wie zum Beispiel die Identifikationsnummer dieses Zieles gesetzt werden müssen, da der Server diese beim Empfangen der Nachricht entsprechend ausfüllt, sollten sie leer sein. Die Identifikationsnummer ist für den Client wichtig, um einen bestimmten Auftrag abzubrechen und sollte folglich in diesem Fall von ihm gesetzt werden. Die Funktion \textit{feedback\_callback} gibt lediglich die aktuelle Position an TRON weiter. Da im Uppaal-Modell keine Angaben zur Zeit vorhanden sind, kann die Zeiteinheit in diesem Fall frei gewählt werden. Der Test-Timeout sollte so gewählt sein, dass mehrere Zyklen durchlaufen werden können und ist daher abhängig von der Zeit, die der Server zum Bewältigen der Aufgabe benötigt. +Die vollständige Implementierung des actionlib-Servers und -Clients sowie des verwendeten TRON-Adapters sind in einem Git-Repository vorhanden (siehe Anhang). \ No newline at end of file diff --git a/sections/konzept.tex b/sections/konzept.tex index 6b3c137464fa4c9d8fc2a175b6081c80fdb3dd21..dd64e3c0d8464de2b7561c18d2d451cd93956477 100644 --- a/sections/konzept.tex +++ b/sections/konzept.tex @@ -49,7 +49,7 @@ Templates ermöglichen generische Callbacks mit denen den TRON-Variablen Positio Zur Verwendung des Adapters müssen primär Veränderungen in der Funktion \textit{configuration\_phase vorgenommen werden}. Die Struktur \textit{Mapping} beschreibt das Verhältnis von einem Channel zu einem Topic und muss daher für jedes gewünschte Paar initialisiert werden. Dabei ist (nach dem Namen des Topics und dem Namen des Channels) anzugeben, ob dieses Mapping als Eingabe oder Ausgabe dient. Soll ein Channel sowohl Eingabe als auch Ausgabe sein, oder sollen mehrere Topics einem Channel zugewiesen werden (oder umgekehrt), so müssen dafür zusätzliche Mappings erstellt werden. \\ Nach der Initialisierung können Variablen hinzugefügt werden. Parameter dafür sind in dieser Reihenfolge: der Name in Uppaal, der Offset (in Byte) von der letzten im Mapping vorhandenen Variable und schließlich optional Zeiger zu Konvertierungsfunktionen (von TRON zum Topic und umgekehrt). Letztere lassen sich verwenden, wenn Variablen des Topics nicht in dem von TRON verwendeten 32-Bit-Integer Format vorliegen oder Felder variabler Länge genutzt werden. Anzumerken ist hier, dass durch den Offset der Variable ausgehend von seinem Vorgänger die Variablen in der Reihenfolge angegeben werden müssen, in der sie innerhalb der ROS-Nachricht vorkommen. \\ Für Input-Channel muss zusätzlich ein Callback angegeben werden welcher als Parameter eine Referenz zu einem Mapping und ein Array von 32-Bit-Integern bekommt. Dieser sollte im Normalfall \textit{mapping\_callback\_to\_topic} sein, kann aber so wie die Konvertierungsfunktionen auch selbst implementiert werden. -\\ Schließlich muss das Mapping der Liste \textit{mappings} hinzugefügt werden. Zu beachten ist, dass für jedes Topic, welches im Rahmen von Input-Mappings verwendet wird ein entsprechender \textit{ros::Publisher} in der Liste \textit{input\_publishers} vorhanden sein muss. Analog dazu müssen \textit{ros::Subscriber} zu \textit{output\_subscriber} hinzugefügt werden, wobei beim Erstellen der Subscriber durch die ROS API die Callbacks für Nachrichten innerhalb der Topics hinzugefügt werden müssen. Wie bei den Input-Channels ist dafür eine generische Funktion vorhanden, \textit{mappings\_callback\_to\_TRON}, welche im Normalfall verwendet werden kann. +\\ Schließlich muss das Mapping der Liste \textit{mappings} hinzugefügt werden. Zu beachten ist, dass für jedes Topic, welches im Rahmen von Input-Mappings verwendet wird ein entsprechender \textit{ros::Publisher} in der Liste \textit{input\_publishers} vorhanden sein muss. Analog dazu müssen \textit{ros::Subscriber} zu \textit{output\_subscriber} hinzugefügt werden, wobei beim Erstellen der Subscriber durch die ROS API die Callbacks für Nachrichten innerhalb der Topics hinzugefügt werden müssen. Wie bei den Input-Channels ist dafür eine generische Funktion vorhanden, \textit{mappings\_callback\_to\_TRON}, welche bei einfachen Nachrichten verwendet werden kann. Anzumerken ist hierbei, dass diese Methode alle Channel informiert, die als Output mit dem Topic angegeben sind, während bei den Inputs jeweils eine Funktion im Mapping hinterlegt ist. \\ Neben den Mappings muss auch die Zeiteinheit sowie der Zeitraum für den getestet werden soll festgelegt werden, dies geschieht über einen Funktionsaufruf an \textit{set\_time\_unit\_and\_timeout}. \\ Für das in \cref{konzept:uppaal_tor} dargestellte Modell könnte die Konfigurierung etwa so aussehen: \begin{lstlisting}[tabsize=1] @@ -71,4 +71,10 @@ Zur Verwendung des Adapters müssen primär Veränderungen in der Funktion \text // 100000 Zeiteinheiten lang testen set_time_unit_and_timeout(1000, 100000); \end{lstlisting} -Der Channel \textit{position} sowie die Variable \textit{pos} sind in \cref{konzept:uppaal_tor} nicht dargestellt, werden hier aber verwendet um einen Output-Channel sowie das Übertragen einer Variable zu demonstrieren. \ No newline at end of file +Der Channel \textit{position} sowie die Variable \textit{pos} sind in \cref{konzept:uppaal_tor} nicht dargestellt, werden hier aber verwendet um einen Output-Channel sowie das Übertragen einer Variable zu demonstrieren.\\ +Wenn das Bestimmen der Positionen von Variablen innerhalb einer Nachricht schwierig ist, da diese beispielsweise aus vielen Feldern variabler Größen bestehen, so ist es sinnvoller eigene Callbacks zu implementieren, welche die von ROS erstellten Header-Dateien der Nachrichten zu nutzen, was gleichzeitig zu einer besseren Lesbarkeit des Codes beiträgt und damit die Wartung erleichtert. Dabei können diese Callbacks die Methode \textit{report\_now} verwenden, um TRON das Auslösen eines Channels zu signalisieren und Variablen zu übergeben. Auch hier ist zu erwähnen, dass die Übersetzung von einem Topic zu mehreren Channels in lediglich einer Methode stattfinden muss, die dem ROS-Subscriber als Parameter übergeben wird. +Diese Variante wird auch bei einer später im Text folgenden beispielhaften Anwendung des Adapters für die sogenannte \textit{actionlib} (siehe \cref{konzept:actionlib}) verwendet. + +\section{actionlib}\label{konzept:actionlib} +Die actionlib \cite{Actionlib} baut ein Protokoll auf den Nachrichten von ROS auf und ermöglicht Kommunikation von Nodes für das Ausführen einer bestimmten Aufgabe mithilfe einer Client-Server-Architektur. Diese unterscheidet sich von den \textit{ROS Services}\footnote{\url{http://wiki.ros.org/Services}} insbesondere durch die ermöglichte Asynchronität, welche für konstante Rückmeldung des Servers sowie weitere Anfragen des Clients (wie etwa nach der Präemptierung einer Aktion) genutzt wird. +Dazu wurde die Implementierung eines endlichen Automaten realisiert, was sich neben den verwendeten ROS-Nachrichten für das Testen mittels TRON anbietet. Daher wird im Folgenden die Integration des Testadapters in ein System, welches die actionlib verwendet, beispielhaft vollzogen. \ No newline at end of file diff --git a/thesis.tex b/thesis.tex index 84a2f8f29edf37871f7a95f10e9f738c7a6fae00..109261379234eef84573d4f55077a7e47fc19cfe 100644 --- a/thesis.tex +++ b/thesis.tex @@ -126,6 +126,7 @@ \input{sections/einleitung.tex} \input{sections/grundlagen.tex} \input{sections/konzept.tex} +\input{sections/ausfuehrung.tex} \printbibliography[heading=bibintoc]\label{sec:bibliography}%