From 5ec8da9df81dab4cb2f9f06c4664b3de725bc23b Mon Sep 17 00:00:00 2001 From: Jan Philipp Timme Date: Tue, 11 Oct 2016 23:01:04 +0200 Subject: [PATCH] [TASK] Fix bad boxes --- Bachelorarbeit.tex | 72 +++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/Bachelorarbeit.tex b/Bachelorarbeit.tex index cd0a927..6f96828 100644 --- a/Bachelorarbeit.tex +++ b/Bachelorarbeit.tex @@ -93,7 +93,7 @@ \includegraphics[width=0.2\textwidth]{res/Wortmarke_WI_schwarz.pdf} { ~ \sffamily \vfill - {\Huge\bfseries Complex Event Processing auf RDF-Da\-ten\-strö\-men mit C-SPARQL} + {\Huge\bfseries Complex Event Processing auf RDF-\\Da\-ten\-strö\-men mit C-SPARQL} \bigskip {\Large Jan Philipp Timme @@ -168,14 +168,14 @@ In Abbildung~\ref{fig:aggregation_patternmatching} wird die Aggregation von Erei Die Integration von \emph{Domänenwissen}\footnote{Hintergrundwissen für den Kontext der Ereignisverarbeitung, verändert sich während der Verarbeitung nur selten} ist ein weiterer Schritt, der die Brücke zwischen den aus komplexen Ereignissen gewonnenen Kenntnissen und bereits bekannten Fakten schlagen soll, um die gewonnenen Kenntnisse in einen eindeutigen Zusammenhang stellen und eine eindeutige Interpretation zu ermöglichen. -Um unter diesen Bedingungen viele Ereignisdatenströme mit hochfrequenten Ereignissen in nahezu Echtzeit zu verarbeiten ist \emph{CEP}\footnote{Complex-Event-Processing} das Mittel der Wahl: Mit CEP werden die Ereignisse der verschiedenen Datenströme für begrenzte Zeiträume im Speicher vorgehalten und innerhalb von sogenannten \emph{Sliding-Windows}\footnote{Mehr dazu in Kapitel~\ref{cpt:cep_intro}} betrachtet. Dabei können Ereignismuster erkannt werden und verschiedene Ereignisse aggregiert werden um neue komplexe Ereignisse zu erzeugen. +Um unter diesen Bedingungen viele Ereignisdatenströme mit hochfrequenten Ereignissen in nahezu Echtzeit zu verarbeiten ist \emph{CEP}\footnote{Complex-Event-Processing} das Mittel der Wahl: Mit CEP werden die Ereignisse der verschiedenen Datenströme für begrenzte Zeiträume im Speicher vorgehalten und innerhalb von sogenannten \emph{Sliding Windows}\footnote{Mehr dazu in Kapitel~\ref{cpt:cep_intro}} betrachtet. Dabei können Ereignismuster erkannt werden und verschiedene Ereignisse aggregiert werden um neue komplexe Ereignisse zu erzeugen. Ziel dieser Arbeit ist die Einführung in die Konzepte von CEP und RDF, sowie die Demonstration der praktischen Nutzung der CEP-Engine \enquote{C-SPARQL} zur Verarbeitung von RDF-Datenströmen am Beispiel einer Autoverleihgesellschaft zur Überwachung von Leihfahrzeugen. Auch soll ergründet werden, welche technischen Möglichkeiten existieren, um \emph{Reasoning} auf RDF-Datenströmen zu betreiben --- ein Prozess, durch den eine vorhandene Sammlung von Fakten auf Basis von vorgegebener Terminologie automatisch um daraus ableitbarem Wissen angereichert werden kann. Diesbezüglich soll ergründet werden, welche CEP-Engines Reasoning bereits implementieren und wie weit ihre technischen Möglichkeiten reichen --- eine große Herausforderung, da die mit einzubeziehenden Ereignisdaten sich kontinuierlich verändern. \section{Szenario}\label{cpt:scenario} -Das Beispielszenario, welches für diese Arbeit verwendet wird, ist eine Autoverleihgesellschaft, die ihren Fuhrpark überwachen möchte, um ihren Kunden vergünstigte Ta\-ri\-fe für verschleißarmes Fahrverhalten anbieten zu können. Weiterhin soll auf plötzlich auftretende Probleme an den Leihwagen möglichst schnell reagiert werden können um Schäden zu begrenzen, gefährliche Situationen zu vermeiden und bei Bedarf dem Kunden unverzüglich einen Ersatzwagen oder weitere Serviceleistungen anbieten zu können. +Das Beispielszenario, welches für diese Arbeit verwendet wird, beinhaltet eine Autoverleihgesellschaft, die ihren Fuhrpark überwachen möchte, um ihren Kunden vergünstigte Ta\-ri\-fe für verschleißarmes Fahrverhalten anbieten zu können. Weiterhin soll auf plötzlich auftretende Probleme an den Leihwagen möglichst schnell reagiert werden können um Schäden zu begrenzen, gefährliche Situationen zu vermeiden und bei Bedarf dem Kunden unverzüglich einen Ersatzwagen oder weitere Serviceleistungen anbieten zu können. \paragraph{Anforderungen} Um die Ziele der Autoverleihgesellschaft erreichen zu können, sollen folgende Situationen erkannt werden: @@ -393,7 +393,7 @@ Um Complex Event Processing durchführen zu können, wird eine CEP-Engine benöt \paragraph{Ereignisse} Im Rahmen von Complex Event Processing werden Ereignisdaten als Instanzen von Ereignistypen betrachtet. Während ein Ereignistyp für eine bestimmte Klasse von Vorkommnissen grundlegende Eigenschaften und Attribute definiert, re\-prä\-sen\-tiert eine Ereignisinstanz ein konkretes Ereignis dieses Ereignistypen\cite{hsh:cep}. Diese trägt neben inhaltlichen Informationen über den Vorgang durch den sie ausgelöst wurde auch eine eindeutige ID sowie einen Zeitstempel mit sich. Während der Zeitstempel den Zeitpunkt der Ereignisauslösung angibt, dient die ID zur eindeutigen Abgrenzung von anderen Ereignissen, die vom selben Ereignistyp sind oder zum selben Zeitpunkt entstanden sind. Da es bedingt durch Übertragunglatenz und weitere technische Randbedingungen möglich ist, dass die Ereignisdaten zeitverzögert bei der CEP-Engine ankommen, wird der Zeitstempel ebenfalls benötigt, um die zeitlichen Beziehungen zwischen den Ereignissen zu erhalten. -Jedes Ereignis trägt abhängig von seinem Ereignistypen eine geringe Menge von Daten mit sich, die für das Ereignis spezifische Informationen enthalten. Dies können beispielsweise Daten von Sensoren, Angaben über eine Benutzersitzung oder Statusdaten eines Systems sein. Diese Daten sind jedoch nur \emph{Momentaufnahmen} und verlieren mit fortschreitender Zeit meist an Gültigkeit. Listing~\ref{lst:sample_abstract_car_status_event} zeigt beispielhaft eine Instanz des Ereignistypen \texttt{CarStatusEvent}. +Jedes Ereignis trägt abhängig von seinem Ereignistypen eine geringe Menge von Daten mit sich, die für das Ereignis spezifische Informationen enthalten. Dies können beispielsweise Daten von Sensoren, Angaben über eine Benutzersitzung oder Statusdaten eines Systems sein. Diese Daten sind jedoch nur \emph{Momentaufnahmen} und verlieren mit fortschreitender Zeit meist an Gültigkeit. Listing~\ref{lst:sample_abstract_car_status_event} zeigt beispielhaft eine Instanz des Ereignistypen \texttt{Car\allowbreak Status\allowbreak Event}. \begin{lstlisting}[caption={Exemplarische Statusmeldung eines PKW in abstrakter Notation},label={lst:sample_abstract_car_status_event}] CarStatusEvent(ID=2342, timestamp=1344829400, relatedCarID=11, speed=63) @@ -547,7 +547,7 @@ Sehr häufig werden die Auswertungsergebnisse einer CEP-Regel als höherwertiges \begin{lstlisting} new CarAccelerationEvent(carID=eventA.pkwID, speed=eventC.speed-eventB.speed) \end{lstlisting} -Um den Anstoß eines externen Dienstes beziehungsweise das Ausführen von Programmcode zu notieren, wird ein Methodenaufruf notiert, der zur Verdeutlichung mit \texttt{call} beginnt und Parameter übergeben bekommen kann. Der Aufruf eines \texttt{PushMessageService}, der einen Parameter \texttt{msg} erwartet, könnte wie folgt aussehen: +Um den Anstoß eines externen Dienstes beziehungsweise das Ausführen von Programmcode zu notieren, wird ein Methodenaufruf notiert, der zur Verdeutlichung mit \texttt{call} beginnt und Parameter übergeben bekommen kann. Der Aufruf eines \texttt{Push\allowbreak Message\allowbreak Service}, der einen Parameter \texttt{msg} erwartet, könnte wie folgt aussehen: \begin{lstlisting} callPushMessageService(msg="PKW mit ID"+eventA.pkwID+" hat ein Problem gemeldet.") \end{lstlisting} @@ -596,12 +596,12 @@ Da in dieser Arbeit die Verarbeitung von RDF-Ereignisdatenströmen anhand einer \end{itemize} Die Bewertung dieser Kriterien ist für EP-SPARQL\footnote{Quellen über Etalis/EP-SPARQL: \cite{ep:unified} und \cite{ep:etalis}}, CQELS\footnote{Paper über CQELS: \cite{cqels:native} und \cite{cqels:stream}} und C-SPARQL\footnote{Erläuternde Werke über C-SPARQL: \cite{barbieri:csparql},\cite{barbieri:reasoning} und \cite{barbieri:querying}} wie folgt ausgefallen: -\begin{tabular}{p{4.25cm}||p{3.3cm}|p{3.3cm}|p{3.3cm}} +\begin{tabular}{p{4.2cm}||p{3.3cm}|p{3.3cm}|p{3.3cm}} & EP-SPARQL & CQELS & C-SPARQL \\ \hline\hline -Dokumentation & Mehrere Paper erläutern Features (o) & Paper über Umsetzung eines Projekts und Performance (o) & Mehrere Paper mit Erläuterungen (+) \\ +Dokumentation & Mehrere Quellen er\-läu\-tern Features (o) & Dokumente über Umsetzung eines Projekts und Performance (o) & Mehrere Quellen mit Er\-läu\-te\-run\-gen (+) \\ \hline -Beispielcode & Archiviertes Projekt auf Google-Code \dots (o) & Projekt als VirtualBox (o) & Kommentiertes Beispielprojekt mit verschiedenen Szenarien (+) \\ +Beispielcode & Archiviertes Projekt auf Google-Code \dots (o) & Projekt als VirtualBox (o) & Kom\-men\-tier\-tes Bei\-spiel\-pro\-jekt mit ver\-schie\-de\-nen Szenarien (+) \\ \hline Programmiersprache & Prolog, Java (-) & Java (+) & Java (+) \\ \hline @@ -643,13 +643,13 @@ Wie aus Listing~\ref{lst:sample_abstract_event_data} zu erkennen ist, ist jede E (1344829405) event:325 carOnt:relatedCar car:0 . (1344829405) event:325 carOnt:speed 75 . \end{lstlisting} -Wie in Listing~\ref{lst:sample_event_rdf_quads} zu sehen ist, wurde die ID der Ereignisinstanzen zur Generierung der Subjekt-URI verwendet, die in den Quadrupeln verwendet werden, um die Ereignisse zu beschreiben. Aus dem Ereignistyp wurde eine Aussage, die die Subjekt-URI via \texttt{rdf:type} mit einer RDFS-Objektklasse verknüpft, die diesen Ereignistypen repräsentieren soll. Alle weiteren Attribute der Ereignisinstanzen wurden über eigens hierfür definierte RDFS-Prädikate dem Ereignissubjekt zugeordnet. Die Angabe des PKW, auf den sich die Ereignisinstanzen beziehen, wurde durch eine direkte Verknüpfung der Ereignissubjekte mit dem zugehörigen PKW-Subjekt modelliert. Die PKW-Subjekte sind hierbei im Domänenwissen hinterlegt; dazu später mehr in Kapitel~\ref{cpt:integrating_knowledge}. +Wie in Listing~\ref{lst:sample_event_rdf_quads} zu sehen ist, wurde die ID der Ereignisinstanzen zur Generierung der Subjekt-URI verwendet, die in den Quadrupeln verwendet werden, um die Ereignisse zu beschreiben. Aus dem Ereignistyp wurde eine Aussage, die die Subjekt-URI via \texttt{rdf:type} mit einer RDFS-Objektklasse verknüpft, die diesen Ereignistypen repräsentieren soll. Alle weiteren Attribute der Ereignisinstanzen wurden über eigens hierfür definierte RDFS-Prädikate dem Ereignissubjekt zugeordnet. Die Angabe des PKW, auf den sich die Ereignisinstanzen beziehen, wurde durch eine direkte Ver\-knü\-pfung der Ereignissubjekte mit dem zugehörigen PKW-Subjekt modelliert. Die PKW-Subjekte sind hierbei im Domänenwissen hinterlegt; dazu später mehr in Kapitel~\ref{cpt:integrating_knowledge}. \section{C-SPARQL als Sprache für CEP-Regeln} Um die Ereignisdatenströme mit RDF-Quadrupeln nun in der C-SPARQL-Engine verarbeiten zu können, werden im Verarbeitungsprozess in C-SPARQL-Queries formulierte CEP-Regeln benötigt. Die Konstrukte und Fähigkeiten von C-SPARQL sollen in diesem Abschnitt erläutert werden. Weiterführend gibt es neben den Grundlagen zu SPARQL aus Kapitel~\ref{cpt:sparql_intro} noch ein Dokument der W3C\cite{w3c:sparql}. Eine detailliertere Erläuterung von C-SPARQL mit Beispielabfragen ist unter \cite{barbieri:csparql} nachzulesen. -Da C-SPARQL die Abfragesprache SPARQL lediglich erweitert, sind alle gültigen SPARQL-Abfragen automatisch auch gültige C-SPARQL-Abfragen\cite{barbieri:csparql}. Bevor nun die einzelnen Features von C-SPARQL erläutert werden, folgt nun ein ein gültiger Beispiel-Query, der als Grundlage der Erläuterungen dienen soll. Die Angabe von Prefixen mit der \texttt{PREFIX}-Klausel ist natürlich auch in C-SPARQL-Abfragen erforderlich, wird in diesem Abschnitt aber aus Gründen der Übersichtlichkeit weggelassen. +Da C-SPARQL die Abfragesprache SPARQL lediglich erweitert, sind alle gültigen Abfragen aus SPARQL automatisch auch gültige C-SPARQL-Abfragen\cite{barbieri:csparql}. Bevor nun die einzelnen Features von C-SPARQL erläutert werden, folgt nun ein ein gültiger Beispiel-Query, der als Grundlage der Erläuterungen dienen soll. Die Angabe von Prefixen mit der \texttt{PREFIX}-Klausel ist natürlich auch in C-SPARQL-Abfragen erforderlich, wird in diesem Abschnitt aber aus Gründen der Übersichtlichkeit weggelassen. \begin{lstlisting}[label={lst:example_csparql_query},caption={Beispielhafter C-SPARQL-Query}] REGISTER QUERY exampleQuery AS SELECT ?a ?c @@ -675,7 +675,7 @@ Vorerst werden C-SPARQL-Queries unter Angabe des Schlüsselwortes \texttt{QUERY} Somit wurde die in Listing~\ref{lst:example_csparql_query} angegebene Abfrage als \texttt{QUERY} unter dem Namen \enquote{exampleQuery} an der Engine registriert. \paragraph{Ereignisdatenströme in Sliding Windows} -Um mit der Verarbeitung von Ereignisdatenströmen beginnen zu können, müssen die für die Verarbeitung benötigten RDF-Datenströme in der CEP-Regel angegeben werden. Das Konstrukt \texttt{FROM [NAMED] STREAM <\emph{streamUri}> [\emph{window}]} wird zwischen den Klauseln \texttt{SELECT} und \texttt{WHERE} angegeben und definiert neben der in \texttt{streamUri} zu hinterlegenden URI des zu konsumierenden RDF-Datenstroms auch das an Stelle von \texttt{window} zu definierende Sliding Window, in dem dieser betrachtet werden soll\cite{barbieri:csparql}. Die Angabe des Schlüsselworts \texttt{NAMED} ist hierbei optional und funktioniert ähnlich wie in herkömmlichen SPARQL-Queries indem die Herkunft der einströmenden Tripel in Variablen vorgehalten wird, die über die \texttt{GRAPH}-Klausel verwendet werden können. +Um mit der Verarbeitung von Ereignisdatenströmen beginnen zu können, müssen die für die Verarbeitung benötigten RDF-Datenströme in der CEP-Regel angegeben werden. Zwischen den Klauseln \texttt{SELECT} und \texttt{WHERE} gibt das Konstrukt \texttt{FROM [NAMED] STREAM <\emph{streamUri}> [\emph{window}]} den zu verwendenden Ereignisdatenstrom an. Es definiert neben der in \texttt{streamUri} zu hinterlegenden URI des zu konsumierenden RDF-Datenstroms auch das an Stelle von \texttt{window} zu definierende Sliding Window, in dem dieser betrachtet werden soll\cite{barbieri:csparql}. Die Angabe des Schlüsselworts \texttt{NAMED} ist hierbei optional und funktioniert ähnlich wie in herkömmlichen SPARQL-Queries indem die Herkunft der einströmenden Tripel in Variablen vorgehalten wird, die über die \texttt{GRAPH}-Klausel verwendet werden können. Die Angabe des Sliding Windows ist zwingend erforderlich, da die kontinuierlich einströmenden Ereignisdaten zur Verarbeitung auf eine endliche Datenmenge reduziert werden müssen. Weiterhin ist die Spezifikation des Sliding Window sinnvoll, da je nach Anforderungen der CEP-Regel Ereignisse aus bestimmten Zeiträumen für die Verarbeitung interessant sind --- je nach Zweck der CEP-Regel können diese Zeiträume sich stark unterscheiden. @@ -720,7 +720,7 @@ Um nun innerhalb der \texttt{WHERE}-Klausel von C-SPARQL-Abfragen Ereignismuster \begin{lstlisting} PREFIX f: \end{lstlisting} -Die Funktion akzeptiert für jeden der drei Parameter neben klassischen Platzhaltern wie \texttt{?var}, auch die direkte Angabe von Werten in Form von URIs, sowohl in vollständiger Notation (\texttt{<\emph{uri}>}) und unter Nutzung von Prefixen (\texttt{\emph{prefix:example}}). Auch Literale können als Parameter angegeben werden\footnote{In der aktuell verwendeten Version 0.9.7 der C-SPARQL-Engine gibt es diesbezüglich einen Bug, sodass die Funktion für Tripel, die Literalwerte enhalten, keinen Zeitstempel zurückgibt.}. Ein Beispiel für die Nutzung dieser Funktion kann wie folgt aussehen: +Die Funktion akzeptiert für jeden der drei Parameter neben klassischen Platzhaltern wie \texttt{?var}, auch die direkte Angabe von Werten in Form von URIs sowohl in vollständiger Notation nach dem Schema \texttt{<\emph{uri}>} als auch unter der Nutzung von Prefixen nach dem Schema (\texttt{\emph{prefix}:example}). Auch Literale können als Parameter angegeben werden\footnote{In der aktuell verwendeten Version 0.9.7 der C-SPARQL-Engine gibt es diesbezüglich einen Bug, sodass die Funktion für Tripel, die Literalwerte enhalten, keinen Zeitstempel zurückgibt.}. Ein Beispiel für die Nutzung dieser Funktion kann wie folgt aussehen: \begin{lstlisting} f:timestamp(?s,rdf:type,) \end{lstlisting} @@ -728,7 +728,7 @@ f:timestamp(?s,rdf:type,) \begin{lstlisting} BIND(f:timestamp(?a,?b,?c) - f:timestamp(?x,?y,?z)) AS ?difference) \end{lstlisting} -\item \textbf{Eventuell vorhandene Tripel selektieren}: Manchmal kann man zur Zeit der Formulierung einer Abfrage nicht mit vollständiger Gewissheit eine Aussage darüber treffen, ob bestimmte Muster von Tripeln in den zur Verfügung stehenden Daten enthalten sind. In diesem Fall hilft der \texttt{OPTIONAL \{ ... \}}-Block weiter: Er erlaubt die Selektion von Tripeln, wobei der Fall, dass keine Tripel selektiert werden konnten keinen negativen Einfluss auf die restliche Abfrage hat. Möchte man beispielsweise das Tripelmuster \texttt{?a :b :c} selektieren und zusätzlich --- falls vorhanden --- die Tripel für das Muster \texttt{?d :e :f}, so kann dies in der \texttt{WHERE}-Klausel wie folgt notiert werden: +\item \textbf{Eventuell vorhandene Tripel selektieren}: Manchmal kann man zur Zeit der For\-mu\-lie\-rung einer Abfrage nicht mit vollständiger Gewissheit eine Aussage da\-rü\-ber treffen, ob bestimmte Muster von Tripeln in den zur Verfügung stehenden Daten enthalten sind. In diesem Fall hilft der \texttt{OPTIONAL \{ ... \}}-Block weiter: Er erlaubt die Selektion von Tripeln, wobei der Fall, dass keine Tripel selektiert werden konnten keinen negativen Einfluss auf die restliche Abfrage hat. Möchte man beispielsweise das Tripelmuster \texttt{?a :b :c} selektieren und zusätzlich --- falls vorhanden --- die Tripel für das Muster \texttt{?d :e :f}, so kann dies in der \texttt{WHERE}-Klausel wie folgt notiert werden: \begin{lstlisting} WHERE { ?a :b :c . @@ -754,7 +754,7 @@ WHERE { } \end{lstlisting} Das Ergebnis enthält dann nur noch Personen, die laut ihrer Altersangabe älter als 18 sind. -\item \textbf{Abwesenheit oder Präsenz von Tripeln erkennen}: Unter bestimmten Umständen kann es notwendig sein, die Abwesenheit von Tripeln als Kriterium für ein Ergebnis festzulegen. Hiefür kann der Block \texttt{FILTER NOT EXISTS \{ ... \}} eingesetzt werden. Ähnlich wie der \texttt{OPTIONAL}-Block nimmt er Tripelmuster entgegen. Jedoch stellen alle in ihm gefundenen Tripel das Ausschlusskriterium für die zu ihnen bezogene Tripel aus der restlichen Ergebnismenge dar. Ein Beispiel aus \cite{w3c:sparql}[Kapitel 8.1.1] zeigt dies sehr schön. Gegeben seien folgende Daten: +\item \textbf{Abwesenheit oder Präsenz von Tripeln erkennen}: Unter bestimmten Um\-stän\-den kann es notwendig sein, die Abwesenheit von Tripeln als Kriterium für ein Ergebnis festzulegen. Hiefür kann der Block \texttt{FILTER NOT EXISTS \{ ... \}} eingesetzt werden. Ähnlich wie der \texttt{OPTIONAL}-Block nimmt er Tripelmuster entgegen. Jedoch stellen alle in ihm gefundenen Tripel das Ausschlusskriterium für die zu ihnen bezogene Tripel aus der restlichen Ergebnismenge dar. Ein Beispiel aus \cite{w3c:sparql}[Kapitel 8.1.1] zeigt dies sehr schön. Gegeben seien folgende Daten: \begin{lstlisting} :alice rdf:type foaf:Person . :alice foaf:name "Alice" . @@ -807,7 +807,7 @@ WHERE { } \end{lstlisting} Das Ergebnis sind ausschließlich Tripel, in denen \emph{entweder} ein Ereignis vom Typ A \emph{oder} ein Ereignis vom Typ B vorkommt. -\item Der \textbf{Negationsoperator} ($\neg A$): Die Abbildung des Negationsoperators in C-SPARQL unterscheidet sich je nach Ereignismuster, in dem er verwendet wird. Um ein Muster vom Schema $(\neg A)$ abbilden zu können, kann eine Kombination aus \texttt{OPTIONAL}-Block und \texttt{FILTER}-Anweisung verwendet werden. Neben der Selektion aller verfügbaren Ereignistripel wird im \texttt{OPTIONAL +\item Der \textbf{Negationsoperator} ($\neg A$): In C-SPARQL unterscheidet sich die Abbildung des Negationsoperators je nach Ereignismuster, in dem er verwendet wird. Um ein Muster vom Schema $(\neg A)$ abbilden zu können, kann eine Kombination aus \texttt{OPTIONAL}-Block und \texttt{FILTER}-Anweisung verwendet werden. Neben der Selektion aller verfügbaren Ereignistripel wird im \texttt{OPTIONAL }-Block ein Tripel für den Ereignistypen A selektiert. Die auf den Block folgende \texttt{FILTER}-Anweisung filtert dann mit Hilfe von \texttt{BOUND()} nach dem Kriterium, ob das im \texttt{OPTIONAL}-Block selektierte Ereignistripel gefunden wurde oder nicht. Die entsprechende \texttt{WHERE}-Klausel dieser Abfrage sieht dann wie folgt aus: \begin{lstlisting} WHERE { @@ -872,14 +872,14 @@ Das Auslösen von Ereignissen innerhalb der Ereignisverarbeitung kann genutzt we In C-SPARQL können bestimmte Abfragen, wie etwa \texttt{CONSTRUCT}-Queries als Datenströme registriert werden, um somit ihre Ergebnisse in weiteren CEP-Regeln verarbeiten zu können. Hierzu wird nach dem Schlüsselwort \texttt{REGISTER}, wie die Grammatik aus Listing~\ref{lst:register_query_csparql} beschreibt, das Schlüsselwort \texttt{STREAM} angegeben\footnote{Zusätzlich ist es notwendig, den Query nach der Registrierung von einem \texttt{RDFStreamFormatter} beobachten zu lassen, den man separat als Stream an der Engine registrieren muss. Mehr dazu in Kapitel~\ref{cpt:csparql_in_practice}}. -Um dies zu zeigen, sei in Listing~\ref{lst:abstract_cep_rule_five} eine abstrakte CEP-Regel gegeben, die Ereignisse vom Typ \texttt{CarStatusEvent} nach den im Attribut \texttt{relatedCar} angegebenen PKW gruppiert, für jeden PKW dessen durchschnittliche Geschwindigkeit ermittelt und für jedes Ergebnis ein neues Ereignis vom Typ \texttt{AverageSpeedEvent} erzeugt, welches die Durchschnittsgeschwindigkeit enthalten soll. +Um dies zu zeigen, sei in Listing~\ref{lst:abstract_cep_rule_five} eine abstrakte CEP-Regel gegeben, die Ereignisse vom Typ \texttt{Car\allowbreak Status\allowbreak Event} nach den im Attribut \texttt{relatedCar} angegebenen PKW gruppiert, für jeden PKW dessen durchschnittliche Geschwindigkeit ermittelt und für jedes Ergebnis ein neues Ereignis vom Typ \texttt{AverageSpeedEvent} erzeugt, welches die Durchschnittsgeschwindigkeit enthalten soll. \begin{lstlisting}[mathescape=true,label={lst:abstract_cep_rule_five},caption={CEP-Regel erzeugt neue Ereignisse}] CONDITION ($(CarStatusEvent\ AS\ statusEvent)$)[WindowSize:15min,StepSize:5m] $\wedge$ AGGREGATE(statusEvent, "speed", {statusEvent.relatedCar}, AVG) AS avgSpeed ACTION new AverageSpeedEvent(relatedCar=statusEvent.relatedCar, averageSpeed=avgSpeed) \end{lstlisting} -Diese CEP-Regel lässt sich mit C-SPARQL umsetzen, indem eine entsprechende Abfrage mit dem Schlüsselwort \texttt{CONSTRUCT} formuliert wird, welche die Durchschnittsgeschwindigkeit der PKW ermittelt und daraus neue Ereignisinstanzen vom Typ \texttt{AverageSpeedEvent} generiert. Listing~\ref{lst:csparql_construct_query} zeigt diese C-SPARQL-Abfrage: +Diese CEP-Regel lässt sich mit C-SPARQL umsetzen, indem eine entsprechende Abfrage mit dem Schlüsselwort \texttt{CONSTRUCT} formuliert wird, welche die Durchschnittsgeschwindigkeit der PKW ermittelt und daraus neue Ereignisinstanzen vom Typ \texttt{Average\allowbreak Speed\allowbreak Event} generiert. Listing~\ref{lst:csparql_construct_query} zeigt diese C-SPARQL-Abfrage: \begin{lstlisting}[label={lst:csparql_construct_query},caption={Konstruktion eines Ereignisstromes mit C-SPARQL}] REGISTER STREAM getAverageSpeedByCar AS CONSTRUCT { @@ -903,7 +903,7 @@ WHERE { Da C-SPARQL die gleichzeitige Nutzung von \texttt{GROUP BY} mit \texttt{CONSTRUCT} erlaubt, nutzt die Abfrage aus Listing~\ref{lst:csparql_construct_query} einen in geschweiften Klammern eingefassten Subquery, um die Aggregation vornehmen zu können. Um für die innerhalb von \texttt{CONSTRUCT} neu zu erzeugten Ereignisinstanzen ein Subjekt zu erhalten, wird mit \texttt{[]} ein sogenannter \emph{Blank Node} verwendet. Innerhalb der \texttt{CONSTRUCT}-Anweisung wird nur das letzte Tripel mit einem Punkt beendet; die letzten beiden Tripel zeigen durch die Verwendung eines Semikolons an, dass sie das selbe Subjekt wie das erste Tripel für ihre Aussage verwenden. Somit haben alle drei der konstruierten Tripel das selbe Blank Node als Subjekt. Nach Auswertung des Queries erhält jeder Blank Node von der Engine eine Kennung, anhand welcher er identifiziert werden kann. \paragraph{Ausführen von Code und Anstoßen externer Dienste} -Natürlich bietet die C-SPARQL-Engine auch Möglichkeiten zum Ausführen von eigenem Code und somit auch zum Anstoßen von externen Diensten. Allerdings bietet sie dafür keine Möglichkeiten innerhalb der C-SPARQL-Sprache selbst, sondern erfordert das Anbringen von einem \texttt{Observer} an den durch die Registrierung eines Queries an der Engine entstandenen \texttt{CsparqlQueryResultProxy}. Dieser wird jedes Mal benachrichtigt, wenn für den registrierten Query ein Ergebnis vorliegt. Dabei wird die durch das \texttt{Observer}-Interface implementierte Methode \texttt{update()} aufgerufen, welcher der \texttt{CsparqlQueryResultProxy} des Queries sowie die \texttt{RDFTable} mit den Ergebnissen übergeben wird. Innerhalb der Methode \texttt{update()} kann dann beliebiger Java-Code hinterlegt werden, der mit den Ergebnisdaten arbeitet oder einen externen Dienst anstößt. +Die C-SPARQL-Engine bietet auch Möglichkeiten zum Ausführen von eigenem Code und somit auch zum Anstoßen von externen Diensten. Allerdings bietet sie dafür keine Möglichkeiten innerhalb der C-SPARQL-Sprache selbst, sondern erfordert das Anbringen von einem \texttt{Observer} an den durch die Registrierung eines Queries an der Engine entstandenen \texttt{Csparql\allowbreak Query\allowbreak Result\allowbreak Proxy}. Dieser wird jedes Mal benachrichtigt, wenn für den registrierten Query ein Ergebnis vorliegt. Dabei wird die durch das \texttt{Observer}-Interface implementierte Methode \texttt{update()} aufgerufen, welcher der \texttt{CsparqlQueryResultProxy} des Queries sowie die \texttt{RDFTable} mit den Ergebnissen übergeben wird. Innerhalb der Methode \texttt{update()} kann dann beliebiger Java-Code hinterlegt werden, der mit den Ergebnisdaten arbeitet oder einen externen Dienst anstößt. Somit kann von der CEP-Regel in Listing~\ref{lst:abstract_cep_rule_six} nur der \texttt{CONDITION}-Teil in C-SPARQL umgesetzt werden (Listing~\ref{lst:csparql_speedavg_greater_140}), da für den \texttt{ACTION}-Teil zusätzlicher Java-Code benötigt wird: \begin{lstlisting}[mathescape=true,label={lst:abstract_cep_rule_six},caption={CEP-Regel stößt externen Dienst an}] @@ -911,7 +911,9 @@ CONDITION ($(CarStatusEvent\ AS\ statusEvent)$)[WindowSize:15min,StepSize:5m] $\wedge$ AGGREGATE(statusEvent, "speed", {statusEvent.relatedCar}, AVG) AS avgSpeed $\wedge$ avgSpeed > 140 ACTION - callNotifyCustomerIsSpeedingService(relatedCar=statusEvent.relatedCar, averageSpeed=avgSpeed) + callNotifyCustomerIsSpeedingService( + relatedCar=statusEvent.relatedCar, + averageSpeed=avgSpeed) \end{lstlisting} \begin{lstlisting}[label={lst:csparql_speedavg_greater_140},caption={Fahrzeuge mit Durchschnittsgeschwindigkeit $>$ 140km/h}] @@ -931,7 +933,7 @@ Zur Einbindung von lokalem Domänenwissen in einem Query bietet C-SPARQL die Mö \begin{lstlisting} FROM \end{lstlisting} -Danach stehen die Daten aus dem Graphen im Kontext der Abfrage zur frei zur Verfügung. Der folgende C-SPARQL-Query nutzt Daten aus dem lokalen Domänenwissen, um für zu schnell fahrende PKW den aktuellen Fahrer zu ermitteln: +Danach stehen die Daten aus dem Graphen im Kontext der Abfrage zur frei zur Ver\-fü\-gung. Der folgende C-SPARQL-Query nutzt Daten aus dem lokalen Domänenwissen, um für zu schnell fahrende PKW den aktuellen Fahrer zu ermitteln: \begin{lstlisting}[label={lst:csparql_lookup_driver},caption={Zugriff auf lokales Domänenwissen aus Graph}] REGISTER QUERY getSpeedingCars AS SELECT ?driverName ?car (AVG(?speed) AS ?avgSpeed) @@ -980,7 +982,7 @@ Im Folgenden werden zunächst die beiden zu verarbeitenden RDF-Ereignisdatenstr \paragraph{PKW-Ereignisstrom} Dieser Ereignisdatenstrom übermittelt alle Ereignisse, die von den PKW aus dem Fuhrpark der Verleihgesellschaft ausgehen. Jedes einzelne Ereignis bezieht sich dabei auf einen konkreten PKW. Die folgenden Ereignistypen werden durch den Datenstrom übermittelt: \begin{itemize} -\item \textbf{\texttt{CarStatusEvent}}: Wird von jedem PKW in regelmäßigen Abständen ausgelöst und übermittelt ein Paket von Statuswerten. Neben dem Attribut \texttt{relatedCar}, welches auf den jeweiligen PKW verweist, werden die Attribute \texttt{motorOn}, \texttt{motorRPM}, \texttt{speed}, \texttt{handbrakeEngaged}, \texttt{locked} und \texttt{tirePressure\{1-4\}} für die Beschreibung der Statuswerte verwendet. Da dieses der Ereignistyp mit den meisten Attributen ist, wird im Folgenden ein Beispiel für eine solche Ereignisinstanz gezeigt: +\item \textbf{\texttt{Car\allowbreak Status\allowbreak Event}}: Wird von jedem PKW in regelmäßigen Abständen ausgelöst und übermittelt ein Paket von Statuswerten. Neben dem Attribut \texttt{relatedCar}, welches auf den jeweiligen PKW verweist, werden die Attribute \texttt{motorOn}, \texttt{motorRPM}, \texttt{speed}, \texttt{handbrakeEngaged}, \texttt{locked} und \texttt{tirePressure\{1-4\}} für die Beschreibung der Statuswerte verwendet. Da dieses der Ereignistyp mit den meisten Attributen ist, wird im Folgenden ein Beispiel für eine solche Ereignisinstanz gezeigt: \begin{lstlisting} :event rdf:type car:CarStatusEvent . :event car:relatedCar :someCar . @@ -995,7 +997,7 @@ Dieser Ereignisdatenstrom übermittelt alle Ereignisse, die von den PKW aus dem :event car:tirePressure4 29^^xsd:integer . \end{lstlisting} \item \textbf{\texttt{CarLockEvent} und \texttt{CarUnlockEvent}}: Sobald ein Fahrer den PKW auf- oder abschließt, wird jeweils ein \texttt{CarLockEvent} beziehungsweise \texttt{CarUnlockEvent} ausgelöst. Dieses enthält lediglich eine Angabe des betroffenen PKW über das Attribut \texttt{relatedCar}. -\item \textbf{\texttt{CarHandbrakeEngageEvent} und \texttt{CarHandbrakeReleaseEvent}}: Wird die Handbremse angezogen oder gelöst, so löst der PKW ein entsprechendes Ereignis vom Typ \texttt{CarHandbrakeEngageEvent} beziehungsweise \texttt{CarHandbrakeReleaseEvent} aus. Diese enthalten nur die Angabe des PKW über das Attribut \texttt{relatedCar}. +\item \textbf{\texttt{CarHandbrakeEngageEvent} und \texttt{Car\allowbreak Handbrake\allowbreak Release\allowbreak Event}}: Wird die Handbremse angezogen oder gelöst, so löst der PKW ein entsprechendes Ereignis vom Typ \texttt{CarHandbrakeEngageEvent} oder \texttt{Car\allowbreak Handbrake\allowbreak Release\allowbreak Event} aus. Diese enthalten nur die Angabe des PKW über das Attribut \texttt{relatedCar}. \item \textbf{\texttt{CarCheckEngineEvent}}: Sollte die Bordelektronik eines PKW eine Fehlermeldung auslösen, so wird diese mit einem \texttt{CarCheckEngineEvent} angezeigt. Hierbei wird der betroffene PKW über \texttt{relatedCar} angegeben. \item \textbf{\texttt{CarAirbagTriggeredEvent}}: Wurde an einem PKW der Airbag ausgelöst, so wird ein Ereignis dieses Typen ausgelöst, um auf diesen Umstand aufmerksam zu machen. Auch hier wird lediglich der PKW über \texttt{relatedCar} angegeben. \end{itemize} @@ -1003,7 +1005,7 @@ Dieser Ereignisdatenstrom übermittelt alle Ereignisse, die von den PKW aus dem \paragraph{Kundenereignisstrom} Die Ereignisse in diesem Datenstrom beziehen sich ausschließlich auf Interaktionen von Kunden mit den PKW. Somit werden durh diesen Ereignisdatenstrom nur Ereignisse dieser zwei Typen übertragen: \begin{itemize} -\item \texttt{CarTakenEvent}: Wird ausgelöst, wenn ein Kunde einen PKW ausleiht. Ein solches Ereignis verweist über \texttt{relatedCar} auf den geliehenen PKW und über \texttt{relatedUser} auf den entsprechenden Kunden. +\item \texttt{CarTakenEvent}: Wird ausgelöst, wenn ein Kunde einen PKW ausleiht. Ein solches Ereignis verweist über \texttt{relatedCar} auf den geliehenen PKW und über \texttt{related\allowbreak User} auf den entsprechenden Kunden. \item \texttt{CarReturnedEvent}: Analog zum Ereignistypen \texttt{CarTakenEvent} wird dieses Ereignis ausgelöst, wenn ein Kunde sein geliehenes Fahrzeug wieder zurückgibt. Ebenso wie das \texttt{CarTakenEvent} wird über \texttt{relatedCar} auf den entsprechenden PKW verwiesen und über \texttt{relatedUser} auf den Kunden. \end{itemize} @@ -1032,10 +1034,10 @@ Für die Umsetzung dieses Beispielszenarios befindet sich das lokale Domänenwis \section{Umsetzung der Anforderungen} Um diese Ereignisdatenströme nun zu Verarbeiten, werden die Anforderungen des Szenarios aus Kapitel~\ref{cpt:scenario} zunächst genauer betrachtet und CEP-Regeln für sie formuliert. -\subsection{Erkennung von Verschleiß am Fahrzeug} Um durch unsachgemäße Nutzung bedingten Verschleiß an den PKW feststellen zu können, sind Ereignisse vom Typ \texttt{CarStatusEvent} ein guter Einstiegspunkt. So lassen sich gleich mehrere Arten von Verschleiß hieraus ermitteln: +\subsection{Erkennung von Verschleiß am Fahrzeug} Um durch unsachgemäße Nutzung bedingten Verschleiß an den PKW feststellen zu können, sind Ereignisse vom Typ \texttt{Car\allowbreak Status\allowbreak Event} ein guter Einstiegspunkt. So lassen sich gleich mehrere Arten von Verschleiß hieraus ermitteln: \begin{itemize} \item \textbf{Verschleiß des Motors durch starkes Beschleunigen:} -Hierfür genügt es, zwei direkt aufeinanderfolgende \texttt{CarStatusEvent}s zu betrachten, die sich auf den selben PKW beziehen. Ermittelt man zwischen den beiden Ereignissen die Geschwindigkeits\-differenz und prüft, ob diese größer als ein gegebener Schwellwert (beispielsweise 25km/h) ist. Trifft dies zu, so wurde eine starke Beschleunigung erkannt. +Hierfür genügt es, zwei direkt aufeinanderfolgende \texttt{Car\allowbreak Status\allowbreak Event}s zu betrachten, die sich auf den selben PKW beziehen. Ermittelt man zwischen den beiden Ereignissen die Geschwin\-dig\-keits\-dif\-fe\-renz und prüft, ob diese größer als ein gegebener Schwellwert (beispielsweise 25km/h) ist. Trifft dies zu, so wurde eine starke Beschleunigung erkannt. Die hieraus resultierende, abstrakte CEP-Regel sieht wie folgt aus: \begin{lstlisting}[mathescape=true,label={},caption={}] CONDITION ($CarStatusEvent\ AS\ c1 \rightarrow\ CarStatusEvent\ AS\ c2$) @@ -1077,12 +1079,12 @@ WHERE { FILTER(?deltaSpeed > 25) } \end{lstlisting} -Diese Abfrage wird nun als Ereignisdatenstrom mit der URI \texttt{http://example.org/carSim/stream/getStronglyAcceleratingCars} an der Engine registriert, und kann dann innerhalb von weiteren C-SPARQL-Queries verwendet werden. +Diese Abfrage wird nun als Ereignisdatenstrom mit der URI \path{http://example.org/carSim/stream/getStronglyAcceleratingCars} an der Engine registriert, und kann dann innerhalb von weiteren C-SPARQL-Queries verwendet werden. \item \textbf{Verschleiß der Bremsen durch sehr starke Bremsmanöver:} Die Umsetzung hier ist sehr ähnlich zu dem Query aus Listing~\ref{lst:scenario_strong_acceleration}, da der einzige Unterschied die Beschleunigungsrichtung ist. Somit ist für die Erkennung lediglich die Vertauschung der beiden Geschwindigkeitsparameter bei der Differenzbildung notwendig. Am Ende der Auswertung wird dann ein Ereignis vom Typ \texttt{CarStrongBrakeEvent} gefeuert. -\item \textbf{Verschleiß des Motors durch Fahrt mit überhöhter Motordrehzahl:} Um die andauernd überhöhte Motordrehzahl eines PKW zu erkennen, müssen mehrere \texttt{CarStatusEvent}s für einen einzelnen PKW in einem Zeitfenster betrachtet werden und die durchschnittliche Motordrehzahl durch den Einsatz von Aggregationsfunktionen ermittelt werden. Dieser ermittelte Mittelwert muss nun mit den im Domänenwissen hinterlegten Daten für das Modell des PKW abgeglichen werden, um festzustellen, ob er sich noch in dem für den Betrieb zulässigen Bereich befindet. +\item \textbf{Verschleiß des Motors durch Fahrt mit überhöhter Motordrehzahl:} Um die andauernd überhöhte Motordrehzahl eines PKW zu erkennen, müssen mehrere \texttt{Car\allowbreak Status\allowbreak Event}s für einen einzelnen PKW in einem Zeitfenster betrachtet werden und die durchschnittliche Motordrehzahl durch den Einsatz von Aggregationsfunktionen ermittelt werden. Dieser ermittelte Mittelwert muss nun mit den im Domänenwissen hinterlegten Daten für das Modell des PKW abgeglichen werden, um festzustellen, ob er sich noch in dem für den Betrieb zulässigen Bereich befindet. Die folgende, abstrakte CEP-Regel stellt diesen Vorgang dar: \begin{lstlisting}[mathescape=true,label={},caption={}] CONDITION ($(CarStatusEvent\ AS\ statusEvent)$)[WindowSize:15s,StepSize:5s] @@ -1123,10 +1125,10 @@ WHERE { FILTER(?avgMotorRPM > ?maxMotorRPM) } \end{lstlisting} -Auch diese Abfrage muss später in der Engine als Datenstrom registriert werden, um innerhalb von weiteren Abfragen auf die Ergebnisse dieses Queries zugreifen zu können. Hierfür wird die URI \texttt{http://example.org/carSim/stream/getEngineWear} verwendet. +Auch diese Abfrage muss später in der Engine als Datenstrom registriert werden, um innerhalb von weiteren Abfragen auf die Ergebnisse dieses Queries zugreifen zu können. Hierfür wird die URI \path{http://example.org/carSim/stream/getEngineWear} verwendet. \item \textbf{Verschleiß der Handbremse durch Fahren mit angezogener Handbremse:} -Da das \texttt{CarStatusEvent} Informationen zur Momentangeschwindigkeit eines PKW und zum Status dessen Handbremse liefert, ist es völlig ausreichend, für alle Statusereignisse ein neues Verschleißereignis auszulösen, in denen die Geschwindigkeit bei angezogener Handbremse größer als null ist. Abstrakt formuliert sieht dies so aus: +Da das \texttt{Car\allowbreak Status\allowbreak Event} Informationen zur Momentangeschwindigkeit eines PKW und zum Status dessen Handbremse liefert, ist es völlig ausreichend, für alle Statusereignisse ein neues Verschleißereignis auszulösen, in denen die Geschwindigkeit bei angezogener Handbremse größer als null ist. Abstrakt formuliert sieht dies so aus: \begin{lstlisting}[mathescape=true,label={},caption={}] CONDITION ($(CarStatusEvent\ AS\ statusEvent)$)[WindowSize:3s,StepSize:1s] $\wedge$ statusEvent.handbrakeEngaged = true @@ -1155,7 +1157,7 @@ WHERE { FILTER(?handbrakeEngaged = true) } \end{lstlisting} -Registriert man diese Abfrage ebenfalls als Datenstrom an der Engine, so kann man auch auf die Ergebnisse dieser Abfrage aus anderen C-SPARQL-Queries heraus zugreifen, um diese weiter zu verarbeiten. Für diesen Datenstrom wird die URI \texttt{http://example.org/carSim/stream/getHandbrakeWear} verwendet. +Registriert man diese Abfrage ebenfalls als Datenstrom an der Engine, so kann man auch auf die Ergebnisse dieser Abfrage aus anderen C-SPARQL-Queries heraus zugreifen, um diese weiter zu verarbeiten. Für diesen Datenstrom wird die URI \path{http://example.org/carSim/stream/getHandbrakeWear} verwendet. \item \textbf{Verschleiß der Reifen durch Fahren mit zu niedrigem Reifendruck:} Das Verfahren hierfür ist vergleichsweise ähnlich zu der Erkennung von Verschleiß an der Handbremse. Allerdings muss hier zusätzlich auf das lokale Domänenwissen zugegriffen werden, um den aktuellen Druck der Reifen mit dem empfohlenen Reifendruckbereich zu vergleichen. Liegt der Druck eines Reifen nicht im empfohlenen Bereich, so soll ein \texttt{CarTireWearEvent} ausgelöst werden. Als CEP-Regel sieht dies so aus: @@ -1201,7 +1203,7 @@ WHERE { || ?tpress3 < ?minTirePressure || ?tpress4 < ?minTirePressure) } \end{lstlisting} -Auch dieser Query wird als Ereignisdatenstrom unter der URI \texttt{http://example.org/carSim/stream/getTireWear} zur weiteren Verarbeitung an der Engine registriert. +Auch dieser Query wird als Ereignisdatenstrom unter der URI \path{http://example.org/carSim/stream/getTireWear} zur weiteren Verarbeitung an der Engine registriert. \end{itemize} \paragraph{Tracking der Fahrer eines PKW} @@ -1382,13 +1384,17 @@ Beobachtet man die Ergebnisse dieses Queries, so erfasst man PKW, die aufgrund e Nachdem die C-SPARQL-Queries zur Umsetzung der Anforderungen des Beispielszenarios aus Kapitel~\ref{cpt:scenario} feststehen, muss nun das Szenario implementiert werden. Das CSPARQL-ReadyToGoPack\footnote{Zu beziehen unter \url{http://streamreasoning.org/resources/c-sparql}} diente hierbei als Referenz. Die für die C-SPARQL-Engine benötigten Abhängigkeiten werden dabei durch das Werkzeug Maven bezogen und bereitgestellt. \paragraph{Initialisierung der Engine}\label{cpt:csparql_domain_knowledge} -Zunächst wird eine Instanz der Engine benötigt. Diese muss dann initialisiert werden, wobei ein Parameter festlegt, ob die Funktion \texttt{f:timestamp(?s,?p?o)} zum Ermitteln der Zeitstempel von Tripeln aus Ereignisdatenströmen zur Verfügung stehen soll. +Zunächst wird eine Instanz der Engine benötigt. Diese muss dann initialisiert werden, wobei ein Parameter festlegt, ob die Funktion +\begin{lstlisting} +f:timestamp(?s,?p?o) +\end{lstlisting} +zum Ermitteln der Zeitstempel von Tripeln aus Ereignisdatenströmen zur Verfügung stehen soll. \begin{lstlisting} CsparqlEngine engine = new CsparqlEngineImpl(); engine.initialize(true); \end{lstlisting} -Als nächstes wird das lokale Domänenwissen aus einer RDF-Datei im Pfad \path{data/carSimulationAbox.rdf} geladen und in einem lokalen, benannten Graphen mit der URI \path{http://example.org/carSim/localDomainKnowledge} abgelegt: +Als nächstes wird das lokale Domänenwissen aus einer RDF-Datei im Pfad \texttt{data/\allowbreak car\allowbreak Simulation\allowbreak Abox.rdf} geladen und in einem lokalen, benannten Graphen mit der URI \path{http://example.org/carSim/localDomainKnowledge} abgelegt: \begin{lstlisting} engine.putStaticNamedModel( "http://example.org/carSim/localDomainKnowledge",