Skip to content
Snippets Groups Projects
Commit 47023ffb authored by Christoph Schröter's avatar Christoph Schröter
Browse files

No commit message

No commit message
parent 6d00bcbc
No related branches found
No related tags found
No related merge requests found
\chapter{Code der Adapter-Implementierung} \chapter{Header der Adapter-Implementierung}
\lstset{ \lstset{
escapeinside={(@*}{*@)} escapeinside={(@*}{*@)}
} }
......
...@@ -34,36 +34,43 @@ Zunächst wird der SocketAdapter auf eine eingehende Verbindung warten. Folglich ...@@ -34,36 +34,43 @@ Zunächst wird der SocketAdapter auf eine eingehende Verbindung warten. Folglich
Nach erfolgreicher Konfiguration sendet der Node ein Byte mit dem Wert 64 um den Start des Tests anzufragen, als Antwort erhält er ein Byte mit dem Wert 0. Folglich werden Inputs und Outputs ausgetauscht, dabei bestehen die ersten 4 Byte der Nachricht aus den Channel Identifiern, worauf 2 Byte mit der Anzahl an verknüpften Variablen und schließlich die Variablen (je 4 Byte) folgen. Diese müssen dann innerhalb der ROS-Topics verbreitet werden. Als Antwort wird von beiden Seiten eine Bestätigung zurückgesendet, Inputs und Outputs können jedoch asynchron übertragen werden. Nach erfolgreicher Konfiguration sendet der Node ein Byte mit dem Wert 64 um den Start des Tests anzufragen, als Antwort erhält er ein Byte mit dem Wert 0. Folglich werden Inputs und Outputs ausgetauscht, dabei bestehen die ersten 4 Byte der Nachricht aus den Channel Identifiern, worauf 2 Byte mit der Anzahl an verknüpften Variablen und schließlich die Variablen (je 4 Byte) folgen. Diese müssen dann innerhalb der ROS-Topics verbreitet werden. Als Antwort wird von beiden Seiten eine Bestätigung zurückgesendet, Inputs und Outputs können jedoch asynchron übertragen werden.
\subsection{Implementierung} \subsection{Implementierung}
Der implementierte Adapter verwendet vom Betriebssystem bereitgestellte Sockets für die Kommunikation mit TRON und hält globale Variablen, in welchen die Abbildungen von Channels auf Topics sowie die zugehörigen Variablen beschrieben werden. Die Verwendung dieser wird im nächsten Abschnitt dargestellt. Der implementierte Adapter verwendet vom Betriebssystem bereitgestellte Sockets für die Kommunikation mit TRON und hält relevante Variablen, in welchen die Abbildungen von Channels auf Topics sowie die zugehörigen Variablen beschrieben werden, innerhalb einer Klasse. Die Verwendung dieser wird im nächsten Abschnitt dargestellt.
Bei der Implementierung des Adapters (siehe Anhang) sind einige Unklarheiten im Handbuch zu TRON (\cite{TronManual}) aufgefallen. So werden die Bytecodes 64 (tatsächlich zum Starten verwendet) und 127 (verwendet zum Anfragen von Fehlermeldungen) vertauscht. Lokale Variablen sowie probabilistische Kanten wie in \cref{konzept:uppaal_tor} werden von TRON nicht unterstützt, im Handbuch allerdings auch nicht erwähnt. Außerdem ist bei der Übertragung der Zeit pro Zeiteinheit die Rede von zwei 32-Bit-Integer-Variablen, während TRON tatsächlich einen 64-Bit-Integer (ohne Vorzeichen) verwendet, um die Mikrosekunden einer Modell-Zeiteinheit festzulegen. Derartige Fehlinformationen sind vermutlich darauf zurückzuführen, dass Uppaal sowie TRON selbst Updates erhielten, ohne dass das Handbuch angepasst wurde.\\ Bei der Implementierung des Adapters (siehe Header-Datei im Anhang) sind einige Unklarheiten im Handbuch zu TRON (\cite{TronManual}) aufgefallen. So werden die Bytecodes 64 (tatsächlich zum Starten verwendet) und 127 (verwendet zum Anfragen von Fehlermeldungen) vertauscht. Lokale Variablen sowie probabilistische Kanten wie in \cref{konzept:uppaal_tor} werden von TRON nicht unterstützt, im Handbuch allerdings auch nicht erwähnt. Außerdem ist bei der Übertragung der Zeit pro Zeiteinheit die Rede von zwei 32-Bit-Integer-Variablen, während TRON tatsächlich einen 64-Bit-Integer (ohne Vorzeichen) verwendet, um die Mikrosekunden einer Modell-Zeiteinheit festzulegen. Derartige Fehlinformationen sind vermutlich darauf zurückzuführen, dass Uppaal sowie TRON selbst Updates erhielten, ohne dass das Handbuch angepasst wurde.\\
Der Adapter wurde möglichst generisch gehalten und ist somit für sämtliche Nachrichten die innerhalb von ROS Topics ausgetauscht werden verwendbar. Einem Channel des Modells lassen sich beliebig viele Topics zuweisen und umgekehrt. Der Adapter wurde möglichst generisch gehalten und ist somit für sämtliche Nachrichten die innerhalb von ROS Topics ausgetauscht werden verwendbar. Einem Channel des Modells lassen sich beliebig viele Topics zuweisen und umgekehrt.
Templates ermöglichen generische Callbacks mit denen den TRON-Variablen Positionen innerhalb der ROS-Nachrichten zugewiesen werden, es sind jedoch auch eigene Implementierungen zum Beispiel für Felder mit variabler Länge möglich, welche in den meisten Fällen zu empfehlen sind. Templates ermöglichen generische Callbacks mit denen den TRON-Variablen Positionen innerhalb der ROS-Nachrichten zugewiesen werden, es sind jedoch auch eigene Implementierungen zum Beispiel für Felder mit variabler Länge möglich, welche in den meisten Fällen zu empfehlen sind.
\subsubsection{Verwendung} \subsubsection{Verwendung}
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. Zur Verwendung des Adapters muss eine Instanz der Klasse \textit{TRON\_Adapter} erstellt werden. Ihr wird die Ziel IP-Adresse sowie der Port übergeben, über den eine bereits laufende TRON Instanz erreichbar ist. Die Struktur \textit{Mapping} beschreibt das Verhältnis von einem Channel zu einem Topic und muss daher für jedes gewünschte Paar mittels der Funktion \textit{createMapping} 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. \\ Nach der Initialisierung lassen sich Variablen hinzufügen. Dazu wird dem Adapter über \textit{add\_var\_to\_mapping} neben dem Mapping selbst der Name der Variablen in Uppaal übergeben. Optionale Parameter wie zum Beispiel der Offset können angegeben werden, wenn die Übersetzung mittels Byte-Positionen innerhalb der ROS-Nachricht stattfinden soll. Der Offset beginnt vom Ende des letzten Felder, so dass in diesem Fall die Variablen in der richtigen Reihenfolge angegeben werden müssen.
\\ 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. \\ Für Input-Channel ist zusätzlich ein Callback zu spezifizieren, welcher als Parameter eine Referenz zum entsprechenden Mapping und ein Array von 32-Bit-Integern, den von TRON übergebenen Variablen, bekommt. \textit{mapping\_callback\_to\_topic} kann dafür verwendet werden, und mit den festgelegten Byte-Positionen zu arbeiten. Output-Channel haben keinen separaten Callback, da dieser beim Abonnieren eines Topics durch die ROS API festgelegt wird. Wie bei den Input-Channel 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.
\\ 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. \\ 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.
\\ 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}. \\ 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: \\ Für das in \cref{konzept:uppaal_tor} dargestellte Modell könnte die Konfigurierung etwa so aussehen:
\begin{lstlisting}[tabsize=1] \begin{lstlisting}[tabsize=2]
// Input Channel "ausloesen" wird auf Topic "/command" abgebildet // Input Channel "ausloesen" wird auf Topic "/command" abgebildet
// es werden keine Variablen uebertragen // es werden keine Variablen uebertragen
Mapping map = Mapping("/command", "ausloesen", true); Mapping map = adapter.createMapping("/command", "ausloesen", true);
map.input_callback = mapping_callback_to_topic<std_msgs::Empty>; map.input_callback = boost::bind(&TRON_Adapter::mapping_callback_to_topic<std_msgs::Empty>, &adapter, _1, _2);
mappings.push_back(map); adapter.input_publishers.push_back(nh.advertise<std_msgs::Empty>("/command", 10));
input_publishers.push_back(nh.advertise<std_msgs::Empty>("/command", 1)); adapter.mappings.push_back(map);
// Output Channel "position" wird auf Topic "/position" abgebildet // Output Channel "position" wird auf Topic "/position" abgebildet
map = Mapping("/position", "position", false); map = adapter.createMapping("/position", "position", false);
// es wird eine Variable namens "pos" uebertragen, die sich bei Offset 0 (direkt am Anfang der Nachricht) befindet // es wird eine Variable namens "pos" uebertragen, die sich bei Offset 0 (direkt am Anfang der Nachricht) befindet
map.add_var_to_mapping("pos", 0); adapter.add_var_to_mapping(map, "akt_position", 0);
mappings.push_back(map); adapter.mappings.push_back(map);
output_subscribers.push_back(nh.subscribe("/position", 1, mappings_callback_to_TRON<std_msgs::Int32>)); adapter.output_subscribers.push_back(
nh.subscribe<std_msgs::Int32>("/position", 10,
// 1000 Mikrosekunden pro Uppaal Zeiteinheit // Callback als letztes Argument von ros::Nodehandle::subscribe
// 100000 Zeiteinheiten lang testen boost::function<void(const ros::MessageEvent<std_msgs::Int32>&)>(
set_time_unit_and_timeout(1000, 100000); boost::bind(&TRON_Adapter::mappings_callback_to_TRON<std_msgs::Int32>, &adapter, _1))));
// 1000 Mikrosekunden pro Uppaal Zeiteinheit
// 100000 Zeiteinheiten lang testen
adapter.set_time_unit_and_timeout(1000, 100000);
\end{lstlisting} \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.\\ 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. Ein besonderes Augenmerk liegt auf dem Callback des ros::Subscriber. Dieser benötigt nach dem boost::bind der Adapter-Instanz an die Funktion einen expliziten Cast zu einer boost::function des entsprechenden Typs, da der Compiler diesen nicht zuverlässig selbstständig ableiten kann und dennoch keinen Fehler ausgibt. Dies ist auf der zum Thema passenden Übersichtsseite von ROS\footnote{\url{http://wiki.ros.org/roscpp/Overview/Publishers\%20and\%20Subscribers}} in einer Notiz unter 2.3.3 angemerkt.
Diese Variante wird auch bei einer später im Text folgenden beispielhaften Anwendung des Adapters für die sogenannte \textit{actionlib} (siehe \cref{fallbeispiel}) verwendet. \\
In den meisten praktischen Fällen ist das verwenden eigens für bestimmte Nachrichten implementierter Callbacks sinnvoller, da ROS-Nachrichten sehr komplex sein können und viele Felder variabler Größe verwendet werden, wofür Konvertierungsfunktionen nötig wären. Außerdem wird dadurch die Lesbarkeit des Codes erhöht und folglich die Wartung erleichtert. Benutzerdefinierte Callbacks können \textit{report\_now} und \textit{publish\_to\_topic} nutzen um Nachrichten zu TRON beziehungsweise zu den Topics zu senden.
Auch hier ist zu erwähnen, dass die Übersetzung von einem Topic zu mehreren Channels innerhalb lediglich einer Methode stattfinden kann, die dem ROS-Subscriber als Parameter übergeben werden muss.
Diese Variante wird auch bei einer später im Text folgenden beispielhaften Anwendung des Adapters für die sogenannte \textit{actionlib} sowie im durchgeführten Testbeispiel (siehe \cref{fallbeispiel}) verwendet.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment