diff options
-rw-r--r-- | doc/FAQ.html | 4 | ||||
-rw-r--r-- | doc/afb-application-writing.html | 56 | ||||
-rw-r--r-- | doc/afb-bindings-overview.html | 4 | ||||
-rw-r--r-- | doc/afb-bindings-writing.html | 176 | ||||
-rw-r--r-- | doc/afb-daemon-vocabulary.html | 4 | ||||
-rw-r--r-- | doc/afb-events-guide.html | 279 | ||||
-rw-r--r-- | doc/afb-events-guide.md | 449 | ||||
-rw-r--r-- | doc/afb-overview.html | 4 | ||||
-rw-r--r-- | doc/afb-tests-overview.html | 4 | ||||
-rw-r--r-- | doc/doc.css | 3 |
10 files changed, 879 insertions, 104 deletions
diff --git a/doc/FAQ.html b/doc/FAQ.html index 5cc8b653..f3584e0e 100644 --- a/doc/FAQ.html +++ b/doc/FAQ.html @@ -7,10 +7,10 @@ <meta name="author" content="José Bollo"> <title>Frequently Asked Question about AFB-DAEMON</title> <style type="text/css">code{white-space: pre;}</style> + <link rel="stylesheet" href="doc.css"> <!--[if lt IE 9]> - <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> + <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> <![endif]--> - <link rel="stylesheet" href="doc.css"> </head> <body> <header> diff --git a/doc/afb-application-writing.html b/doc/afb-application-writing.html index 12a2f13f..9d051d5e 100644 --- a/doc/afb-application-writing.html +++ b/doc/afb-application-writing.html @@ -8,29 +8,47 @@ <meta name="author" content="Fulup Ar Foll"> <title>HOWTO WRITE an APPLICATION above AGL FRAMEWORK</title> <style type="text/css">code{white-space: pre;}</style> - <!--[if lt IE 9]> - <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> - <![endif]--> <style type="text/css"> +div.sourceCode { overflow-x: auto; } table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode { margin: 0; padding: 0; vertical-align: baseline; border: none; } table.sourceCode { width: 100%; line-height: 100%; } td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; } td.sourceCode { padding-left: 5px; } -code > span.kw { color: #007020; font-weight: bold; } -code > span.dt { color: #902000; } -code > span.dv { color: #40a070; } -code > span.bn { color: #40a070; } -code > span.fl { color: #40a070; } -code > span.ch { color: #4070a0; } -code > span.st { color: #4070a0; } -code > span.co { color: #60a0b0; font-style: italic; } -code > span.ot { color: #007020; } -code > span.al { color: #ff0000; font-weight: bold; } -code > span.fu { color: #06287e; } -code > span.er { color: #ff0000; font-weight: bold; } +code > span.kw { color: #007020; font-weight: bold; } /* Keyword */ +code > span.dt { color: #902000; } /* DataType */ +code > span.dv { color: #40a070; } /* DecVal */ +code > span.bn { color: #40a070; } /* BaseN */ +code > span.fl { color: #40a070; } /* Float */ +code > span.ch { color: #4070a0; } /* Char */ +code > span.st { color: #4070a0; } /* String */ +code > span.co { color: #60a0b0; font-style: italic; } /* Comment */ +code > span.ot { color: #007020; } /* Other */ +code > span.al { color: #ff0000; font-weight: bold; } /* Alert */ +code > span.fu { color: #06287e; } /* Function */ +code > span.er { color: #ff0000; font-weight: bold; } /* Error */ +code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */ +code > span.cn { color: #880000; } /* Constant */ +code > span.sc { color: #4070a0; } /* SpecialChar */ +code > span.vs { color: #4070a0; } /* VerbatimString */ +code > span.ss { color: #bb6688; } /* SpecialString */ +code > span.im { } /* Import */ +code > span.va { color: #19177c; } /* Variable */ +code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */ +code > span.op { color: #666666; } /* Operator */ +code > span.bu { } /* BuiltIn */ +code > span.ex { } /* Extension */ +code > span.pp { color: #bc7a00; } /* Preprocessor */ +code > span.at { color: #7d9029; } /* Attribute */ +code > span.do { color: #ba2121; font-style: italic; } /* Documentation */ +code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */ +code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */ +code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */ </style> <link rel="stylesheet" href="doc.css"> + <!--[if lt IE 9]> + <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> + <![endif]--> </head> <body> <header> @@ -184,7 +202,7 @@ ON-REPLY 1:auth/check: {"jtype":"afb-reply","request&qu <p>This object contains at least 2 mandatory fields of name <strong>jtype</strong> and <strong>request</strong> and one optional field of name <strong>response</strong>.</p> <h3 id="template">Template</h3> <p>This is a template of replies:</p> -<pre class="sourceCode json"><code class="sourceCode json"><span class="fu">{</span> +<div class="sourceCode"><pre class="sourceCode json"><code class="sourceCode json"><span class="fu">{</span> <span class="dt">"jtype"</span><span class="fu">:</span> <span class="st">"afb-reply"</span><span class="fu">,</span> <span class="dt">"request"</span><span class="fu">:</span> <span class="fu">{</span> <span class="dt">"status"</span><span class="fu">:</span> <span class="st">"success"</span><span class="fu">,</span> @@ -194,7 +212,7 @@ ON-REPLY 1:auth/check: {"jtype":"afb-reply","request&qu <span class="dt">"reqid"</span><span class="fu">:</span> <span class="st">"application-generated-id-23456"</span> <span class="fu">},</span> <span class="dt">"response"</span><span class="fu">:</span> <span class="er">....any</span> <span class="er">response</span> <span class="er">object....</span> -<span class="fu">}</span></code></pre> +<span class="fu">}</span></code></pre></div> <h3 id="field-jtype">Field jtype</h3> <p>The field <strong>jtype</strong> must have a value of type string equal to <strong>"afb-reply"</strong>.</p> <h3 id="field-request">Field request</h3> @@ -216,11 +234,11 @@ ON-REPLY 1:auth/check: {"jtype":"afb-reply","request&qu <p>This object contains at least 2 mandatory fields of name <strong>jtype</strong> and <strong>event</strong> and one optional field of name <strong>data</strong>.</p> <h3 id="template-1">Template</h3> <p>Here is a template of event:</p> -<pre class="sourceCode json"><code class="sourceCode json"><span class="fu">{</span> +<div class="sourceCode"><pre class="sourceCode json"><code class="sourceCode json"><span class="fu">{</span> <span class="dt">"jtype"</span><span class="fu">:</span> <span class="st">"afb-event"</span><span class="fu">,</span> <span class="dt">"event"</span><span class="fu">:</span> <span class="st">"sample_api_name/sample_event_name"</span><span class="fu">,</span> <span class="dt">"data"</span><span class="fu">:</span> <span class="er">...any</span> <span class="er">event</span> <span class="er">data...</span> -<span class="fu">}</span></code></pre> +<span class="fu">}</span></code></pre></div> <h3 id="field-jtype-1">Field jtype</h3> <p>The field <strong>jtype</strong> must have a value of type string equal to <strong>"afb-event"</strong>.</p> <h3 id="field-event">Field event</h3> diff --git a/doc/afb-bindings-overview.html b/doc/afb-bindings-overview.html index b650860c..0ffc769a 100644 --- a/doc/afb-bindings-overview.html +++ b/doc/afb-bindings-overview.html @@ -7,10 +7,10 @@ <meta name="author" content="José Bollo"> <title>Overview of bindings shipped with AFB-Daemon</title> <style type="text/css">code{white-space: pre;}</style> + <link rel="stylesheet" href="doc.css"> <!--[if lt IE 9]> - <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> + <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> <![endif]--> - <link rel="stylesheet" href="doc.css"> </head> <body> <header> diff --git a/doc/afb-bindings-writing.html b/doc/afb-bindings-writing.html index b32912b6..ea03801f 100644 --- a/doc/afb-bindings-writing.html +++ b/doc/afb-bindings-writing.html @@ -7,35 +7,53 @@ <meta name="author" content="José Bollo"> <title>HOWTO WRITE a BINDING for AFB-DAEMON</title> <style type="text/css">code{white-space: pre;}</style> - <!--[if lt IE 9]> - <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> - <![endif]--> <style type="text/css"> +div.sourceCode { overflow-x: auto; } table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode { margin: 0; padding: 0; vertical-align: baseline; border: none; } table.sourceCode { width: 100%; line-height: 100%; } td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; } td.sourceCode { padding-left: 5px; } -code > span.kw { color: #007020; font-weight: bold; } -code > span.dt { color: #902000; } -code > span.dv { color: #40a070; } -code > span.bn { color: #40a070; } -code > span.fl { color: #40a070; } -code > span.ch { color: #4070a0; } -code > span.st { color: #4070a0; } -code > span.co { color: #60a0b0; font-style: italic; } -code > span.ot { color: #007020; } -code > span.al { color: #ff0000; font-weight: bold; } -code > span.fu { color: #06287e; } -code > span.er { color: #ff0000; font-weight: bold; } +code > span.kw { color: #007020; font-weight: bold; } /* Keyword */ +code > span.dt { color: #902000; } /* DataType */ +code > span.dv { color: #40a070; } /* DecVal */ +code > span.bn { color: #40a070; } /* BaseN */ +code > span.fl { color: #40a070; } /* Float */ +code > span.ch { color: #4070a0; } /* Char */ +code > span.st { color: #4070a0; } /* String */ +code > span.co { color: #60a0b0; font-style: italic; } /* Comment */ +code > span.ot { color: #007020; } /* Other */ +code > span.al { color: #ff0000; font-weight: bold; } /* Alert */ +code > span.fu { color: #06287e; } /* Function */ +code > span.er { color: #ff0000; font-weight: bold; } /* Error */ +code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */ +code > span.cn { color: #880000; } /* Constant */ +code > span.sc { color: #4070a0; } /* SpecialChar */ +code > span.vs { color: #4070a0; } /* VerbatimString */ +code > span.ss { color: #bb6688; } /* SpecialString */ +code > span.im { } /* Import */ +code > span.va { color: #19177c; } /* Variable */ +code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */ +code > span.op { color: #666666; } /* Operator */ +code > span.bu { } /* BuiltIn */ +code > span.ex { } /* Extension */ +code > span.pp { color: #bc7a00; } /* Preprocessor */ +code > span.at { color: #7d9029; } /* Attribute */ +code > span.do { color: #ba2121; font-style: italic; } /* Documentation */ +code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */ +code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */ +code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */ </style> <link rel="stylesheet" href="doc.css"> + <!--[if lt IE 9]> + <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> + <![endif]--> </head> <body> <header> <h1 class="title">HOWTO WRITE a BINDING for AFB-DAEMON</h1> <h2 class="author">José Bollo</h2> -<h3 class="date">24 juin 2016</h3> +<h3 class="date">27 juillet 2016</h3> </header> <nav id="TOC"> <ul> @@ -152,11 +170,11 @@ code > span.er { color: #ff0000; font-weight: bold; } </blockquote> <h2 id="header-files-to-include">Header files to include</h2> <p>Binding <em>tictactoe</em> has following includes:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="ot">#define _GNU_SOURCE</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="ot">#define _GNU_SOURCE</span> <span class="ot">#include <stdio.h></span> <span class="ot">#include <string.h></span> <span class="ot">#include <json-c/json.h></span> -<span class="ot">#include <afb/afb-binding.h></span></code></pre> +<span class="ot">#include <afb/afb-binding.h></span></code></pre></div> <p>Header <em>afb/afb-binding.h</em> is the only hard dependency, it includes all features that a binding MUST HAVE. Outside of includes used to support application logic, common external headers used within bindings are:</p> <ul> <li><em>json-c/json.h</em>: should be include to handle json objects;</li> @@ -196,7 +214,7 @@ code > span.er { color: #ff0000; font-weight: bold; } <p>It is a good practice, even for arguments not to rely on case sensitivity. This may reduce headache strength at debug time, especially with interpreted language like javascript that may not warn you that a variable was not defined.</p> <h2 id="writing-a-synchronous-method-implementation">Writing a synchronous method implementation</h2> <p>The method <strong>tictactoe/board</strong> is a synchronous implementation. Here is its listing:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * get the board</span> <span class="co"> */</span> <span class="dt">static</span> <span class="dt">void</span> board(<span class="kw">struct</span> afb_req req) @@ -213,7 +231,7 @@ code > span.er { color: #ff0000; font-weight: bold; } <span class="co">/* send the board's description */</span> afb_req_success(req, description, NULL); -}</code></pre> +}</code></pre></div> <p>This example shows many aspects of a synchronous method implementation. Let summarise it:</p> <ol type="1"> <li><p>The function <strong>board_of_req</strong> retrieves the context stored for the binding: the board.</p></li> @@ -227,13 +245,13 @@ code > span.er { color: #ff0000; font-weight: bold; } <p>Note that this is a PLAIN structure, not a pointer to a structure.</p> </blockquote> <p>The definition of <strong>struct afb_req</strong> is:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Describes the request by bindings from afb-daemon</span> <span class="co"> */</span> <span class="kw">struct</span> afb_req { <span class="dt">const</span> <span class="kw">struct</span> afb_req_itf *itf; <span class="co">/* the interfacing functions */</span> <span class="dt">void</span> *closure; <span class="co">/* the closure for functions */</span> -};</code></pre> +};</code></pre></div> <p>It contains two pointers: first one <em>itf</em>, points to functions used to handle internal request. Second one <em>closure</em> point onto function closure.</p> <blockquote> <p>The structure must never be used directly. Instead developer should use the intended functions provided by afb-daemon as described here after.</p> @@ -255,16 +273,16 @@ code > span.er { color: #ff0000; font-weight: bold; } <li><p><strong>afb_req_context_clear</strong>: reset the stored context data.</p></li> </ul> <p>The binding <em>tictactoe</em> use a convenient function to retrieve its context: the board. This function is <em>board_of_req</em>:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * retrieves the board of the request</span> <span class="co"> */</span> <span class="dt">static</span> <span class="kw">inline</span> <span class="kw">struct</span> board *board_of_req(<span class="kw">struct</span> afb_req req) { <span class="kw">return</span> afb_req_context(req, (<span class="dt">void</span>*)get_new_board, (<span class="dt">void</span>*)release_board); -}</code></pre> +}</code></pre></div> <p>The function <strong>afb_req_context</strong> ensures an existing context for the session of the request. Its two last arguments are functions to allocate and free context. Note function type casts to avoid compilation warnings.</p> <p>Here is the definition of the function <strong>afb_req_context</strong></p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Gets the pointer stored by the binding for the session of 'req'.</span> <span class="co"> * If the stored pointer is NULL, indicating that no pointer was</span> <span class="co"> * already stored, afb_req_context creates a new context by calling</span> @@ -279,11 +297,11 @@ code > span.er { color: #ff0000; font-weight: bold; } afb_req_context_set(req, result, free_context); } <span class="kw">return</span> result; -}</code></pre> +}</code></pre></div> <p>The second argument if the function that creates the context. For binding <em>tic-tac-toe</em> (function <strong>get_new_board</strong>). The function <strong>get_new_board</strong> creates a new board and set usage its count to 1. The boards are checking usage count to free resources when not used.</p> <p>The third argument is a function that frees context resources. For binding <em>tic-tac-toe</em> (function <strong>release_board</strong>). The function <strong>release_board</strong> decrease usage count of the board passed in argument. When usage count falls to zero, data board are freed.</p> <p>Definition of other functions dealing with contexts:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Gets the pointer stored by the binding for the session of 'req'.</span> <span class="co"> * When the binding has not yet recorded a pointer, NULL is returned.</span> <span class="co"> */</span> @@ -305,14 +323,14 @@ code > span.er { color: #ff0000; font-weight: bold; } <span class="dt">static</span> <span class="kw">inline</span> <span class="dt">void</span> afb_req_context_clear(<span class="kw">struct</span> afb_req req) { afb_req_context_set(req, NULL, NULL); -}</code></pre> +}</code></pre></div> <h3 id="sending-reply-to-a-request">Sending reply to a request</h3> <p>Two kinds of replies: successful or failure.</p> <blockquote> <p>Sending a reply to a request MUST be done once and only once.</p> </blockquote> <p>It exists two functions for "success" replies: <strong>afb_req_success</strong> and <strong>afb_req_success_f</strong>.</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Sends a reply of kind success to the request 'req'.</span> <span class="co"> * The status of the reply is automatically set to "success".</span> <span class="co"> * Its send the object 'obj' (can be NULL) with an</span> @@ -332,9 +350,9 @@ code > span.er { color: #ff0000; font-weight: bold; } <span class="co"> * Thus, in the case where 'obj' should remain available after</span> <span class="co"> * the function returns, the function 'json_object_get' shall be used.</span> <span class="co"> */</span> -<span class="dt">void</span> afb_req_success_f(<span class="kw">struct</span> afb_req req, <span class="kw">struct</span> json_object *obj, <span class="dt">const</span> <span class="dt">char</span> *info, ...);</code></pre> +<span class="dt">void</span> afb_req_success_f(<span class="kw">struct</span> afb_req req, <span class="kw">struct</span> json_object *obj, <span class="dt">const</span> <span class="dt">char</span> *info, ...);</code></pre></div> <p>It exists two functions for "failure" replies: <strong>afb_req_fail</strong> and <strong>afb_req_fail_f</strong>.</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Sends a reply of kind failure to the request 'req'.</span> <span class="co"> * The status of the reply is set to 'status' and an</span> <span class="co"> * informational comment 'info' (can also be NULL) can be added.</span> @@ -357,7 +375,7 @@ code > span.er { color: #ff0000; font-weight: bold; } <span class="co"> * Thus, in the case where 'obj' should remain available after</span> <span class="co"> * the function returns, the function 'json_object_get' shall be used.</span> <span class="co"> */</span> -<span class="dt">void</span> afb_req_fail_f(<span class="kw">struct</span> afb_req req, <span class="dt">const</span> <span class="dt">char</span> *status, <span class="dt">const</span> <span class="dt">char</span> *info, ...);</code></pre> +<span class="dt">void</span> afb_req_fail_f(<span class="kw">struct</span> afb_req req, <span class="dt">const</span> <span class="dt">char</span> *status, <span class="dt">const</span> <span class="dt">char</span> *info, ...);</code></pre></div> <blockquote> <p>For convenience, these functions automatically call <strong>json_object_put</strong> to release <strong>obj</strong>. Because <strong>obj</strong> usage count is null after being passed to a reply function, it SHOULD not be used anymore. If exceptionally <strong>obj</strong> needs to remain usable after reply function then using <strong>json_object_get</strong> on <strong>obj</strong> to increase usage count and cancels the effect the <strong>json_object_put</strong> is possible.</p> </blockquote> @@ -365,7 +383,7 @@ code > span.er { color: #ff0000; font-weight: bold; } <p>Many methods expect arguments. Afb-daemon's bindings retrieve arguments by name and not by position.</p> <p>Arguments are passed by requests through either HTTP or WebSockets.</p> <p>For example, the method <strong>join</strong> of binding <strong>tic-tac-toe</strong> expects one argument: the <em>boardid</em> to join. Here is an extract:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Join a board</span> <span class="co"> */</span> <span class="dt">static</span> <span class="dt">void</span> join(<span class="kw">struct</span> afb_req req) @@ -381,14 +399,14 @@ code > span.er { color: #ff0000; font-weight: bold; } id = afb_req_value(req, <span class="st">"boardid"</span>); <span class="kw">if</span> (id == NULL) <span class="kw">goto</span> bad_request; - ...</code></pre> + ...</code></pre></div> <p>The function <strong>afb_req_value</strong> searches in the request <em>req</em> for argument name passed in the second argument. When argument name is not passed, <strong>afb_req_value</strong> returns NULL.</p> <blockquote> <p>The search is case sensitive and <em>boardid</em> is not equivalent to <em>BoardId</em>. Nevertheless having argument names that only differ by name case is not a good idea.</p> </blockquote> <h3 id="basic-functions-for-querying-arguments">Basic functions for querying arguments</h3> <p>The function <strong>afb_req_value</strong> is defined here after:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Gets from the request 'req' the string value of the argument of 'name'.</span> <span class="co"> * Returns NULL if when there is no argument of 'name'.</span> <span class="co"> * Returns the value of the argument of 'name' otherwise.</span> @@ -398,9 +416,9 @@ code > span.er { color: #ff0000; font-weight: bold; } <span class="dt">static</span> <span class="kw">inline</span> <span class="dt">const</span> <span class="dt">char</span> *afb_req_value(<span class="kw">struct</span> afb_req req, <span class="dt">const</span> <span class="dt">char</span> *name) { <span class="kw">return</span> afb_req_get(req, name).value; -}</code></pre> +}</code></pre></div> <p>It is defined as a shortcut to call the function <strong>afb_req_get</strong>. That function is defined here after:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Gets from the request 'req' the argument of 'name'.</span> <span class="co"> * Returns a PLAIN structure of type 'struct afb_arg'.</span> <span class="co"> * When the argument of 'name' is not found, all fields of result are set to NULL.</span> @@ -412,11 +430,11 @@ code > span.er { color: #ff0000; font-weight: bold; } <span class="co"> * an HTTP POST of Content-Type "application/json". In that case, the</span> <span class="co"> * argument of name "" receives the value of the body of the HTTP request.</span> <span class="co"> */</span> -<span class="kw">struct</span> afb_arg afb_req_get(<span class="kw">struct</span> afb_req req, <span class="dt">const</span> <span class="dt">char</span> *name);</code></pre> +<span class="kw">struct</span> afb_arg afb_req_get(<span class="kw">struct</span> afb_req req, <span class="dt">const</span> <span class="dt">char</span> *name);</code></pre></div> <p>That function takes 2 parameters: the request and the name of the argument to retrieve. It returns a PLAIN structure of type <strong>struct afb_arg</strong>.</p> <p>There is a special name that is defined when the request is of type HTTP/POST with a Content-Type being application/json. This name is <strong>""</strong> (the empty string). In that case, the value of this argument of empty name is the string received as a body of the post and is supposed to be a JSON string.</p> <p>The definition of <strong>struct afb_arg</strong> is:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Describes an argument (or parameter) of a request</span> <span class="co"> */</span> <span class="kw">struct</span> afb_arg { @@ -425,9 +443,9 @@ code > span.er { color: #ff0000; font-weight: bold; } <span class="co">/* original filename of the argument if path != NULL */</span> <span class="dt">const</span> <span class="dt">char</span> *path; <span class="co">/* if not NULL, path of the received file for the argument */</span> <span class="co">/* when the request is finalized this file is removed */</span> -};</code></pre> +};</code></pre></div> <p>The structure returns the data arguments that are known for the request. This data include a field named <strong>path</strong>. This <strong>path</strong> can be accessed using the function <strong>afb_req_path</strong> defined here after:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Gets from the request 'req' the path for file attached to the argument of 'name'.</span> <span class="co"> * Returns NULL if when there is no argument of 'name' or when there is no file.</span> <span class="co"> * Returns the path of the argument of 'name' otherwise.</span> @@ -437,29 +455,29 @@ code > span.er { color: #ff0000; font-weight: bold; } <span class="dt">static</span> <span class="kw">inline</span> <span class="dt">const</span> <span class="dt">char</span> *afb_req_path(<span class="kw">struct</span> afb_req req, <span class="dt">const</span> <span class="dt">char</span> *name) { <span class="kw">return</span> afb_req_get(req, name).path; -}</code></pre> +}</code></pre></div> <p>The path is only defined for HTTP/POST requests that send file.</p> <h3 id="arguments-for-received-files">Arguments for received files</h3> <p>As it is explained above, clients can send files using HTTP/POST requests.</p> <p>Received files are attached to "file" argument name. For example, the following HTTP fragment (from test/sample-post.html) will send an HTTP/POST request to the method <strong>post/upload-image</strong> with 2 arguments named <em>file</em> and <em>hidden</em>.</p> -<pre class="sourceCode html"><code class="sourceCode html"><span class="kw"><h2></span>Sample Post File<span class="kw"></h2></span> +<div class="sourceCode"><pre class="sourceCode html"><code class="sourceCode html"><span class="kw"><h2></span>Sample Post File<span class="kw"></h2></span> <span class="kw"><form</span><span class="ot"> enctype=</span><span class="st">"multipart/form-data"</span><span class="kw">></span> <span class="kw"><input</span><span class="ot"> type=</span><span class="st">"file"</span><span class="ot"> name=</span><span class="st">"file"</span> <span class="kw">/></span> <span class="kw"><input</span><span class="ot"> type=</span><span class="st">"hidden"</span><span class="ot"> name=</span><span class="st">"hidden"</span><span class="ot"> value=</span><span class="st">"bollobollo"</span> <span class="kw">/></span> <span class="kw"><br></span> <span class="kw"><button</span><span class="ot"> formmethod=</span><span class="st">"POST"</span><span class="ot"> formaction=</span><span class="st">"api/post/upload-image"</span><span class="kw">></span>Post File<span class="kw"></button></span> -<span class="kw"></form></span></code></pre> +<span class="kw"></form></span></code></pre></div> <p>Argument named <strong>file</strong> should have both its value and path defined.</p> <p>The value is the name of the file as it was set by the HTTP client. Generally it is the filename on client side.</p> <p>The path is the effective path of saved file on the temporary local storage area of the application. This is a randomly generated and unique filename. It is not linked with the original filename as used on client side.</p> <p>After success the binding can use the uploaded file directly from local storage path with no restriction: read, write, remove, copy, rename... Nevertheless when request reply is set and query terminated, the uploaded temporary file at path is destroyed.</p> <h3 id="arguments-as-a-json-object">Arguments as a JSON object</h3> <p>Bindings may also request every arguments of a given call as one single object. This feature is provided by the function <strong>afb_req_json</strong> defined here after:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Gets from the request 'req' the json object hashing the arguments.</span> <span class="co"> * The returned object must not be released using 'json_object_put'.</span> <span class="co"> */</span> -<span class="kw">struct</span> json_object *afb_req_json(<span class="kw">struct</span> afb_req req);</code></pre> +<span class="kw">struct</span> json_object *afb_req_json(<span class="kw">struct</span> afb_req req);</code></pre></div> <p>It returns a json object. This object depends on how the request was built:</p> <ul> <li><p>For HTTP requests, this json object uses key names mapped on argument name. Values are either string for common arguments or object ie: { "file": "...", "path": "..." }</p></li> @@ -482,22 +500,22 @@ code > span.er { color: #ff0000; font-weight: bold; } <li><p>Afb-daemon checks that the returned version and name can be managed. If so, binding and its methods are register to become usable as soon as afb-daemon initialisation is finished.</p></li> </ol> <p>Here after the code used for <strong>afbBindingV1Register</strong> from binding <em>tic-tac-toe</em>:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * activation function for registering the binding called by afb-daemon</span> <span class="co"> */</span> <span class="dt">const</span> <span class="kw">struct</span> afb_binding *afbBindingV1Register(<span class="dt">const</span> <span class="kw">struct</span> afb_binding_interface *itf) { afbitf = itf; <span class="co">// records the interface for accessing afb-daemon</span> <span class="kw">return</span> &binding_description; <span class="co">// returns the description of the binding</span> -}</code></pre> +}</code></pre></div> <p>It is a very minimal initialisation function because <em>tic-tac-toe</em> binding doesn't have any application related initialisation step. It merely record daemon's interface and returns its description.</p> <p>The variable <strong>afbitf</strong> is a binding global variable. It keeps the interface to afb-daemon that should be used for logging and pushing events. Here is its declaration:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * the interface to afb-daemon</span> <span class="co"> */</span> -<span class="dt">const</span> <span class="kw">struct</span> afb_binding_interface *afbitf;</code></pre> +<span class="dt">const</span> <span class="kw">struct</span> afb_binding_interface *afbitf;</code></pre></div> <p>The description of the binding is defined here after.</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * array of the methods exported to afb-daemon</span> <span class="co"> */</span> <span class="dt">static</span> <span class="dt">const</span> <span class="kw">struct</span> afb_verb_desc_v1 binding_methods[] = { @@ -525,7 +543,7 @@ code > span.er { color: #ff0000; font-weight: bold; } .info= <span class="st">"Sample tac-tac-toe game"</span>, <span class="co">/* short description of of the binding */</span> .methods = binding_methods <span class="co">/* the array describing the methods of the API */</span> } -};</code></pre> +};</code></pre></div> <p>The structure <strong>binding_description</strong> describes the binding. It declares the type and version of the binding, its name, a short description and its methods list.</p> <p>The list of methods is an array of structures describing the methods and terminated by a NULL marker.</p> <p>In version one of afb-damon binding, a method description contains 4 fields:</p> @@ -536,7 +554,7 @@ code > span.er { color: #ff0000; font-weight: bold; } <li><p>a short description.</p></li> </ul> <p>The structure describing methods is defined as follows:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Description of one method of the API provided by the binding</span> <span class="co"> * This enumeration is valid for bindings of type 1</span> <span class="co"> */</span> @@ -546,10 +564,14 @@ code > span.er { color: #ff0000; font-weight: bold; } <span class="kw">enum</span> AFB_session_v1 session; <span class="co">/* authorisation and session requirements of the method */</span> <span class="dt">void</span> (*callback)(<span class="kw">struct</span> afb_req req); <span class="co">/* callback function implementing the method */</span> <span class="dt">const</span> <span class="dt">char</span> *info; <span class="co">/* textual description of the method */</span> -};</code></pre> +};</code></pre></div> <p>For technical reasons, the enumeration <strong>enum AFB_session_v1</strong> is not exactly an enumeration but the wrapper of constant definitions that can be mixed using bitwise or (the C operator |).</p> <p>The constants that can bit mixed are:</p> -<table> +<table style="width:100%;"> +<colgroup> +<col style="width: 29%" /> +<col style="width: 70%" /> +</colgroup> <thead> <tr class="header"> <th style="text-align: left;">Constant name</th> @@ -678,7 +700,7 @@ code > span.er { color: #ff0000; font-weight: bold; } </table> <p>You can note that the 2 methods <strong>WARNING</strong> and <strong>INFO</strong> have the same level of verbosity. But they don't have the same <em>syslog level</em>. It means that they are output with a different level on the logging system.</p> <p>All of these methods have the same signature:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="dt">void</span> ERROR(<span class="dt">const</span> <span class="kw">struct</span> afb_binding_interface *afbitf, <span class="dt">const</span> <span class="dt">char</span> *message, ...);</code></pre> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="dt">void</span> ERROR(<span class="dt">const</span> <span class="kw">struct</span> afb_binding_interface *afbitf, <span class="dt">const</span> <span class="dt">char</span> *message, ...);</code></pre></div> <p>The first argument <strong>afbitf</strong> is the interface to afb daemon that the binding received at initialisation time when <strong>afbBindingV1Register</strong> is called.</p> <p>The second argument <strong>message</strong> is a formatting string compatible with printf/sprintf.</p> <p>The remaining arguments are arguments of the formating message like with printf.</p> @@ -758,7 +780,7 @@ code > span.er { color: #ff0000; font-weight: bold; } <h2 id="sending-events">Sending events</h2> <p>Since version 0.5, bindings can broadcast events to any potential listener. As today only unattended even are supported. Targeted events are expected for next coming version.</p> <p>The binding <em>tic-tac-toe</em> broadcasts events when the board changes. This is done in the function <strong>changed</strong>:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * signals a change of the board</span> <span class="co"> */</span> <span class="dt">static</span> <span class="dt">void</span> changed(<span class="kw">struct</span> board *board, <span class="dt">const</span> <span class="dt">char</span> *reason) @@ -772,11 +794,11 @@ code > span.er { color: #ff0000; font-weight: bold; } ... afb_daemon_broadcast_event(afbitf->daemon, reason, description); -}</code></pre> +}</code></pre></div> <p>The description of the changed board is pushed via the daemon interface.</p> <p>Within binding <em>tic-tac-toe</em>, <em>reason</em> indicates the origin of the change. In function <strong>afb_daemon_broadcast_event</strong> the second parameter is the name of broadcasted event. The third argument is the object that is transmitted with the event.</p> <p>Function <strong>afb_daemon_broadcast_event</strong> is defined here after:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * Broadcasts widely the event of 'name' with the data 'object'.</span> <span class="co"> * 'object' can be NULL.</span> <span class="co"> * 'daemon' MUST be the daemon given in interface when activating the binding.</span> @@ -785,7 +807,7 @@ code > span.er { color: #ff0000; font-weight: bold; } <span class="co"> * Thus, in the case where 'object' should remain available after</span> <span class="co"> * the function returns, the function 'json_object_get' shall be used.</span> <span class="co"> */</span> -<span class="dt">void</span> afb_daemon_broadcast_event(<span class="kw">struct</span> afb_daemon daemon, <span class="dt">const</span> <span class="dt">char</span> *name, <span class="kw">struct</span> json_object *object);</code></pre> +<span class="dt">void</span> afb_daemon_broadcast_event(<span class="kw">struct</span> afb_daemon daemon, <span class="dt">const</span> <span class="dt">char</span> *name, <span class="kw">struct</span> json_object *object);</code></pre></div> <blockquote> <p>Be aware, as with reply functions <strong>object</strong> is automatically released using <strong>json_object_put</strong> when using this function. Call <strong>json_object_get</strong> before calling <strong>afb_daemon_broadcast_event</strong> to keep <strong>object</strong> available after function returns.</p> </blockquote> @@ -812,7 +834,7 @@ code > span.er { color: #ff0000; font-weight: bold; } <p>Here, this is an invocation of the binding by an other client that unblock the suspended <em>wait</em> call. Nevertheless in most case this should be a timer, a hardware event, a sync with a concurrent process or thread, ...</p> <p>Common case of an asynchronous implementation.</p> <p>Here is the listing of the function <strong>wait</strong>:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="dt">static</span> <span class="dt">void</span> wait(<span class="kw">struct</span> afb_req req) +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="dt">static</span> <span class="dt">void</span> wait(<span class="kw">struct</span> afb_req req) { <span class="kw">struct</span> board *board; <span class="kw">struct</span> waiter *waiter; @@ -827,7 +849,7 @@ code > span.er { color: #ff0000; font-weight: bold; } waiter->next = board->waiters; afb_req_addref(req); board->waiters = waiter; -}</code></pre> +}</code></pre></div> <p>After retrieving the board, the function adds a new waiter to waiters list and returns without setting a reply.</p> <p>Before returning, it increases <strong>req</strong> request's reference count using <strong>afb_req_addref</strong> function.</p> <blockquote> @@ -835,7 +857,7 @@ code > span.er { color: #ff0000; font-weight: bold; } </blockquote> <p>Later, when a board changes, it calls <em>tic-tac-toe</em> <strong>changed</strong> function with reason of change in parameter.</p> <p>Here is the full listing of the function <strong>changed</strong>:</p> -<pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> <span class="co"> * signals a change of the board</span> <span class="co"> */</span> <span class="dt">static</span> <span class="dt">void</span> changed(<span class="kw">struct</span> board *board, <span class="dt">const</span> <span class="dt">char</span> *reason) @@ -857,18 +879,18 @@ code > span.er { color: #ff0000; font-weight: bold; } } afb_event_sender_push(afb_daemon_get_event_sender(afbitf->daemon), reason, description); -}</code></pre> +}</code></pre></div> <p>The list of waiters is walked and a reply is sent to each waiter. After sending the reply, the reference count of the request is decremented using <strong>afb_req_unref</strong> to allow resources to be freed.</p> <blockquote> <p>The reference count <strong>MUST</strong> be decremented using <strong>afb_req_unref</strong> to free resources and avoid memory leaks. This usage count decrement should happen <strong>AFTER</strong> setting reply or bad things may happen.</p> </blockquote> <h2 id="how-to-build-a-binding">How to build a binding</h2> <p>Afb-daemon provides a <em>pkg-config</em> configuration file that can be queried by providing <strong>afb-daemon</strong> in command line arguments. This configuration file provides data that should be used for bindings compilation. Examples:</p> -<pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">pkg-config</span> --cflags afb-daemon -$ <span class="kw">pkg-config</span> --libs afb-daemon</code></pre> +<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="kw">pkg-config</span> --cflags afb-daemon +$ <span class="kw">pkg-config</span> --libs afb-daemon</code></pre></div> <h3 id="example-for-cmake-meta-build-system">Example for cmake meta build system</h3> <p>This example is the extract for building the binding <em>afm-main</em> using <em>CMAKE</em>.</p> -<pre class="sourceCode cmake"><code class="sourceCode cmake"><span class="fu">pkg_check_modules</span>(afb afb-daemon) +<div class="sourceCode"><pre class="sourceCode cmake"><code class="sourceCode cmake"><span class="fu">pkg_check_modules</span>(afb afb-daemon) <span class="kw">if</span>(afb_FOUND) <span class="kw">message</span>(<span class="ot">STATUS</span> <span class="st">"Creation afm-main-binding for AFB-DAEMON"</span>) <span class="kw">add_library</span>(afm-main-binding <span class="ot">MODULE</span> afm-main-binding.c) @@ -882,11 +904,15 @@ $ <span class="kw">pkg-config</span> --libs afb-daemon</code></pre> <span class="kw">install</span>(<span class="ot">TARGETS</span> afm-main-binding <span class="ot">LIBRARY</span> <span class="ot">DESTINATION</span> <span class="dv">${binding_dir}</span>) <span class="kw">else</span>() <span class="kw">message</span>(<span class="ot">STATUS</span> <span class="st">"Not creating the binding for AFB-DAEMON"</span>) -<span class="kw">endif</span>()</code></pre> +<span class="kw">endif</span>()</code></pre></div> <p>Let now describe some of these lines.</p> -<pre class="sourceCode cmake"><code class="sourceCode cmake"><span class="fu">pkg_check_modules</span>(afb afb-daemon)</code></pre> +<div class="sourceCode"><pre class="sourceCode cmake"><code class="sourceCode cmake"><span class="fu">pkg_check_modules</span>(afb afb-daemon)</code></pre></div> <p>This first lines searches to the <em>pkg-config</em> configuration file for <strong>afb-daemon</strong>. Resulting data are stored in the following variables:</p> -<table> +<table style="width:94%;"> +<colgroup> +<col style="width: 26%" /> +<col style="width: 68%" /> +</colgroup> <thead> <tr class="header"> <th style="text-align: left;">Variable</th> @@ -921,20 +947,20 @@ $ <span class="kw">pkg-config</span> --libs afb-daemon</code></pre> </tbody> </table> <p>If development files are found, the binding can be added to the set of target to build.</p> -<pre class="sourceCode cmake"><code class="sourceCode cmake"><span class="kw">add_library</span>(afm-main-binding <span class="ot">MODULE</span> afm-main-binding.c)</code></pre> +<div class="sourceCode"><pre class="sourceCode cmake"><code class="sourceCode cmake"><span class="kw">add_library</span>(afm-main-binding <span class="ot">MODULE</span> afm-main-binding.c)</code></pre></div> <p>This line asks to create a shared library having a single source file named afm-main-binding.c to be compiled. The default name of the created shared object is <strong>libafm-main-binding.so</strong>.</p> -<pre class="sourceCode cmake"><code class="sourceCode cmake"><span class="kw">set_target_properties</span>(afm-main-binding <span class="ot">PROPERTIES</span> +<div class="sourceCode"><pre class="sourceCode cmake"><code class="sourceCode cmake"><span class="kw">set_target_properties</span>(afm-main-binding <span class="ot">PROPERTIES</span> <span class="ot">PREFIX</span> <span class="st">""</span> <span class="ot">LINK_FLAGS</span> <span class="st">"-Wl,--version-script=</span><span class="dv">${CMAKE_CURRENT_SOURCE_DIR}</span><span class="st">/afm-main-binding.export-map"</span> -)</code></pre> +)</code></pre></div> <p>This lines are doing two things:</p> <ol type="1"> <li><p>It renames the built library from <strong>libafm-main-binding.so</strong> to <strong>afm-main-binding.so</strong> by removing the implicitly added prefix <em>lib</em>. This step is not mandatory because afb-daemon doesn't check names of files at load time. The only filename convention used by afb-daemon relates to <strong>.so</strong> termination. *.so pattern is used when afb-daemon automatically discovers binding from a directory hierarchy.</p></li> <li><p>It applies a version script at link time to only export the reserved name <strong>afbBindingV1Register</strong> for registration entry point. By default, when building a shared library linker exports all the public symbols (C functions that are not <strong>static</strong>).</p></li> </ol> <p>Next line are:</p> -<pre class="sourceCode cmake"><code class="sourceCode cmake"><span class="kw">target_include_directories</span>(afm-main-binding <span class="ot">PRIVATE</span> <span class="dv">${afb_INCLUDE_DIRS}</span>) -<span class="kw">target_link_libraries</span>(afm-main-binding utils <span class="dv">${afb_LIBRARIES}</span>)</code></pre> +<div class="sourceCode"><pre class="sourceCode cmake"><code class="sourceCode cmake"><span class="kw">target_include_directories</span>(afm-main-binding <span class="ot">PRIVATE</span> <span class="dv">${afb_INCLUDE_DIRS}</span>) +<span class="kw">target_link_libraries</span>(afm-main-binding utils <span class="dv">${afb_LIBRARIES}</span>)</code></pre></div> <p>As you can see it uses the variables computed by <strong><em>pkg_check_modules(afb afb-daemon)</em></strong> to configure the compiler and the linker.</p> <h3 id="exporting-the-function-afbbindingv1register">Exporting the function afbBindingV1Register</h3> <p>The function <strong>afbBindingV1Register</strong> MUST be exported. This can be achieved using a version script at link time. Here after is a version script used for <em>tic-tac-toe</em> (bindings/samples/export.map).</p> diff --git a/doc/afb-daemon-vocabulary.html b/doc/afb-daemon-vocabulary.html index 282be6b4..5a585c23 100644 --- a/doc/afb-daemon-vocabulary.html +++ b/doc/afb-daemon-vocabulary.html @@ -7,10 +7,10 @@ <meta name="author" content="José Bollo"> <title>Vocabulary for AFB-DAEMON</title> <style type="text/css">code{white-space: pre;}</style> + <link rel="stylesheet" href="doc.css"> <!--[if lt IE 9]> - <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> + <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> <![endif]--> - <link rel="stylesheet" href="doc.css"> </head> <body> <header> diff --git a/doc/afb-events-guide.html b/doc/afb-events-guide.html new file mode 100644 index 00000000..aa606400 --- /dev/null +++ b/doc/afb-events-guide.html @@ -0,0 +1,279 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <meta name="generator" content="pandoc"> + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> + <meta name="author" content="José Bollo"> + <title>Guid for developing with events</title> + <style type="text/css">code{white-space: pre;}</style> + <style type="text/css"> +div.sourceCode { overflow-x: auto; } +table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode { + margin: 0; padding: 0; vertical-align: baseline; border: none; } +table.sourceCode { width: 100%; line-height: 100%; } +td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; } +td.sourceCode { padding-left: 5px; } +code > span.kw { color: #007020; font-weight: bold; } /* Keyword */ +code > span.dt { color: #902000; } /* DataType */ +code > span.dv { color: #40a070; } /* DecVal */ +code > span.bn { color: #40a070; } /* BaseN */ +code > span.fl { color: #40a070; } /* Float */ +code > span.ch { color: #4070a0; } /* Char */ +code > span.st { color: #4070a0; } /* String */ +code > span.co { color: #60a0b0; font-style: italic; } /* Comment */ +code > span.ot { color: #007020; } /* Other */ +code > span.al { color: #ff0000; font-weight: bold; } /* Alert */ +code > span.fu { color: #06287e; } /* Function */ +code > span.er { color: #ff0000; font-weight: bold; } /* Error */ +code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */ +code > span.cn { color: #880000; } /* Constant */ +code > span.sc { color: #4070a0; } /* SpecialChar */ +code > span.vs { color: #4070a0; } /* VerbatimString */ +code > span.ss { color: #bb6688; } /* SpecialString */ +code > span.im { } /* Import */ +code > span.va { color: #19177c; } /* Variable */ +code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */ +code > span.op { color: #666666; } /* Operator */ +code > span.bu { } /* BuiltIn */ +code > span.ex { } /* Extension */ +code > span.pp { color: #bc7a00; } /* Preprocessor */ +code > span.at { color: #7d9029; } /* Attribute */ +code > span.do { color: #ba2121; font-style: italic; } /* Documentation */ +code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */ +code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */ +code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */ + </style> + <link rel="stylesheet" href="doc.css"> + <!--[if lt IE 9]> + <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> + <![endif]--> +</head> +<body> +<header> +<h1 class="title">Guid for developing with events</h1> +<h2 class="author">José Bollo</h2> +<h3 class="date">16 septembre 2016</h3> +</header> +<nav id="TOC"> +<ul> +<li><a href="#guid-for-developing-with-events">Guid for developing with events</a><ul> +<li><a href="#subscribing-and-unsubscribing">Subscribing and unsubscribing</a></li> +<li><a href="#generating-and-pushing-signals-and-data">Generating and pushing signals and data</a></li> +<li><a href="#receiving-the-signals">Receiving the signals</a></li> +<li><a href="#the-exceptional-case-of-wide-broadcast">The exceptional case of wide broadcast</a></li> +</ul></li> +<li><a href="#reference-of-functions">Reference of functions</a><ul> +<li><a href="#function-afb_event-afb_daemon_make_event">Function afb_event afb_daemon_make_event</a></li> +<li><a href="#function-afb_event_push">Function afb_event_push</a></li> +<li><a href="#function-afb_event_drop">Function afb_event_drop</a></li> +<li><a href="#function-afb_req_subscribe">Function afb_req_subscribe</a></li> +<li><a href="#function-afb_req_unsubscribe">Function afb_req_unsubscribe</a></li> +<li><a href="#function-afb_event_broadcast">Function afb_event_broadcast</a></li> +<li><a href="#function-afb_daemon_broadcast_event">Function afb_daemon_broadcast_event</a></li> +</ul></li> +<li><a href="#architectural-digressions">Architectural digressions</a><ul> +<li><a href="#strict-separation">Strict separation</a></li> +<li><a href="#soft-composition">Soft composition</a></li> +</ul></li> +</ul> +</nav> +<h2 id="guid-for-developing-with-events">Guid for developing with events</h2> +<p>Signaling agents are services that send events to any clients that subscribed for receiving it. The sent events carry any data.</p> +<p>To have a good understanding of how to write a signaling agent, the actions of subscribing, unsubscribing, producing, sending, receiving events must be described and explained.</p> +<p>The basis of a signaling agent is shown on the following figure:</p> +<figure> +<img src="signaling-basis.svg" alt="scenario of using events" /><figcaption>scenario of using events</figcaption> +</figure> +<p>This figure shows the main role of the signaling framework for the propagation of events.</p> +<p>For people not familiar with the framework, a signaling agent and a “binding” are similar.</p> +<h3 id="subscribing-and-unsubscribing">Subscribing and unsubscribing</h3> +<p>Subscribing and subscription is the action that makes a client able to receive data from a signaling agent. Subscription must create resources for generating the data and for delivering the data to the client. These two aspects are not handled by the same piece of software: generating the data is the responsibility of the developer of the signaling agent while delivering the data is handled by the framework.</p> +<p>When a client subscribes for data, the agent must:</p> +<ol type="1"> +<li>check that the subscription request is correct;</li> +<li>establish the computation chain of the required data, if not already done;</li> +<li>create a named event for the computed data, if not already done;</li> +<li>ask the framework to establish the subscription to the event for the request;</li> +<li>optionally give indications about the event in the reply to the client.</li> +</ol> +<p>The first two steps are not involving the framework. They are linked to the business logic of the binding. The request can be any description of the requested data and the computing stream can be of any nature, this is specific to the binding.</p> +<p>As said before, the framework uses and integrates “libsystemd” and its event loop. Within the framework, "libsystemd" is the standard API/library for bindings expecting to setup and handle I/O, timer or signal events.</p> +<p>Steps 3 and 4 are bound to the framework.</p> +<p>The agent must create an object for handling the propagation of produced data to its clients. That object is called “event” in the framework. An event has a name that allows clients to distinguish it from other events.</p> +<p>Events are created using the <strong><em>afb_daemon_make_event</em></strong> function that takes the name of the event. Example:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"> event = afb_daemon_make_event(afb_daemon, name);</code></pre></div> +<p>Once created, the event can be used either to push data to its subscribers or to broadcast data to any listener.</p> +<p>The event must be used to establish the subscription for the requesting client. This is done using the <strong><em>afb_req_subscribe</em></strong> function that takes the current request object and event and associates them together. Example:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"> rc = afb_req_subscribe(afb_req, event);</code></pre></div> +<p>When successful, this function make the connection between the event and the client that emitted the request. The client becomes a subscriber of the event until it unsubscribes or disconnects. The <strong><em>afb_req_subscribe</em></strong> function will fail if the client connection is weak: if the request comes from a HTTP link. To receive signals, the client must be connected. The AGL framework allows connections using WebSocket.</p> +<p>The name of the event is either a well known name or an ad hoc name forged for the usecase.</p> +<p>Let's see a basic example: client A expects to receive the speed in km/h every second while client B expects the speed in mph twice a second. In that case, there are two different events because it is not the same unit and it is not the same frequency. Having two different events allows to associate clients to the correct event. But this doesn't tell any word about the name of these events. The designer of the signaling agent has two options for naming:</p> +<ol type="1"> +<li>names can be the same (“speed” for example) with sent data self-describing itself or having a specific tag (requiring from clients awareness about requesting both kinds of speed isn't safe).</li> +<li>names of the event include the variations (by example: “speed-km/h-1Hz” and “speed-mph-2Hz”) and, in that case, sent data can self-describe itself or not.</li> +</ol> +<p>In both cases, the signaling agent might have to send the name of the event and/or an associated tag to its client in the reply of the subscription. This is part of the step 5 above.</p> +<p>The framework only uses the event (not its name) for subscription, unsubscription and pushing.</p> +<p>When the requested data is already generated and the event used for pushing it already exists, the signaling agent must not instantiate a new processing chain and must not create a new event object for pushing data. The signaling agent must reuse the existing chain and event.</p> +<p>Unsubscribing is made by the signaling agent on a request of its client. The <strong><em>afb_req_unsubscribe</em></strong> function tells the framework to remove the requesting client from the event's list of subscribers. Example:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"> afb_req_unsubscribe(afb_req, event);</code></pre></div> +<p>Subscription count does not matter to the framework: subscribing the same client several times has the same effect that subscribing only one time. Thus, when unsubscribing is invoked, it becomes immediately effective.</p> +<h4 id="more-on-naming-events">More on naming events</h4> +<p>Within the AGL framework, a signaling agent is a binding that has an API prefix. This prefix is meant to be unique and to identify the binding API. The names of the events that this signaling agent creates are automatically prefixed by the framework, using the API prefix of the binding.</p> +<p>Thus, if a signaling agent of API prefix <strong><em>api</em></strong> creates an event of name <strong><em>event</em></strong> and pushes data to that event, the subscribers will receive an event of name <strong><em>api/event</em></strong>.</p> +<h3 id="generating-and-pushing-signals-and-data">Generating and pushing signals and data</h3> +<p>This of the responsibility of the designer of the signaling agent to establish the processing chain for generating events. In many cases, this can be achieved using I/O or timer or signal events inserted in the main loop. For this case, the AGL framework uses “libsystemd” and provide a way to integrates to the main loop of this library using afb_daemon_get_event_loop. Example:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"> sdev = afb_daemon_get_event_loop(af_daemon); + rc = sd_event_add_io(sdev, &source, fd, EPOLLIN, myfunction, NULL);</code></pre></div> +<p>In some other cases, the events are coming from D-Bus. In that case, the framework also uses “libsystemd” internally to access D-Bus. It provides two methods to get the available D-Bus objects, already existing and bound to the main libsystemd event loop. Use either <strong><em>afb_daemon_get_system_bus</em></strong> or <strong><em>afb_daemon_get_user_bus</em></strong> to get the required instance. Then use functions of “libsystemd” to handle D-Bus.</p> +<p>In some rare cases, the generation of the data requires to start a new thread.</p> +<p>When a data is generated and ready to be pushed, the signaling agent should call the function <strong><em>afb_event_push</em></strong>. Example:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"> rc = afb_event_push(event, json); + <span class="kw">if</span> (rc == <span class="dv">0</span>) { + stop_generating(event); + afb_event_drop(event); + }</code></pre></div> +<p>The function <strong><em>afb_event_push</em></strong> pushes json data to all the subscribers. It then returns the count of subscribers. When the count is zero, there is no subscriber listening for the event. The example above shows that in that case, the signaling agent stops to generate data for the event and delete the event using afb_event_drop. This is one possible option. Other valuable options are: do nothing and continue to generate and push the event or just stop to generate and push the data but keep the event existing.</p> +<h3 id="receiving-the-signals">Receiving the signals</h3> +<p>Understanding what a client expects when it receives signals, events or data shall be the most important topic of the designer of a signaling agent. The good point here is that because JSON<a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a> is the exchange format, structured data can be sent in a flexible way.</p> +<p>The good design is to allow as much as possible the client to describe what is needed with the goal to optimize the processing to the requirements only.</p> +<h3 id="the-exceptional-case-of-wide-broadcast">The exceptional case of wide broadcast</h3> +<p>Some data or events have so much importance that they can be widely broadcasted to alert any listening client. Examples of such an alert are:</p> +<ul> +<li>system is entering/leaving “power safe” mode</li> +<li>system is shutting down</li> +<li>the car starts/stops moving</li> +<li>...</li> +</ul> +<p>An event can be broadcasted using one of the two following methods: <strong><em>afb_daemon_broadcast_event</em></strong> or <strong><em>afb_event_broadcast</em></strong>.</p> +<p>Example 1:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"> afb_daemon_broadcast_event(afb_daemon, name, json);</code></pre></div> +<p>Example 2:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"> event = afb_daemon_make_event(afb_daemon, name); + . . . . + afb_event_broadcast(event, json);</code></pre></div> +<p>As for other events, the name of events broadcasted using <strong><em>afb_daemon_broadcast_event</em></strong> are automatically prefixed by the framework with API prefix of the binding (signaling agent).</p> +<h2 id="reference-of-functions">Reference of functions</h2> +<h3 id="function-afb_event-afb_daemon_make_event">Function afb_event afb_daemon_make_event</h3> +<p>The function <strong><em>afb_daemon_make_event</em></strong> that is defined as below:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<span class="co"> * Creates an event of 'name' and returns it.</span> +<span class="co"> * 'daemon' MUST be the daemon given in interface when activating the binding.</span> +<span class="co"> */</span> +<span class="kw">struct</span> afb_event afb_daemon_make_event(<span class="kw">struct</span> afb_daemon daemon, <span class="dt">const</span> <span class="dt">char</span> *name);</code></pre></div> +<p>The daemon is the handler to the application framework binder daemon received during initialisation steps of the binding.</p> +<p>Calling the function <strong><em>afb_daemon_make_event</em></strong> within the initialisation function <strong><em>afbBindingV1Register</em></strong> will <em>fail</em> because the plugin name is not known at this time.</p> +<p>The correct way to create the event at initialisation is to call the function <strong><em>afb_daemon_make_event</em></strong> within the initialisation function <strong><em>afbBindingV1ServiceInit</em></strong>.</p> +<h3 id="function-afb_event_push">Function afb_event_push</h3> +<p>The function <strong><em>afb_event_push</em></strong> is defined as below:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<span class="co"> * Pushes the 'event' with the data 'object' to its observers.</span> +<span class="co"> * 'object' can be NULL.</span> +<span class="co"> *</span> +<span class="co"> * For convenience, the function calls 'json_object_put' for object'.</span> +<span class="co"> * Thus, in the case where 'object' should remain available after</span> +<span class="co"> * the function returns, the function 'json_object_get' shall be used.</span> +<span class="co"> *</span> +<span class="co"> * Returns the count of clients that received the event.</span> +<span class="co"> */</span> +<span class="dt">int</span> afb_event_push(<span class="kw">struct</span> afb_event event, <span class="kw">struct</span> json_object *object);</code></pre></div> +<p>As the function <strong><em>afb_event_push</em></strong> returns 0 when there is no more subscriber, a binding can remove such unexpected event using the function <strong><em>afb_event_drop</em></strong>.</p> +<h3 id="function-afb_event_drop">Function afb_event_drop</h3> +<p>The function <strong><em>afb_event_drop</em></strong> is defined as below:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<span class="co"> * Drops the data associated to the event</span> +<span class="co"> * After calling this function, the event</span> +<span class="co"> * MUST NOT BE USED ANYMORE.</span> +<span class="co"> */</span> +<span class="dt">void</span> afb_event_drop(<span class="kw">struct</span> afb_event event);</code></pre></div> +<h3 id="function-afb_req_subscribe">Function afb_req_subscribe</h3> +<p>The function <strong><em>afb_req_subscribe</em></strong> is defined as below:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<span class="co"> * Establishes for the client link identified by 'req' a subscription</span> +<span class="co"> * to the 'event'.</span> +<span class="co"> * Returns 0 in case of successful subscription or -1 in case of error.</span> +<span class="co"> */</span> +<span class="dt">int</span> afb_req_subscribe(<span class="kw">struct</span> afb_req req, <span class="kw">struct</span> afb_event event);</code></pre></div> +<p>The subscription adds the client of the request to the list of subscribers to the event.</p> +<h3 id="function-afb_req_unsubscribe">Function afb_req_unsubscribe</h3> +<p>The function <strong><em>afb_req_unsubscribe</em></strong> is defined as below:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<span class="co"> * Revokes the subscription established to the 'event' for the client</span> +<span class="co"> * link identified by 'req'.</span> +<span class="co"> * Returns 0 in case of successful unsubscription or -1 in case of error.</span> +<span class="co"> */</span> +<span class="dt">int</span> afb_req_unsubscribe(<span class="kw">struct</span> afb_req req, <span class="kw">struct</span> afb_event event);</code></pre></div> +<p>The unsubscription removes the client of the request of the list of subscribers to the event. When the list of subscribers to the event becomes empty, the function <strong><em>afb_event_push</em></strong> will return zero.</p> +<h3 id="function-afb_event_broadcast">Function afb_event_broadcast</h3> +<p>The function <strong><em>afb_event_broadcast</em></strong> is defined as below:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<span class="co"> * Broadcasts widely the 'event' with the data 'object'.</span> +<span class="co"> * 'object' can be NULL.</span> +<span class="co"> *</span> +<span class="co"> * For convenience, the function calls 'json_object_put' for 'object'.</span> +<span class="co"> * Thus, in the case where 'object' should remain available after</span> +<span class="co"> * the function returns, the function 'json_object_get' shall be used.</span> +<span class="co"> *</span> +<span class="co"> * Returns the count of clients that received the event.</span> +<span class="co"> */</span> +<span class="dt">int</span> afb_event_broadcast(<span class="kw">struct</span> afb_event event, <span class="kw">struct</span> json_object *object);</code></pre></div> +<p>This uses an existing event (created with <strong><em>afb_daemon_make_event</em></strong>) for broadcasting an event having its name.</p> +<h3 id="function-afb_daemon_broadcast_event">Function afb_daemon_broadcast_event</h3> +<p>The function <strong><em>afb_daemon_broadcast_event</em></strong> is defined as below:</p> +<div class="sourceCode"><pre class="sourceCode c"><code class="sourceCode c"><span class="co">/*</span> +<span class="co"> * Broadcasts widely the event of 'name' with the data 'object'.</span> +<span class="co"> * 'object' can be NULL.</span> +<span class="co"> * 'daemon' MUST be the daemon given in interface when activating the binding.</span> +<span class="co"> *</span> +<span class="co"> * For convenience, the function calls 'json_object_put' for 'object'.</span> +<span class="co"> * Thus, in the case where 'object' should remain available after</span> +<span class="co"> * the function returns, the function 'json_object_get' shall be used.</span> +<span class="co"> *</span> +<span class="co"> * Returns the count of clients that received the event.</span> +<span class="co"> */</span> +<span class="dt">int</span> afb_daemon_broadcast_event(<span class="kw">struct</span> afb_daemon daemon, <span class="dt">const</span> <span class="dt">char</span> *name, <span class="kw">struct</span> json_object *object);</code></pre></div> +<p>The name is given here explicitely. The name is automatically prefixed with the name of the binding. For example, a binding of prefix "xxx" would broadcat the event "xxx/name".</p> +<h2 id="architectural-digressions">Architectural digressions</h2> +<p>Based on their dependencies to hardware, signaling agents can be split into 2 categories: low-level signaling agents and high-level signaling agents.</p> +<p>Low-level signaling agents are bound to the hardware and focused on interfacing and driving.</p> +<p>High-level signaling agent are independent of the hardware and ocused on providing service.</p> +<p>This separation (that may in the corner look artificial) aim to help in the systems design. The main idea here is that high-level signaling agents are providing “business logic”, also known as “application logic”, that is proper to the car industry and that can be reused and that can evolve as a foundation for the future of the industry.</p> +<p>The implementation of this decomposition may follow 2 paths: strict separation or soft composition.</p> +<h3 id="strict-separation">Strict separation</h3> +<p>The strict separation implements the modularity composition of signaling agent through the framework. The high-level signaling agent subscribes to the low level signaling agent using the standard client API.</p> +<p>Advantages:</p> +<ul> +<li>Modularity</li> +<li>Separation of responsibilities</li> +<li>Possible aggregation of multiple sources</li> +<li>Soft binding of agent good for maintenance</li> +</ul> +<p>Drawbacks:</p> +<ul> +<li>Cost of propagation of data (might serialize)</li> +<li>Difficulties to abstract low-level signaling agent or to find a trade-of between abstracting and specializing</li> +</ul> +<p>The key is modularity versus cost of propagation. It can be partly solved when logical group of signaling agent are launched together in the same binder process. In that particular case, the cost of propagation of data between agents is reduced<a href="#fn2" class="footnoteRef" id="fnref2"><sup>2</sup></a> because there is no serialization.</p> +<p>This reduction of the propagation cost (and of the resources used) precludes implementation of strong security between the agents because they share the same memory.</p> +<h3 id="soft-composition">Soft composition</h3> +<p>The soft composition implements the business logic of high-level signaling agents as libraries that can then be used directly by the low level signaling agents.</p> +<p>Advantages:</p> +<ul> +<li>No propagation: same memory, sharing of native structures</li> +</ul> +<p>Drawbacks:</p> +<ul> +<li>Cannot be used for aggregation of several sources</li> +<li>Difficulties to abstract low-level signaling agent or to find a trade-of between abstracting and specializing</li> +<li>Source code binding not good for maintenance</li> +</ul> +<section class="footnotes"> +<hr /> +<ol> +<li id="fn1"><p>There are two aspect in using JSON: the first is the flexible data structure that mixes common types (booleans, numbers, strings, arrays, dictionaries, nulls), the second, is the streaming specification. Streaming is often seen as the bottleneck of using JSON (see http://bjson.org). When the agent share the same process, there is no streaming at all.<a href="#fnref1">↩</a></p></li> +<li id="fn2"><p>Within the same process, there is not serialization, the propagation has the cost of wrapping a json data and calling callbacks with the benefit of having a powerful callback manager: the event mechanism of the framework.<a href="#fnref2">↩</a></p></li> +</ol> +</section> +</body> +</html> diff --git a/doc/afb-events-guide.md b/doc/afb-events-guide.md new file mode 100644 index 00000000..488c52af --- /dev/null +++ b/doc/afb-events-guide.md @@ -0,0 +1,449 @@ +Guid for developing with events +------------------------------- + +Signaling agents are services that send events to any clients that +subscribed for receiving it. The sent events carry any data. + +To have a good understanding of how to write a signaling agent, the +actions of subscribing, unsubscribing, producing, sending, receiving +events must be described and explained. + +The basis of a signaling agent is shown on the following figure: + +![scenario of using events](signaling-basis.svg) + +This figure shows the main role of the signaling framework for the +propagation of events. + +For people not familiar with the framework, a signaling agent and +a “binding” are similar. + +### Subscribing and unsubscribing + +Subscribing and subscription is the action that makes a client able to +receive data from a signaling agent. Subscription must create resources +for generating the data and for delivering the data to the client. These +two aspects are not handled by the same piece of software: generating +the data is the responsibility of the developer of the signaling agent +while delivering the data is handled by the framework. + +When a client subscribes for data, the agent must: + +1. check that the subscription request is correct; +2. establish the computation chain of the required data, if not already + done; +3. create a named event for the computed data, if not already done; +4. ask the framework to establish the subscription to the event for the + request; +5. optionally give indications about the event in the reply to + the client. + +The first two steps are not involving the framework. They are linked to +the business logic of the binding. The request can be any description of +the requested data and the computing stream can be of any nature, this +is specific to the binding. + +As said before, the framework uses and integrates “libsystemd” and its event +loop. Within the framework, "libsystemd" is the standard API/library for +bindings expecting to setup and handle I/O, timer or signal events. + +Steps 3 and 4 are bound to the framework. + +The agent must create an object for handling the propagation of produced +data to its clients. That object is called “event” in the framework. An +event has a name that allows clients to distinguish it from other +events. + +Events are created using the ***afb\_daemon\_make\_event*** function +that takes the name of the event. Example: + +```C + event = afb_daemon_make_event(afb_daemon, name); +``` + +Once created, the event can be used either to push data to its +subscribers or to broadcast data to any listener. + +The event must be used to establish the subscription for the requesting +client. This is done using the ***afb\_req\_subscribe*** function +that takes the current request object and event and associates them +together. Example: + +```C + rc = afb_req_subscribe(afb_req, event); +``` + +When successful, this function make the connection between the event and +the client that emitted the request. The client becomes a subscriber of +the event until it unsubscribes or disconnects. The +***afb\_req\_subscribe*** function will fail if the client +connection is weak: if the request comes from a HTTP link. To receive +signals, the client must be connected. The AGL framework allows +connections using WebSocket. + +The name of the event is either a well known name or an ad hoc name +forged for the usecase. + +Let's see a basic example: client A expects to receive the speed in km/h +every second while client B expects the speed in mph twice a second. In +that case, there are two different events because it is not the same +unit and it is not the same frequency. Having two different events +allows to associate clients to the correct event. But this doesn't tell +any word about the name of these events. The designer of the signaling +agent has two options for naming: + +1. names can be the same (“speed” for example) with sent data + self-describing itself or having a specific tag (requiring from + clients awareness about requesting both kinds of speed isn't safe). +2. names of the event include the variations (by example: + “speed-km/h-1Hz” and “speed-mph-2Hz”) and, in that case, sent data + can self-describe itself or not. + +In both cases, the signaling agent might have to send the name of the +event and/or an associated tag to its client in the reply of the +subscription. This is part of the step 5 above. + +The framework only uses the event (not its name) for subscription, +unsubscription and pushing. + +When the requested data is already generated and the event used for +pushing it already exists, the signaling agent must not instantiate a +new processing chain and must not create a new event object for pushing +data. The signaling agent must reuse the existing chain and event. + +Unsubscribing is made by the signaling agent on a request of its client. +The ***afb\_req\_unsubscribe*** function tells the framework to +remove the requesting client from the event's list of subscribers. +Example: + +```C + afb_req_unsubscribe(afb_req, event); +``` + +Subscription count does not matter to the framework: subscribing the +same client several times has the same effect that subscribing only one +time. Thus, when unsubscribing is invoked, it becomes immediately +effective. + +#### More on naming events + +Within the AGL framework, a signaling agent is a binding that has an API +prefix. This prefix is meant to be unique and to identify the binding +API. The names of the events that this signaling agent creates are +automatically prefixed by the framework, using the API prefix of the +binding. + +Thus, if a signaling agent of API prefix ***api*** creates an event +of name ***event*** and pushes data to that event, the subscribers +will receive an event of name ***api/event***. + +### Generating and pushing signals and data + +This of the responsibility of the designer of the signaling agent to +establish the processing chain for generating events. In many cases, +this can be achieved using I/O or timer or signal events inserted in the +main loop. For this case, the AGL framework uses “libsystemd” and +provide a way to integrates to the main loop of this library using +afb\_daemon\_get\_event\_loop. Example: + +```C + sdev = afb_daemon_get_event_loop(af_daemon); + rc = sd_event_add_io(sdev, &source, fd, EPOLLIN, myfunction, NULL); +``` + +In some other cases, the events are coming from D-Bus. In that case, the +framework also uses “libsystemd” internally to access D-Bus. It provides +two methods to get the available D-Bus objects, already existing and +bound to the main libsystemd event loop. Use either +***afb\_daemon\_get\_system\_bus*** or +***afb\_daemon\_get\_user\_bus*** to get the required instance. Then +use functions of “libsystemd” to handle D-Bus. + +In some rare cases, the generation of the data requires to start a new +thread. + +When a data is generated and ready to be pushed, the signaling agent +should call the function ***afb\_event\_push***. Example: + +```C + rc = afb_event_push(event, json); + if (rc == 0) { + stop_generating(event); + afb_event_drop(event); + } +``` + +The function ***afb\_event\_push*** pushes json data to all the +subscribers. It then returns the count of subscribers. When the count is +zero, there is no subscriber listening for the event. The example above +shows that in that case, the signaling agent stops to generate data for +the event and delete the event using afb\_event\_drop. This is one +possible option. Other valuable options are: do nothing and continue to +generate and push the event or just stop to generate and push the data +but keep the event existing. + +### Receiving the signals + +Understanding what a client expects when it receives signals, events or +data shall be the most important topic of the designer of a signaling +agent. The good point here is that because JSON[^1] is the exchange +format, structured data can be sent in a flexible way. + +The good design is to allow as much as possible the client to describe +what is needed with the goal to optimize the processing to the +requirements only. + +### The exceptional case of wide broadcast + +Some data or events have so much importance that they can be widely +broadcasted to alert any listening client. Examples of such an alert +are: + +- system is entering/leaving “power safe” mode +- system is shutting down +- the car starts/stops moving +- ... + +An event can be broadcasted using one of the two following methods: +***afb\_daemon\_broadcast\_event*** or +***afb\_event\_broadcast***. + +Example 1: + +```C + afb_daemon_broadcast_event(afb_daemon, name, json); +``` + +Example 2: + +```C + event = afb_daemon_make_event(afb_daemon, name); + . . . . + afb_event_broadcast(event, json); +``` + +As for other events, the name of events broadcasted using +***afb\_daemon\_broadcast\_event*** are automatically prefixed by +the framework with API prefix of the binding (signaling agent). + +Reference of functions +---------------------- + +### Function afb\_event afb\_daemon\_make\_event + +The function ***afb\_daemon\_make\_event*** that is defined as below: + +```C +/* + * Creates an event of 'name' and returns it. + * 'daemon' MUST be the daemon given in interface when activating the binding. + */ +struct afb_event afb_daemon_make_event(struct afb_daemon daemon, const char *name); +``` + +The daemon is the handler to the application framework binder daemon +received during initialisation steps of the binding. + +Calling the function ***afb\_daemon\_make\_event*** within the initialisation +function ***afbBindingV1Register*** will _fail_ because the plugin +name is not known at this time. + +The correct way to create the event at initialisation is to call the function +***afb\_daemon\_make\_event*** within the initialisation +function ***afbBindingV1ServiceInit***. + +### Function afb\_event\_push + +The function ***afb\_event\_push*** is defined as below: + +```C +/* + * Pushes the 'event' with the data 'object' to its observers. + * 'object' can be NULL. + * + * For convenience, the function calls 'json_object_put' for object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Returns the count of clients that received the event. + */ +int afb_event_push(struct afb_event event, struct json_object *object); +``` + +As the function ***afb\_event\_push*** returns 0 when there is no +more subscriber, a binding can remove such unexpected event using the +function ***afb\_event\_drop***. + +### Function afb\_event\_drop + +The function ***afb\_event\_drop*** is defined as below: + +```C +/* + * Drops the data associated to the event + * After calling this function, the event + * MUST NOT BE USED ANYMORE. + */ +void afb_event_drop(struct afb_event event); +``` + +### Function afb\_req\_subscribe + +The function ***afb\_req\_subscribe*** is defined as below: + +```C +/* + * Establishes for the client link identified by 'req' a subscription + * to the 'event'. + * Returns 0 in case of successful subscription or -1 in case of error. + */ +int afb_req_subscribe(struct afb_req req, struct afb_event event); +``` + +The subscription adds the client of the request to the list of subscribers +to the event. + +### Function afb\_req\_unsubscribe + +The function ***afb\_req\_unsubscribe*** is defined as +below: + +```C +/* + * Revokes the subscription established to the 'event' for the client + * link identified by 'req'. + * Returns 0 in case of successful unsubscription or -1 in case of error. + */ +int afb_req_unsubscribe(struct afb_req req, struct afb_event event); +``` + +The unsubscription removes the client of the request of the list of subscribers +to the event. +When the list of subscribers to the event becomes empty, +the function ***afb\_event\_push*** will return zero. + +### Function afb\_event\_broadcast + +The function ***afb\_event\_broadcast*** is defined as below: + +```C +/* + * Broadcasts widely the 'event' with the data 'object'. + * 'object' can be NULL. + * + * For convenience, the function calls 'json_object_put' for 'object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Returns the count of clients that received the event. + */ +int afb_event_broadcast(struct afb_event event, struct json_object *object); +``` + +This uses an existing event (created with ***afb\_daemon\_make\_event***) +for broadcasting an event having its name. + + +### Function afb\_daemon\_broadcast\_event + +The function ***afb\_daemon\_broadcast\_event*** is defined as below: + +```C +/* + * Broadcasts widely the event of 'name' with the data 'object'. + * 'object' can be NULL. + * 'daemon' MUST be the daemon given in interface when activating the binding. + * + * For convenience, the function calls 'json_object_put' for 'object'. + * Thus, in the case where 'object' should remain available after + * the function returns, the function 'json_object_get' shall be used. + * + * Returns the count of clients that received the event. + */ +int afb_daemon_broadcast_event(struct afb_daemon daemon, const char *name, struct json_object *object); +``` + +The name is given here explicitely. The name is automatically prefixed +with the name of the binding. For example, a binding of prefix "xxx" +would broadcat the event "xxx/name". + + +Architectural digressions +------------------------- + +Based on their dependencies to hardware, signaling agents can be split +into 2 categories: low-level signaling agents and high-level signaling +agents. + +Low-level signaling agents are bound to the hardware and focused on +interfacing and driving. + +High-level signaling agent are independent of the hardware and ocused on +providing service. + +This separation (that may in the corner look artificial) aim to help in +the systems design. The main idea here is that high-level signaling +agents are providing “business logic”, also known as “application +logic”, that is proper to the car industry and that can be reused and +that can evolve as a foundation for the future of the industry. + +The implementation of this decomposition may follow 2 paths: strict +separation or soft composition. + +### Strict separation + +The strict separation implements the modularity composition of signaling +agent through the framework. The high-level signaling agent subscribes +to the low level signaling agent using the standard client API. + +Advantages: + +- Modularity +- Separation of responsibilities +- Possible aggregation of multiple sources +- Soft binding of agent good for maintenance + +Drawbacks: + +- Cost of propagation of data (might serialize) +- Difficulties to abstract low-level signaling agent or to find a + trade-of between abstracting and specializing + +The key is modularity versus cost of propagation. It can be partly +solved when logical group of signaling agent are launched together in +the same binder process. In that particular case, the cost of +propagation of data between agents is reduced[^2] because there is no +serialization. + +This reduction of the propagation cost (and of the resources used) +precludes implementation of strong security between the agents because +they share the same memory. + +### Soft composition + +The soft composition implements the business logic of high-level +signaling agents as libraries that can then be used directly by the low +level signaling agents. + +Advantages: + +- No propagation: same memory, sharing of native structures + +Drawbacks: + +- Cannot be used for aggregation of several sources +- Difficulties to abstract low-level signaling agent or to find a + trade-of between abstracting and specializing +- Source code binding not good for maintenance + +[^1]: There are two aspect in using JSON: the first is the flexible data + structure that mixes common types (booleans, numbers, strings, + arrays, dictionaries, nulls), the second, is the streaming + specification. Streaming is often seen as the bottleneck of using + JSON (see http://bjson.org). When the agent share the same process, + there is no streaming at all. + +[^2]: Within the same process, there is not serialization, the + propagation has the cost of wrapping a json data and calling + callbacks with the benefit of having a powerful callback manager: + the event mechanism of the framework. diff --git a/doc/afb-overview.html b/doc/afb-overview.html index caf11d81..707443cc 100644 --- a/doc/afb-overview.html +++ b/doc/afb-overview.html @@ -7,10 +7,10 @@ <meta name="author" content="José Bollo"> <title>Overview of AFB-DAEMON</title> <style type="text/css">code{white-space: pre;}</style> + <link rel="stylesheet" href="doc.css"> <!--[if lt IE 9]> - <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> + <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> <![endif]--> - <link rel="stylesheet" href="doc.css"> </head> <body> <header> diff --git a/doc/afb-tests-overview.html b/doc/afb-tests-overview.html index 549fe492..27231a08 100644 --- a/doc/afb-tests-overview.html +++ b/doc/afb-tests-overview.html @@ -8,10 +8,10 @@ <meta name="author" content="José Bollo"> <title>Overview of tests shipped with AFB-Daemon</title> <style type="text/css">code{white-space: pre;}</style> + <link rel="stylesheet" href="doc.css"> <!--[if lt IE 9]> - <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> + <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> <![endif]--> - <link rel="stylesheet" href="doc.css"> </head> <body> <header> diff --git a/doc/doc.css b/doc/doc.css index 8c327c09..f3d55f9d 100644 --- a/doc/doc.css +++ b/doc/doc.css @@ -35,3 +35,6 @@ table { thead {background-color: #a6f;} tr:nth-child(even) {background-color: #aee;} td { padding: 0.1em 0.5em; } + +figure img {width: 80%;} +figure {text-align: center;} |