aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCorentinLGS <corentinlgs@gmail.com>2018-08-07 14:23:11 +0200
committerRomain Forlot <romain.forlot@iot.bzh>2018-12-13 15:02:55 +0100
commit80ec58eac2bd1da4236b59c0f0c1042c50d99790 (patch)
treece622f3888d340ff44049401de4822c417e631fe
parent80596c5dddf5174f485a963eac8fc7d1a1058e22 (diff)
Controller: Converted README to a gitbook version.
-Split the README into a gitbook documentation. Change-Id: Iacf94e94d15606d68c925885dac5233d828f29b7 Signed-off-by: CorentinLGS <corentinlgs@gmail.com>
-rw-r--r--README.md422
-rw-r--r--book.json104
-rw-r--r--docs/README.md19
-rw-r--r--docs/SUMMARY.md6
-rw-r--r--docs/Usage.md53
-rw-r--r--docs/_layouts/ebook/page.html36
-rw-r--r--docs/_layouts/ebook/pdf_footer.html13
-rw-r--r--docs/_layouts/ebook/pdf_header.html13
-rw-r--r--docs/_layouts/ebook/summary.html58
-rw-r--r--docs/_layouts/layout.html28
-rw-r--r--docs/configSample.md98
-rw-r--r--docs/controller.md28
-rw-r--r--docs/controllerConfig.md241
-rw-r--r--docs/resources/cover.svg210
-rw-r--r--docs/resources/ebook.css402
-rwxr-xr-xdocs/resources/make_cover.sh27
-rwxr-xr-xgendocs.sh90
17 files changed, 1431 insertions, 417 deletions
diff --git a/README.md b/README.md
index a6b8f19..f91a8c3 100644
--- a/README.md
+++ b/README.md
@@ -1,423 +1,11 @@
-# Controller
+# App Controller
-* Object: Generic Controller to handle Policy,Small Business Logic, Glue in between components, ...
-* Status: Release Candidate
-* Author: Fulup Ar Foll fulup@iot.bzh
-* Date : May-2018
+Here you will find a guide on how to install, configure and use the controller.
-## Features
-
-* Create a controller application from a JSON config file
-* Each control (eg: navigation, multimedia, ...) is a suite of actions. When all actions succeed control is granted, if one fails control access is denied.
-* Actions can either be:
- * Invocation of an other binding API, either internal or external (eg: a policy service, Alsa UCM, ...)
- * C routines from a user provided plugin (eg: policy routine, proprietary code, ...)
- * Lua script function. Lua provides access to every AGL appfw functionality and can be extended by plugins written in C.
-
-## Installation
-
-* Controller can easily be included as a git submodule in any AGL service or application binder.
-* Dependencies: the only dependencies are AGL application framework (https://gerrit.automotivelinux.org/gerrit/p/src/app-framework-binder.git) and app-afb-helpers-submodule (https://gerrit.automotivelinux.org/gerrit/p/apps/app-afb-helpers-submodule.git).
-* Controller relies on Lua-5.3, when not needed Lua might be removed at compilation time.
-
-## Monitoring
-
-* The default test HTML page expect the monitoring HTML page to be accessible under /monitoring with the --monitoring option.
-* The monitoring HTML pages are installed with the app framework binder in a subdirectory called monitoring.
-* You can add other HTML pages with the alias options e.g. afb-daemon --port=1234 --monitoring --alias=/path1/to/htmlpages:/path2/to/htmlpages --ldpaths=. --workdir=. --roothttp=../htdocs
-* The monitoring is accessible at http://localhost:1234/monitoring.
-
-## Controller binding configuration
-
-By default the controller searches for a config filename with the same 'middlename' as the daemon process. As an example if your process name is afb-daemon then middle name is 'daemon'. In addition, if your process name is afb-daemon-audio the middle name is also 'daemon'. Moreover the prefix is chosen when you call the [CtlConfigSearch](<#4)_Do_controller_config_parsing_at_binding_pre-init>) function, see below:
-
-```bash
-CtlConfigSearch(AFB_ApiT apiHandle, const char *dirList, const char *prefix)
-```
-
-```bash
-# Middlename is taken from process middlename.
-(prefix-)middlename*.json
-```
-
-You may overload the config search path with environment variables
-
-* **CONTROL_CONFIG_PATH**: change default reserch path for configuration. You may provide multiple directories separated by ':'.
-* **CONTROL_LUA_PATH**: same as CONTROL_CONFIG_PATH but for Lua script files.
-
-Example: to load a config named '(prefix-)myconfig-test.json' do
-
-```bash
-afb-daemon --name myconfig --verbose ...'
-```
-
-The configuration is loaded dynamically during startup time. The controller scans **CONTROL_CONFIG_PATH** for a file corresponding to the pattern
-"(prefix-)bindermiddlename*.json". The first file found in the path is loaded,
-any other file corresponding to the same path is ignored and only generates a warning.
-
-Each block in the configuration file is defined with
-
-* **uid**: mandatory, it is used either for debugging or as input for the action (eg: signal name, control name, ...)
-* **info**: optional, it is used for documentation purpose only
-
-> **Note**: by default the controller config search path is defined at compilation time, but the path might be overloaded with the **CONTROL_CONFIG_PATH**
-> environment variable.
-
-### Config is organised in sections
-
-* **metadata**: describes the configuration
-* **plugins or resources**: defines the set of functions provided by the plugins allowing to load additionnal resources (compiled C or lua)
-* **onload**: a collection of actions meant to be executed at startup time
-* **control**: sets the controls with a collection of actions, in dynamic api it could also specify the verbs of the api
-* **event**: a collection of actions meant to be executed when receiving a given signal
-* **personnal sections**: personnal section
-
-Callbacks to parse sections are documented in [Declare your controller config section in your binding](<#3_Declare_your_controller_config_section_in_your_binding>) section. You can use the callback defined in controller or define your own callback.
-
-### Metadata
-
-As today matadata is only used for documentation purpose.
-
-* **uid**: mandatory
-* **version**: mandatory
-* **api**: mandatory
-* **info**: optional
-* **require**: optional
-* **author**: optional
-* **date**: optional
-
-### OnLoad section
-
-Onload section defines startup time configuration. Onload may provide multiple initialisation
-profiles, each with a different uid.
-
-You can define the following keys or arrays of the following keys:
-
-* **uid**: mandatory.
-* **info**: optional
-* **action**: mandatory
-* **args**: optionnal
-
-### Control section
-
-Control section defines a list of controls that are accessible.
-
-You can define the following keys or arrays of the following keys, moreover
-this section could be verb api:
-
-* **uid**: mandatory
-* **info**: optional
-* **action**: the list of actions is mandatory
-
-### Event section
-
-Event section defines a list of actions to be executed on event reception. Event can do
-anything a controller can (change state, send back signal, ...)
-eg: if a controller subscribes to vehicle speed, then speed-event may adjust
-master-volume to speed.
-
-You can define the following keys or arrays of the following keys, moreover you can define an event from an another API with the following syntax "API/event".
-
-* **uid**: mandatory
-* **info**: optional
-* **action**: the list of actions is mandatory
-
-### Plugin section
-
-Plugin section defines plugins used with this controller. A plugin is a C/C++ program meant to
-execute some tasks after an event or on demand. This easily extends intrinsec
-binding logic for ad-hoc needs.
-
-You can define the following keys or arrays of the following keys:
-
-* **uid**: mandatory
-* **info**: optionnal
-* **spath**: optionnal, semicolon separated paths where to find the plugin. This could be a compiled shared library or LUA scripts. Could be specified using CONTROL_PLUGIN_PATH environment variable also.
-* **libs**: mandatory, Plugin file or LUA scripts to load
-* **lua**: optionnal, C functions that could be called from a LUA script
-
-### Personnal sections
-
-* **uid**: mandatory
-* **info**: optionnal
-* **action**: mandatory
-* **any keys wanted**: optionnal
-
-You can define your own sections and add your own callbacks into the
-CtlSectionT structure, see
-[Declare your controller config section in your binding](<#3_Declare_your_controller_config_section_in_your_binding>) section.
-
-### Actions Categories
-
-Controller supports three categories of actions. Each action returns a status
-where 0=success and 1=failure.
-
-* **AppFw API** provides a generic model to request other bindings. Requested bindings can be local (eg: ALSA/UCM) or external (eg: vehicle signalling).
- * `"action": "api://API_NAME#verb_name"`
-* C-API, when defined in the onload section, the plugin may provide C native API with `CTLP-CAPI(apiname, uid, args, query, context)`. Plugin may also create Lua command with `CTLP-LUA2C(LuaFuncName, uid, args, query, context)`. Where `args`+`query` are JSON-C object and context is the returned value from `CTLP_ONLOAD` function. Any missing value is set to NULL.
- * `"action": "plugin://plugin_name#function_name"`
-* Lua-API, when compiled with Lua option, the controller supports action defined directly in Lua script. During "*onload*" phase, the controller searches in `CONTROL_LUA_PATH` file with pattern "(prefix-)bindermiddlename*.lua". Any file corresponding to this pattern is automatically loaded. Any function defined in those Lua scripts can be called through a controller action. Lua functions receive three parameters (uid, args, query).
- * `"action": "lua://plugin_name#function_name"`
-
-You also can add the **privileges** property that handles AGL permission
-needed to be able to call this action.
-
-> **Note**: Lua added functions are systematically prefixed. AGL standard AppFw
-functions are prefixed with AGL: (eg: AFB:notice(), AFB:success(), ...).
-> User Lua functions added through the plugin and CTLP_LUA2C are prefixed with
-the plugin uid or the one you defined in your config (eg: MyPlug:HelloWorld1).
-
-### Available Application Framework Commands
-
-Each Lua AppFw commands should be prefixed by AFB:
-
-* `AFB:notice ("format", arg1,... argn)` directly printed LUA tables as json string with '%s'.
- `AFB:error`, `AFB:warning`, `AFB:info`, `AFB:debug` work on the same model. Printed messages are limited to 512 characters.
-
-* `AFB:service ('API', 'VERB', {query}, "Lua_Callback_Name", {context})` is an asynchronous call to another binding. When empty, query/context should be set to '{}'
- and not to 'nil'. When 'nil', Lua does not send 'NULL' value but removes arguments to calling stack. WARNING:"Callback"
- is the name of the callback as a string and not a pointer to the callback. (If someone as a solution to fix this, please
- let me known). Callback is call as LUA "function Alsa_Get_Hal_CB (error, result, context)" where:
- * error is a Boolean
- * result is the full answer from AppFw (do not forget to extract the response)
- * context is a copy of the Lua table pass as an argument (warning it's a copy not a pointer to original table)
-
-* `error,result=AFB:servsync('API', 'VERB', {query})` is saved as previous but for synchronous call. Note that Lua accepts multiple
- returns. AFB:servsync returns both the error message and the response as a Lua table. Like for AFB:service, the user should not
- forget to extract response from result.
-
-* `AFB:success(request, response)` is the success request. request is the opaque handle passes when Lua is called from (api="control", verb="docall").
- Response is a Lua table that will be returned to the client.
-
-* `AFB:fail(request, response)` is the same as for success. Note that LUA generates automatically the error code from Lua function name.
- The response is transformed into a json string before being returned to the client.
-
-* `EventHandle=AFB:evtmake("MyEventName")` creates an event and returns the handle as an opaque handle. Note that due to a limitation
- of json_object, this opaque handle cannot be passed as an argument in a callback context.
-
-* `AFB:subscribe(request, MyEventHandle)` subscribes a given client to a previously created event.
-
-* `AFB:evtpush (MyEventHandle, MyEventData)` pushes an event to every subscribed client. MyEventData is a Lua table that will be
- sent as a json object to the corresponding clients.
-
-* `timerHandle=AFB:timerset (MyTimer, "Timer_Test_CB", context)` initialises a timer from MyTimer Lua table. This table should contains 3 elements:
- MyTimer={[l"abel"]="MyTimerName", ["delay"]=timeoutInMs, ["count"]=nBOfCycles}. Note that if count==0 then timer is cycled
- infinitely. Context is a standard Lua table. This function returns an opaque handle to be used to further control the timer.
-
-* `AFB:timerclear(timerHandle)` kills an existing timer. Returns an error when timer does not exit.
-
-* `MyTimer=AFB:timerget(timerHandle)` returns uid, delay and count of an active timer. Returns an error when timerHandle does not
- point on an active timer.
-
-* `AFB:GetEventLoop()` retrieves the common systemd's event loop of AFB.
-
-* `AFB:RootDirGetFD()` gets the root directory file descriptor. This file descriptor can be used with functions 'openat', 'fstatat', ...
-
-> **Note**: Except for functions call during binding initialisation period. Lua calls are protected and should returned clean messages
-> even when they are improperly used. If you find bug please report.
-
-### Adding Lua command from User Plugin
-
-User Plugin is optional and may provide either native C-action accessible directly from controller actions as defined in
-JSON config file, or alternatively may provide a set of Lua commands usable inside any script (onload, control,event). A simple
-plugin that provides both notice C API and Lua commands is provided as example (see ctl-plugin-sample.c). Technically a
-plugin is a simple sharelibrary and any code fitting in sharelib might be used as a plugin. Developer should nevertheless
-not forget that except when no-concurrency flag was at binding construction time, any binding should to be thread safe.
-
-A plugin must be declared with `CTLP_REGISTER("MyCtlSamplePlugin")`. This entry point defines a special structure that is checked
-at plugin load time by the controller. Then you have an optional init routine declare with `CTLP_ONLOAD(plugin, handle)`.
- The init routine may create
-a plugin context that is later presented to every plugin API, this for both LUA and native C ones. Then each:
-
-* C API declare with `CTLP_CAPI(MyCFunction, source, argsJ, queryJ) {your code}`. Where:
- * **MyFunction** is your function
- * **source** is the structure config
- * **argsJ** a json_object containing the argument attaches to this control in JSON config file
- * **queryJ** a json_object
-
-* Lua API declare with `CTLP_LUA2C(MyLuaCFunction, source, argsJ, responseJ) {your code}`. Where
- * **MyLuaCFunction** is both the name of your C function and Lua command
- * **source** is the structure config
- * **argsJ** the arguments passed this time from Lua script and not from Json config file.
- * **responseJ** if success the argument is passed into the request.
-
-> **Warning**: Lua samples use with controller enforce strict mode. As a result every variable should be declared either as
-> local or as global. Unfortunately "luac" is not smart enough to handle strict mode at build time and errors only appear
-> at run time. Because of this strict mode every global variables (which include functions) should be prefixed by '_'.
-> Note that LUA requires an initialisation value for every variables and declaring something like "local myvar" will not
-> allocate "myvar".
-
-### Debugging Facilities
-
-Controller Lua scripts are checked for syntax from CMAKE template with Luac. When needed to go further, a developer API should be allowed to
-execute directly Lua commands within the controller context from Rest/Ws (api=control, verb=lua_doscript). DoScript API takes two
-other optional arguments func=xxxx where xxxx is the function to execute within Lua script and args, a JSON object to provide
-input parameters. When funcname is not given by default, the controller tries to execute middle filename doscript-xxxx-????.lua.
-
-When executed from the controller, Lua script may use any AppFw Apis as well as any L2C user defines commands in plugin.
-
-### Running as Standalone Controller
-
-The controller is a standard binding. It can be started with the following command:
+To generate the doc enter:
```bash
-afb-daemon --name=yourname --port=1234 --workdir=. --roothttp=./htdocs --tracereq=common --token= --verbose --binding=pathtoyourbinding.so --monitoring
-```
-
-Afb-Daemon only loads controller bindings without searching for the other
-binding. In this case, the controller binding will search for a configuration file
-name '(prefix-)bindermiddlename*.json'. This model can be used to implement for testing
-purpose or simply to act as the glue between a UI and other binder/services.
-
-## Usage
-
-### 1) Add app-controller-submodule as a submodule to include in your project
-
-```bash
-git submodule add https://gerrit.automotivelinux.org/gerrit/apps/app-controller-submodule
-```
-
-### 2) Add app-controller-submodule as a static library to your binding
-
-```cmake
- # Library dependencies (include updates automatically)
- TARGET_LINK_LIBRARIES(${TARGET_NAME}
- ctl-utilities
- ... other dependencies ....
- )
-```
-
-### 3) Declare your controller config section in your binding
-
-```C
-// CtlSectionT syntax:
-// key: "section name in config file"
-// loadCB: callback to process section
-// handle: a void* pass to callback when processing section
-static CtlSectionT ctlSections[]= {
- {.key="plugins" , .loadCB= PluginConfig, .handle= &halCallbacks},
- {.key="onload" , .loadCB= OnloadConfig},
- {.key="halmap" , .loadCB= MapConfigLoad},
- {.key=NULL}
-};
-
-```
-
-### 4) Do the controller config parsing at binding pre-init
-
-```C
- // check if config file exist
- const char *dirList= getenv("CTL_CONFIG_PATH");
- if (!dirList) dirList=CONTROL_CONFIG_PATH;
-
- const char *configPath = CtlConfigSearch(apiHandle, dirList, "prefix");
- if(!confiPath) return -1;
-
- ctlConfig = CtlConfigLoad(dirList, ctlSections);
- if (!ctlConfig) return -1;
+./gendocs.sh pdf
```
-### 5) Execute the controller config during binding init
-
-```C
- int err = CtlConfigExec (ctlConfig);
-```
-
-## Config Sample
-
-Here after a simple configuration sample.
-
-```json
-{
- "$schema": "http://iot.bzh/download/public/schema/json/ctl-schema.json",
- "metadata": {
- "uid": "sample-audio-control",
- "api": "audio-control",
- "info": "Provide Default Audio Policy for Multimedia, Navigation and Emergency",
- "version": "1.0",
- "require": ["intel-hda", "jabra-usb", "scarlett-usb"]
- },
- "plugins": {
- "uid" : "MyPlug",
- "spath":"./plugins/pluginname:../conf.d/project/lua.d",
- "libs": ["ctl-audio-plugin-sample.ctlso", "softmixer-simple.lua"],
- "lua": ["Lua2cHelloWorld1", "Lua2cHelloWorld2"]
- },
- "onload": [{
- "uid": "onload-sample-cb",
- "info": "Call control sharelib install entrypoint",
- "action": "lua://MyPlug#SamplePolicyInit",
- "args": {
- "arg1": "first_arg",
- "nextarg": "second arg value"
- }
- }, {
- "uid": "onload-sample-api",
- "info": "Assert AlsaCore Presence",
- "action": "api://alsacore#ping",
- "args": {
- "test": "onload-sample-api"
- }
- }
- ],
- "controls":[{
- "uid": "multimedia",
- "privileges": "urn:AGL:permission:audio:public:mutimedia",
- "action": "lua://MyPlug#Audio_Set_Multimedia"
- }, {
- "uid": "navigation",
- "privileges": "urn:AGL:permission:audio:public:navigation",
- "action": "lua://MyPlug#Audio_Set_Navigation"
- }, {
- "uid": "emergency",
- "privileges": "urn:AGL:permission:audio:public:emergency",
- "action": "lua://MyPlug#Audio_Set_Emergency"
- }, {
- "uid": "multimedia-control-cb",
- "info": "Call Sharelib Sample Callback",
- "action": "plugin://MyPlug#sampleControlNavigation",
- "args": {
- "arg1": "snoopy",
- "arg2": "toto"
- }
- }, {
- "uid": "navigation-control-ucm",
- "action": "api://alsacore#ping",
- "args": {
- "test": "navigation"
- }
- }, {
- "uid": "navigation-control-lua",
- "info": "Call Lua Script to set Navigation",
- "action": "lua://MyPlug#Audio_Set_Navigation"
- }
- ],
- "events":[{
- "uid": "speed-action-1",
- "action": "plugin://MyPlug#Blink-when-over-130",
- "args": {
- "speed": 130,
- "blink-speed": 1000
- }
- }, {
- "uid": "Adjust-Volume",
- "action": "lua://MyPlug#Adjust_Volume_To_Speed"
- }, {
- "uid": "Display-Rear-Camera",
- "action": "plugin://MyPlug#Display-Rear-Camera"
- }, {
- "uid": "Prevent-Phone-Call",
- "action": "api://phone#status",
- "args": {
- "call-accepted": "false"
- }
- }, {
- "uid": "Authorize-Video",
- "action": "api://video#status",
- "args": {
- "tv-accepted": "true"
- }
- }
- ]
-}
-```
+ to generate it in pdf version, or change *pdf* to *serve* to have it as a web site. \ No newline at end of file
diff --git a/book.json b/book.json
new file mode 100644
index 0000000..eb823f4
--- /dev/null
+++ b/book.json
@@ -0,0 +1,104 @@
+{
+ "title": "APP Controller Guide",
+ "subtitle": "Developer Documentation",
+ "description": "This is the utilisation guide for the APP Controller",
+ "keywords": "AGL, Development, Iotbzh, Controller",
+ "author": "IoT.Bzh Team",
+ "website": "http://iot.bzh",
+ "published": "August 2018",
+ "version": "1.0",
+
+ "gitbook": "3.2.2",
+ "root": "docs",
+ "pdf": {
+ "fontFamily": "Verdana",
+ "fontSize": 12,
+ "paperSize": "a4",
+ "pageBreaksBefore": "//h:div[@class=\"page-break\"]"
+ },
+ "styles": {
+ "website": "resources/ebook.css",
+ "ebook": "resources/ebook.css",
+ "pdf": "resources/ebook.css"
+ },
+
+ "hidepageheaders": [2, 3],
+ "hidepagefooters": [2, 3],
+
+ "plugins": [
+ "regexplace"
+ ],
+ "pluginsConfig": {
+ "regexplace": {
+ "removeFirstPartsInSectionNumber": true,
+ "substitutes": [{
+ "pattern": "<!-- pagebreak -->",
+ "flags": "g",
+ "substitute": "<div class=\"page-break\"></div>"
+ },
+ {
+ "pattern": "<!-- clear -->",
+ "flags": "g",
+ "substitute": "<div class=\"clear\"></div>"
+ },
+ {
+ "pattern": "<!-- nopagebreak -->",
+ "flags": "g",
+ "substitute": "<div class=\"nopb\">"
+ },
+ {
+ "pattern": "<!-- endnopagebreak -->",
+ "flags": "g",
+ "substitute": "</div>"
+ },
+ {
+ "pattern": "<!-- note -->",
+ "flags": "g",
+ "substitute": "<div class=\"note\">"
+ },
+ {
+ "pattern": "<!-- endnote -->",
+ "flags": "g",
+ "substitute": "</div>"
+ },
+ {
+ "pattern": "<!-- warning -->",
+ "flags": "g",
+ "substitute": "<div class=\"warning\">"
+ },
+ {
+ "pattern": "<!-- endwarning -->",
+ "flags": "g",
+ "substitute": "</div>"
+ },
+ {
+ "pattern": "!\\[(.*?)\\]\\((.*?)(?:\\s+\"(.*)\")?\\){0,}{caption=1([^\\}]*)}",
+ "flags": "gmi",
+ "substitute": "<figure id=\"fig_PAGE_LEVEL_._INDEX_\"><img $3 alt=\"$1\" title=\"$1\" href=\"$2\"><figcaption></figcaption></figure>",
+ "decode": true
+ },
+ {
+ "pattern": "<img ([^>]*) {0,}\/{0,}> {0,}{caption=1([^\\}]*)}",
+ "flags": "g",
+ "substitute": "<figure id=\"fig_PAGE_LEVEL_._INDEX_\"><img $2 $1><figcaption></figcaption></figure>",
+ "decode": true
+ },
+ {
+ "pattern": "<img (.*)alt=\"([^\"]*)\"(.*) {0,1}\/{0,1}><figcaption></figcaption>",
+ "flags": "g",
+ "substitute": "<img$1alt=\"$2\"$3><figcaption><span>Picture _PAGE_LEVEL_._INDEX_</span>: $2</figcaption>",
+ "store": {
+ "substitute": "<a href=\"_PAGE_PATH_#fig_PAGE_LEVEL_._INDEX_\">Pic. _PAGE_LEVEL_._INDEX_</a> <span>$2</span>",
+ "variable_name": "pictures"
+ }
+ },
+ {
+ "pattern": "<img ([^>]*)> {0,}{:: {0,}style=([^}]*)}",
+ "flags": "g",
+ "substitute": "<img $1 style=$2>",
+ "decode": true
+ }
+ ]
+ }
+ }
+}
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..69822c8
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,19 @@
+# Introduction
+
+<br>
+<br>
+<br>
+<br>
+<br>
+
+| *Meta* | *Data* |
+| -- | -- |
+| **Title** | {{ config.title }} |
+| **Author** | {{ config.author }} |
+| **Description** | {{ config.description }} |
+| **Keywords** | {{ config.keywords }} |
+| **Language** | English |
+| **Published** | Published {{ config.published }} as an electronic book |
+| **Updated** | {{ gitbook.time }} |
+| **Collection** | Open-source |
+| **Website** | [{{ config.website }}]({{ config.website }}) |
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md
new file mode 100644
index 0000000..edf91f8
--- /dev/null
+++ b/docs/SUMMARY.md
@@ -0,0 +1,6 @@
+# Summary
+
+* [The Controller](controller.md)
+* [Controller Configuration](controllerConfig.md)
+* [Usage](Usage.md)
+* [Config Sample](configSample.md) \ No newline at end of file
diff --git a/docs/Usage.md b/docs/Usage.md
new file mode 100644
index 0000000..65f1ce9
--- /dev/null
+++ b/docs/Usage.md
@@ -0,0 +1,53 @@
+# Usage
+
+## 1) Add app-controller-submodule as a submodule to include in your project
+
+```bash
+git submodule add https://gerrit.automotivelinux.org/gerrit/apps/app-controller-submodule
+```
+
+## 2) Add app-controller-submodule as a static library to your binding
+
+```cmake
+ # Library dependencies (include updates automatically)
+ TARGET_LINK_LIBRARIES(${TARGET_NAME}
+ ctl-utilities
+ ... other dependencies ....
+ )
+```
+
+## 3) Declare your controller config section in your binding
+
+```C
+// CtlSectionT syntax:
+// key: "section name in config file"
+// loadCB: callback to process section
+// handle: a void* pass to callback when processing section
+static CtlSectionT ctlSections[]= {
+ {.key="plugins" , .loadCB= PluginConfig, .handle= &halCallbacks},
+ {.key="onload" , .loadCB= OnloadConfig},
+ {.key="halmap" , .loadCB= MapConfigLoad},
+ {.key=NULL}
+};
+
+```
+
+## 4) Do the controller config parsing at binding pre-init
+
+```C
+ // check if config file exist
+ const char *dirList= getenv("CTL_CONFIG_PATH");
+ if (!dirList) dirList=CONTROL_CONFIG_PATH;
+
+ const char *configPath = CtlConfigSearch(apiHandle, dirList, "prefix");
+ if(!confiPath) return -1;
+
+ ctlConfig = CtlConfigLoad(dirList, ctlSections);
+ if (!ctlConfig) return -1;
+```
+
+## 5) Execute the controller config during binding init
+
+```C
+ int err = CtlConfigExec (ctlConfig);
+``` \ No newline at end of file
diff --git a/docs/_layouts/ebook/page.html b/docs/_layouts/ebook/page.html
new file mode 100644
index 0000000..bf325e9
--- /dev/null
+++ b/docs/_layouts/ebook/page.html
@@ -0,0 +1,36 @@
+{% extends "layout.html" %}
+
+{% block title %}{{ page.title }}{% endblock %}
+{% block description %}{{ page.description }}{% endblock %}
+
+{% block style %}
+ {### Include theme css before plugins css ###}
+ {% if not fileExists(config.styles.print) %}
+ {% if options.format %}
+ <link rel="stylesheet" href="{{ (options.format + ".css")|resolveAsset }}">
+ {% else %}
+ <link rel="stylesheet" href="{{ "ebook.css"|resolveAsset }}">
+ {% endif %}
+ {% endif %}
+
+ {{ super() }}
+
+ {### Custom stylesheets for the book ###}
+
+ {% for type, style in config.styles %}
+ {% if fileExists(style) and (type == "ebook" or type == "print" or type == options.format) %}
+ <link rel="stylesheet" href="{{ style|resolveFile }}">
+ {% endif %}
+ {% endfor %}
+{% endblock %}
+
+{% block body %}
+<div class="page">
+ {% block page %}
+ <h1 class="book-chapter book-chapter-{{ page.depth }}">{{ page.title }}</h1>
+ <div class="section">
+ {{ page.content|safe }}
+ </div>
+ {% endblock %}
+</div>
+{% endblock %}
diff --git a/docs/_layouts/ebook/pdf_footer.html b/docs/_layouts/ebook/pdf_footer.html
new file mode 100644
index 0000000..679e562
--- /dev/null
+++ b/docs/_layouts/ebook/pdf_footer.html
@@ -0,0 +1,13 @@
+{% extends "./page.html" %}
+{% block body %}
+<div id="pdf-footer" class="pdf-footer">
+ <span class="footer-left">Version {{ config.version }}</span>
+ <span class="footer-right">{{ page.num }}</span>
+ <span class="footer-center">{{ config.published }}</span>
+</div>
+
+<!-- Allow to hide footer for some pages using hidepagefooters config in book.json -->
+<script>
+ if (({% for num in config.hidepagefooters %}{{ page.num }} == {{ num }} || {% endfor %}false)) document.getElementById('pdf-footer').style.display = 'none'
+</script>
+{% endblock %}
diff --git a/docs/_layouts/ebook/pdf_header.html b/docs/_layouts/ebook/pdf_header.html
new file mode 100644
index 0000000..ef49641
--- /dev/null
+++ b/docs/_layouts/ebook/pdf_header.html
@@ -0,0 +1,13 @@
+{% extends "./page.html" %}
+{% block body %}
+<div id="pdf-header" class="pdf-header">
+ <span class="header-left">IoT.Bzh</span>
+ <span class="header-right">{{ config.title }}</span>
+</div>
+
+<!-- Allow to hide header for some pages using hidepageheaders config in book.json -->
+<script>
+ if (({% for num in config.hidepageheaders %}{{ page.num }} == {{ num }} || {% endfor %}false)) document.getElementById('pdf-header').style.display = 'none'
+</script>
+
+{% endblock %} \ No newline at end of file
diff --git a/docs/_layouts/ebook/summary.html b/docs/_layouts/ebook/summary.html
new file mode 100644
index 0000000..be328a4
--- /dev/null
+++ b/docs/_layouts/ebook/summary.html
@@ -0,0 +1,58 @@
+{% extends "./page.html" %}
+
+{% block title %}{{ "SUMMARY"|t }}{% endblock %}
+
+{% macro articles(_articles) %}
+ {% for article in _articles %}
+ <li>
+ <span class="inner">
+ {% if article.path or article.url %}
+ {% if article.path %}
+ <a href="{{ article.path|contentURL }}{{ article.anchor }}">{{ article.title }}</a>
+ {% else %}
+ <a target="_blank" href="{{ article.url }}">{{ article.title }}</a>
+ {% endif %}
+ {% else %}
+ <span>{{ article.title }}</span>
+ {% endif %}
+ {% if 1 %}
+ <span class="page">{{ article.level }}</span>
+ {% endif %}
+ </span>
+ {% if article.articles.length > 0 %}
+ <ol>
+ {{ articles(article.articles) }}
+ </ol>
+ {% endif %}
+ </li>
+ {% endfor %}
+{% endmacro %}
+
+{% block page %}
+<div class="section toc">
+ <h1>{{ "SUMMARY"|t }}</h1>
+ <ol>
+ {% for part in summary.parts %}
+ {% if part.title %}
+ <li class="part-title">
+ <h2>{{ part.title }}</h2>
+ </li>
+ {% endif %}
+ {{ articles(part.articles) }}
+
+ {% if not loop.last %}
+ <li class="divider"></li>
+ {% endif %}
+ {% endfor %}
+
+ {% if glossary.path %}
+ <li>
+ <span class="inner">
+ <a href="{{ ('/' + glossary.path)|contentURL }}">{{ "GLOSSARY"|t }}</a>
+ </span>
+ </li>
+ {% endif %}
+ </ol>
+</div>
+{% endblock %}
+
diff --git a/docs/_layouts/layout.html b/docs/_layouts/layout.html
new file mode 100644
index 0000000..3d5aca6
--- /dev/null
+++ b/docs/_layouts/layout.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html lang="{{ config.language }}" {% if page.dir == "rtl" %}dir="rtl"{% endif %}>
+ <head>
+ <meta charset="UTF-8">
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+ <title>{% block title %}{{ config.title|d("GitBook", true) }}{% endblock %}</title>
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="description" content="{% block description %}{% endblock %}">
+ <meta name="generator" content="GitBook {{ gitbook.version }}">
+ {% if config.author %}<meta name="author" content="{{ config.author }}">{% endif %}
+ {% if config.isbn %}<meta name="identifier" content="{{ config.isbn }}" scheme="ISBN">{% endif %}
+ {% block style %}
+ {% for resource in plugins.resources.css %}
+ {% if resource.url %}
+ <link rel="stylesheet" href="{{ resource.url }}">
+ {% else %}
+ <link rel="stylesheet" href="{{ resource.path|resolveAsset }}">
+ {% endif %}
+ {% endfor %}
+ {% endblock %}
+
+ {% block head %}{% endblock %}
+ </head>
+ <body>
+ {% block body %}{% endblock %}
+ {% block javascript %}{% endblock %}
+ </body>
+</html>
diff --git a/docs/configSample.md b/docs/configSample.md
new file mode 100644
index 0000000..0c35044
--- /dev/null
+++ b/docs/configSample.md
@@ -0,0 +1,98 @@
+# Config Sample
+
+Here after a simple configuration sample.
+
+```json
+{
+ "$schema": "http://iot.bzh/download/public/schema/json/ctl-schema.json",
+ "metadata": {
+ "uid": "sample-audio-control",
+ "api": "audio-control",
+ "info": "Provide Default Audio Policy for Multimedia, Navigation and Emergency",
+ "version": "1.0",
+ "require": ["intel-hda", "jabra-usb", "scarlett-usb"]
+ },
+ "plugins": {
+ "uid" : "MyPlug",
+ "spath":"./plugins/pluginname:../conf.d/project/lua.d",
+ "libs": ["ctl-audio-plugin-sample.ctlso", "softmixer-simple.lua"],
+ "lua": ["Lua2cHelloWorld1", "Lua2cHelloWorld2"]
+ },
+ "onload": [{
+ "uid": "onload-sample-cb",
+ "info": "Call control sharelib install entrypoint",
+ "action": "lua://MyPlug#SamplePolicyInit",
+ "args": {
+ "arg1": "first_arg",
+ "nextarg": "second arg value"
+ }
+ }, {
+ "uid": "onload-sample-api",
+ "info": "Assert AlsaCore Presence",
+ "action": "api://alsacore#ping",
+ "args": {
+ "test": "onload-sample-api"
+ }
+ }
+ ],
+ "controls":[{
+ "uid": "multimedia",
+ "privileges": "urn:AGL:permission:audio:public:mutimedia",
+ "action": "lua://MyPlug#Audio_Set_Multimedia"
+ }, {
+ "uid": "navigation",
+ "privileges": "urn:AGL:permission:audio:public:navigation",
+ "action": "lua://MyPlug#Audio_Set_Navigation"
+ }, {
+ "uid": "emergency",
+ "privileges": "urn:AGL:permission:audio:public:emergency",
+ "action": "lua://MyPlug#Audio_Set_Emergency"
+ }, {
+ "uid": "multimedia-control-cb",
+ "info": "Call Sharelib Sample Callback",
+ "action": "plugin://MyPlug#sampleControlNavigation",
+ "args": {
+ "arg1": "snoopy",
+ "arg2": "toto"
+ }
+ }, {
+ "uid": "navigation-control-ucm",
+ "action": "api://alsacore#ping",
+ "args": {
+ "test": "navigation"
+ }
+ }, {
+ "uid": "navigation-control-lua",
+ "info": "Call Lua Script to set Navigation",
+ "action": "lua://MyPlug#Audio_Set_Navigation"
+ }
+ ],
+ "events":[{
+ "uid": "speed-action-1",
+ "action": "plugin://MyPlug#Blink-when-over-130",
+ "args": {
+ "speed": 130,
+ "blink-speed": 1000
+ }
+ }, {
+ "uid": "Adjust-Volume",
+ "action": "lua://MyPlug#Adjust_Volume_To_Speed"
+ }, {
+ "uid": "Display-Rear-Camera",
+ "action": "plugin://MyPlug#Display-Rear-Camera"
+ }, {
+ "uid": "Prevent-Phone-Call",
+ "action": "api://phone#status",
+ "args": {
+ "call-accepted": "false"
+ }
+ }, {
+ "uid": "Authorize-Video",
+ "action": "api://video#status",
+ "args": {
+ "tv-accepted": "true"
+ }
+ }
+ ]
+}
+``` \ No newline at end of file
diff --git a/docs/controller.md b/docs/controller.md
new file mode 100644
index 0000000..4012455
--- /dev/null
+++ b/docs/controller.md
@@ -0,0 +1,28 @@
+# Controller
+
+* Object: Generic Controller to handle Policy,Small Business Logic, Glue in between components, ...
+* Status: Release Candidate
+* Author: Fulup Ar Foll fulup@iot.bzh
+* Date : May-2018
+
+## Features
+
+* Create a controller application from a JSON config file
+* Each control (eg: navigation, multimedia, ...) is a suite of actions. When all actions succeed control is granted, if one fails control access is denied.
+* Actions can either be:
+ * Invocation of an other binding API, either internal or external (eg: a policy service, Alsa UCM, ...)
+ * C routines from a user provided plugin (eg: policy routine, proprietary code, ...)
+ * Lua script function. Lua provides access to every AGL appfw functionality and can be extended by plugins written in C.
+
+## Installation
+
+* Controller can easily be included as a git submodule in any AGL service or application binder.
+* Dependencies: the only dependencies are AGL application framework (https://gerrit.automotivelinux.org/gerrit/p/src/app-framework-binder.git) and app-afb-helpers-submodule (https://gerrit.automotivelinux.org/gerrit/p/apps/app-afb-helpers-submodule.git).
+* Controller relies on Lua-5.3, when not needed Lua might be removed at compilation time.
+
+## Monitoring
+
+* The default test HTML page expect the monitoring HTML page to be accessible under /monitoring with the --monitoring option.
+* The monitoring HTML pages are installed with the app framework binder in a subdirectory called monitoring.
+* You can add other HTML pages with the alias options e.g. afb-daemon --port=1234 --monitoring --alias=/path1/to/htmlpages:/path2/to/htmlpages --ldpaths=. --workdir=. --roothttp=../htdocs
+* The monitoring is accessible at http://localhost:1234/monitoring. \ No newline at end of file
diff --git a/docs/controllerConfig.md b/docs/controllerConfig.md
new file mode 100644
index 0000000..6a21fd2
--- /dev/null
+++ b/docs/controllerConfig.md
@@ -0,0 +1,241 @@
+# Controller binding configuration
+
+By default the controller searches for a config filename with the same 'middlename' as the daemon process. As an example if your process name is afb-daemon then middle name is 'daemon'. In addition, if your process name is afb-daemon-audio the middle name is also 'daemon'. Moreover the prefix is chosen when you call the [CtlConfigSearch](<#4)_Do_controller_config_parsing_at_binding_pre-init>) function, see below:
+
+```bash
+CtlConfigSearch(AFB_ApiT apiHandle, const char *dirList, const char *prefix)
+```
+
+```bash
+# Middlename is taken from process middlename.
+(prefix-)middlename*.json
+```
+
+You may overload the config search path with environment variables
+
+* **CONTROL_CONFIG_PATH**: change default reserch path for configuration. You may provide multiple directories separated by ':'.
+* **CONTROL_LUA_PATH**: same as CONTROL_CONFIG_PATH but for Lua script files.
+
+Example: to load a config named '(prefix-)myconfig-test.json' do
+
+```bash
+afb-daemon --name myconfig --verbose ...'
+```
+
+The configuration is loaded dynamically during startup time. The controller scans **CONTROL_CONFIG_PATH** for a file corresponding to the pattern
+"(prefix-)bindermiddlename*.json". The first file found in the path is loaded,
+any other file corresponding to the same path is ignored and only generates a warning.
+
+Each block in the configuration file is defined with
+
+* **uid**: mandatory, it is used either for debugging or as input for the action (eg: signal name, control name, ...)
+* **info**: optional, it is used for documentation purpose only
+
+> **Note**: by default the controller config search path is defined at compilation time, but the path might be overloaded with the **CONTROL_CONFIG_PATH**
+> environment variable.
+
+## Config is organised in sections
+
+* **metadata**: describes the configuration
+* **plugins or resources**: defines the set of functions provided by the plugins allowing to load additionnal resources (compiled C or lua)
+* **onload**: a collection of actions meant to be executed at startup time
+* **control**: sets the controls with a collection of actions, in dynamic api it could also specify the verbs of the api
+* **event**: a collection of actions meant to be executed when receiving a given signal
+* **personnal sections**: personnal section
+
+Callbacks to parse sections are documented in [Declare your controller config section in your binding](<#3_Declare_your_controller_config_section_in_your_binding>) section. You can use the callback defined in controller or define your own callback.
+
+## Metadata
+
+As today matadata is only used for documentation purpose.
+
+* **uid**: mandatory
+* **version**: mandatory
+* **api**: mandatory
+* **info**: optional
+* **require**: optional
+* **author**: optional
+* **date**: optional
+
+## OnLoad section
+
+Onload section defines startup time configuration. Onload may provide multiple initialisation
+profiles, each with a different uid.
+
+You can define the following keys or arrays of the following keys:
+
+* **uid**: mandatory.
+* **info**: optional
+* **action**: mandatory
+* **args**: optionnal
+
+## Control section
+
+Control section defines a list of controls that are accessible.
+
+You can define the following keys or arrays of the following keys, moreover
+this section could be verb api:
+
+* **uid**: mandatory
+* **info**: optional
+* **action**: the list of actions is mandatory
+
+## Event section
+
+Event section defines a list of actions to be executed on event reception. Event can do
+anything a controller can (change state, send back signal, ...)
+eg: if a controller subscribes to vehicle speed, then speed-event may adjust
+master-volume to speed.
+
+You can define the following keys or arrays of the following keys, moreover you can define an event from an another API with the following syntax "API/event".
+
+* **uid**: mandatory
+* **info**: optional
+* **action**: the list of actions is mandatory
+
+## Plugin section
+
+Plugin section defines plugins used with this controller. A plugin is a C/C++ program meant to
+execute some tasks after an event or on demand. This easily extends intrinsec
+binding logic for ad-hoc needs.
+
+You can define the following keys or arrays of the following keys:
+
+* **uid**: mandatory
+* **info**: optionnal
+* **spath**: optionnal, semicolon separated paths where to find the plugin. This could be a compiled shared library or LUA scripts. Could be specified using CONTROL_PLUGIN_PATH environment variable also.
+* **libs**: mandatory, Plugin file or LUA scripts to load
+* **lua**: optionnal, C functions that could be called from a LUA script
+
+## Personnal sections
+
+* **uid**: mandatory
+* **info**: optionnal
+* **action**: mandatory
+* **any keys wanted**: optionnal
+
+You can define your own sections and add your own callbacks into the
+CtlSectionT structure, see
+[Declare your controller config section in your binding](<#3_Declare_your_controller_config_section_in_your_binding>) section.
+
+## Actions Categories
+
+Controller supports three categories of actions. Each action returns a status
+where 0=success and 1=failure.
+
+* **AppFw API** provides a generic model to request other bindings. Requested bindings can be local (eg: ALSA/UCM) or external (eg: vehicle signalling).
+ * `"action": "api://API_NAME#verb_name"`
+* C-API, when defined in the onload section, the plugin may provide C native API with `CTLP-CAPI(apiname, uid, args, query, context)`. Plugin may also create Lua command with `CTLP-LUA2C(LuaFuncName, uid, args, query, context)`. Where `args`+`query` are JSON-C object and context is the returned value from `CTLP_ONLOAD` function. Any missing value is set to NULL.
+ * `"action": "plugin://plugin_name#function_name"`
+* Lua-API, when compiled with Lua option, the controller supports action defined directly in Lua script. During "*onload*" phase, the controller searches in `CONTROL_LUA_PATH` file with pattern "(prefix-)bindermiddlename*.lua". Any file corresponding to this pattern is automatically loaded. Any function defined in those Lua scripts can be called through a controller action. Lua functions receive three parameters (uid, args, query).
+ * `"action": "lua://plugin_name#function_name"`
+
+You also can add the **privileges** property that handles AGL permission
+needed to be able to call this action.
+
+> **Note**: Lua added functions are systematically prefixed. AGL standard AppFw
+functions are prefixed with AGL: (eg: AFB:notice(), AFB:success(), ...).
+> User Lua functions added through the plugin and CTLP_LUA2C are prefixed with
+the plugin uid or the one you defined in your config (eg: MyPlug:HelloWorld1).
+
+## Available Application Framework Commands
+
+Each Lua AppFw commands should be prefixed by AFB:
+
+* `AFB:notice ("format", arg1,... argn)` directly printed LUA tables as json string with '%s'.
+ `AFB:error`, `AFB:warning`, `AFB:info`, `AFB:debug` work on the same model. Printed messages are limited to 512 characters.
+
+* `AFB:service ('API', 'VERB', {query}, "Lua_Callback_Name", {context})` is an asynchronous call to another binding. When empty, query/context should be set to '{}'
+ and not to 'nil'. When 'nil', Lua does not send 'NULL' value but removes arguments to calling stack. WARNING:"Callback"
+ is the name of the callback as a string and not a pointer to the callback. (If someone as a solution to fix this, please
+ let me known). Callback is call as LUA "function Alsa_Get_Hal_CB (error, result, context)" where:
+ * error is a Boolean
+ * result is the full answer from AppFw (do not forget to extract the response)
+ * context is a copy of the Lua table pass as an argument (warning it's a copy not a pointer to original table)
+
+* `error,result=AFB:servsync('API', 'VERB', {query})` is saved as previous but for synchronous call. Note that Lua accepts multiple
+ returns. AFB:servsync returns both the error message and the response as a Lua table. Like for AFB:service, the user should not
+ forget to extract response from result.
+
+* `AFB:success(request, response)` is the success request. request is the opaque handle passes when Lua is called from (api="control", verb="docall").
+ Response is a Lua table that will be returned to the client.
+
+* `AFB:fail(request, response)` is the same as for success. Note that LUA generates automatically the error code from Lua function name.
+ The response is transformed into a json string before being returned to the client.
+
+* `EventHandle=AFB:evtmake("MyEventName")` creates an event and returns the handle as an opaque handle. Note that due to a limitation
+ of json_object, this opaque handle cannot be passed as an argument in a callback context.
+
+* `AFB:subscribe(request, MyEventHandle)` subscribes a given client to a previously created event.
+
+* `AFB:evtpush (MyEventHandle, MyEventData)` pushes an event to every subscribed client. MyEventData is a Lua table that will be
+ sent as a json object to the corresponding clients.
+
+* `timerHandle=AFB:timerset (MyTimer, "Timer_Test_CB", context)` initialises a timer from MyTimer Lua table. This table should contains 3 elements:
+ MyTimer={[l"abel"]="MyTimerName", ["delay"]=timeoutInMs, ["count"]=nBOfCycles}. Note that if count==0 then timer is cycled
+ infinitely. Context is a standard Lua table. This function returns an opaque handle to be used to further control the timer.
+
+* `AFB:timerclear(timerHandle)` kills an existing timer. Returns an error when timer does not exit.
+
+* `MyTimer=AFB:timerget(timerHandle)` returns uid, delay and count of an active timer. Returns an error when timerHandle does not
+ point on an active timer.
+
+* `AFB:GetEventLoop()` retrieves the common systemd's event loop of AFB.
+
+* `AFB:RootDirGetFD()` gets the root directory file descriptor. This file descriptor can be used with functions 'openat', 'fstatat', ...
+
+> **Note**: Except for functions call during binding initialisation period. Lua calls are protected and should returned clean messages
+> even when they are improperly used. If you find bug please report.
+
+## Adding Lua command from User Plugin
+
+User Plugin is optional and may provide either native C-action accessible directly from controller actions as defined in
+JSON config file, or alternatively may provide a set of Lua commands usable inside any script (onload, control,event). A simple
+plugin that provides both notice C API and Lua commands is provided as example (see ctl-plugin-sample.c). Technically a
+plugin is a simple sharelibrary and any code fitting in sharelib might be used as a plugin. Developer should nevertheless
+not forget that except when no-concurrency flag was at binding construction time, any binding should to be thread safe.
+
+A plugin must be declared with `CTLP_REGISTER("MyCtlSamplePlugin")`. This entry point defines a special structure that is checked
+at plugin load time by the controller. Then you have an optional init routine declare with `CTLP_ONLOAD(plugin, handle)`.
+ The init routine may create
+a plugin context that is later presented to every plugin API, this for both LUA and native C ones. Then each:
+
+* C API declare with `CTLP_CAPI(MyCFunction, source, argsJ, queryJ) {your code}`. Where:
+ * **MyFunction** is your function
+ * **source** is the structure config
+ * **argsJ** a json_object containing the argument attaches to this control in JSON config file
+ * **queryJ** a json_object
+
+* Lua API declare with `CTLP_LUA2C(MyLuaCFunction, source, argsJ, responseJ) {your code}`. Where
+ * **MyLuaCFunction** is both the name of your C function and Lua command
+ * **source** is the structure config
+ * **argsJ** the arguments passed this time from Lua script and not from Json config file.
+ * **responseJ** if success the argument is passed into the request.
+
+> **Warning**: Lua samples use with controller enforce strict mode. As a result every variable should be declared either as
+> local or as global. Unfortunately "luac" is not smart enough to handle strict mode at build time and errors only appear
+> at run time. Because of this strict mode every global variables (which include functions) should be prefixed by '_'.
+> Note that LUA requires an initialisation value for every variables and declaring something like "local myvar" will not
+> allocate "myvar".
+
+## Debugging Facilities
+
+Controller Lua scripts are checked for syntax from CMAKE template with Luac. When needed to go further, a developer API should be allowed to
+execute directly Lua commands within the controller context from Rest/Ws (api=control, verb=lua_doscript). DoScript API takes two
+other optional arguments func=xxxx where xxxx is the function to execute within Lua script and args, a JSON object to provide
+input parameters. When funcname is not given by default, the controller tries to execute middle filename doscript-xxxx-????.lua.
+
+When executed from the controller, Lua script may use any AppFw Apis as well as any L2C user defines commands in plugin.
+
+## Running as Standalone Controller
+
+The controller is a standard binding. It can be started with the following command:
+
+```bash
+afb-daemon --name=yourname --port=1234 --workdir=. --roothttp=./htdocs --tracereq=common --token= --verbose --binding=pathtoyourbinding.so --monitoring
+```
+
+Afb-Daemon only loads controller bindings without searching for the other
+binding. In this case, the controller binding will search for a configuration file
+name '(prefix-)bindermiddlename*.json'. This model can be used to implement for testing
+purpose or simply to act as the glue between a UI and other binder/services. \ No newline at end of file
diff --git a/docs/resources/cover.svg b/docs/resources/cover.svg
new file mode 100644
index 0000000..6726de7
--- /dev/null
+++ b/docs/resources/cover.svg
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1800"
+ height="2360"
+ viewBox="0 0 1800 2360"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="cover.svg">
+ <defs
+ id="defs4175">
+ <filter
+ style="color-interpolation-filters:sRGB"
+ inkscape:label="Drop Shadow"
+ id="filter4000">
+ <feFlood
+ result="flood"
+ flood-color="rgb(0,0,0)"
+ flood-opacity="0.475"
+ id="feFlood4002" />
+ <feComposite
+ result="composite1"
+ operator="in"
+ in="flood"
+ in2="SourceGraphic"
+ id="feComposite4004" />
+ <feGaussianBlur
+ result="blur"
+ stdDeviation="5"
+ id="feGaussianBlur4006" />
+ <feOffset
+ result="offset"
+ dy="8"
+ dx="8"
+ id="feOffset4008" />
+ <feComposite
+ result="composite2"
+ operator="over"
+ in="SourceGraphic"
+ in2="offset"
+ id="feComposite4010" />
+ </filter>
+ <filter
+ style="color-interpolation-filters:sRGB"
+ id="filter4000-6"
+ inkscape:label="Drop Shadow">
+ <feFlood
+ id="feFlood4002-4"
+ flood-opacity="0.475"
+ flood-color="rgb(0,0,0)"
+ result="flood" />
+ <feComposite
+ id="feComposite4004-6"
+ in2="SourceGraphic"
+ in="flood"
+ operator="in"
+ result="composite1" />
+ <feGaussianBlur
+ id="feGaussianBlur4006-2"
+ stdDeviation="5"
+ result="blur" />
+ <feOffset
+ id="feOffset4008-8"
+ dx="8"
+ dy="8"
+ result="offset" />
+ <feComposite
+ id="feComposite4010-9"
+ in2="offset"
+ in="SourceGraphic"
+ operator="over"
+ result="composite2" />
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.24748737"
+ inkscape:cx="928.3577"
+ inkscape:cy="404.58117"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:snap-text-baseline="false"
+ units="px"
+ inkscape:window-width="1920"
+ inkscape:window-height="1171"
+ inkscape:window-x="1920"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ showguides="false" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,1307.6379)">
+ <g
+ id="iotbzh-logo"
+ transform="matrix(2.3917866,0,0,2.3917866,216.6324,-1946.4393)"
+ inkscape:export-filename="/home/sdx/Pictures/Logo/logo_iot_bzh_100dpi.png"
+ inkscape:export-xdpi="100.22011"
+ inkscape:export-ydpi="100.22011"
+ style="display:inline;filter:url(#filter4000-6)">
+ <text
+ sodipodi:linespacing="125%"
+ id="text3557-5-3-7-0-7-3"
+ y="519.50671"
+ x="27.886671"
+ style="font-style:normal;font-weight:normal;font-size:97.09867096px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none"
+ xml:space="preserve"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:97.09867096px;line-height:125%;font-family:FreeEuro;-inkscape-font-specification:'FreeEuro Bold';text-align:start;writing-mode:lr-tb;text-anchor:start"
+ y="519.50671"
+ x="27.886671"
+ id="tspan3559-5-4-1-5-0-6"
+ sodipodi:role="line">IOT</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ d="m 286.73007,473.23356 c 28.21686,16.29102 28.75566,58.73779 0.99693,78.53831 -7.67688,5.47598 -8.77935,4.91028 -1.99529,-1.0238 17.47377,-15.28453 17.98492,-42.17775 1.08522,-57.09786 l -3.91266,-3.45435 0.72312,-3.71053 c 0.39771,-2.04076 0.5997,-5.73115 0.44885,-8.20083 -0.33876,-5.54623 0.15803,-6.49185 2.65383,-5.05094 z m -64.76568,11.40332 c 7.06047,-7.74198 18.64659,-14.16089 29.04027,-16.08874 l 6.87489,-1.27521 0.87404,2.89709 c 0.4807,1.59343 0.67439,5.2245 0.43037,8.06906 l -0.44364,5.17195 -6.13887,1.6918 c -10.91241,3.00731 -20.4022,10.85909 -25.4533,21.05979 l -2.41633,4.87984 -2.74281,-0.41238 c -5.14252,-0.77316 -12.72985,-3.97645 -12.79123,-5.40033 -0.092,-2.13451 8.34659,-15.74625 12.76661,-20.59287 z m 33.20546,36.39493 c -28.21687,16.29101 -65.24624,-4.46574 -68.51461,-38.40577 -0.9039,-9.38637 0.13723,-10.0583 1.88428,-1.21608 4.49989,22.77499 27.53453,36.66428 48.90556,29.48876 l 4.94788,-1.66128 2.85184,2.48149 c 1.56852,1.36481 4.66349,3.38493 6.87772,4.48914 4.97257,2.47973 5.54308,3.38282 3.04733,4.82374 z m 22.50729,-61.79039 c 3.17451,9.98553 2.94038,23.22889 -0.58688,33.19399 l -2.33309,6.59143 -2.94597,-0.69161 c -1.6203,-0.38041 -4.86173,-2.02821 -7.2032,-3.6618 l -4.25721,-2.97018 1.60429,-6.16234 c 2.85178,-10.95404 0.79685,-23.09833 -5.51167,-32.57307 l -3.01788,-4.53253 1.72854,-2.16916 c 3.24083,-4.06698 9.80863,-9.03614 11.07242,-8.37738 1.89457,0.98756 9.46336,15.1015 11.45065,21.35265 z m -48.80223,10.31437 c 0,-32.58201 36.49058,-54.27201 67.51771,-40.1325 8.58077,3.9104 8.6421,5.148 0.11108,2.23988 -21.97368,-7.49048 -45.51946,5.51348 -49.99082,27.6091 l -1.03521,5.11561 -3.57498,1.22902 c -1.96621,0.67596 -5.26316,2.34622 -7.32655,3.71171 -4.63379,3.06649 -5.70115,3.10904 -5.70115,0.22718 z m 42.25842,50.3871 c -10.23499,-2.24356 -21.58699,-9.06801 -28.45341,-17.10525 l -4.5418,-5.31622 2.07194,-2.20549 c 1.13957,-1.21302 4.18733,-3.19628 6.77282,-4.40726 l 4.70085,-2.20176 4.53458,4.47053 c 8.06061,7.94674 19.60535,12.23927 30.96496,11.51329 l 5.43422,-0.34731 1.01427,2.58154 c 1.90169,4.84014 2.92124,13.01261 1.71883,13.77769 -1.80254,1.14695 -17.80995,0.64475 -24.21726,-0.75976 z"
+ style="display:inline;fill:#5a2ca0"
+ id="path3415-4-2-2-5-0-3-7-4-4-1-5" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text3557-5-3-7-46-7-3-7"
+ y="519.50671"
+ x="317.95816"
+ style="font-style:normal;font-weight:normal;font-size:97.09867096px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none"
+ xml:space="preserve"><tspan
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:97.09867096px;line-height:125%;font-family:FreeEuro;-inkscape-font-specification:'FreeEuro Bold';text-align:start;writing-mode:lr-tb;text-anchor:start"
+ y="519.50671"
+ x="317.95816"
+ id="tspan3559-5-4-1-90-0-2-9"
+ sodipodi:role="line">BZH</tspan></text>
+ </g>
+ <flowRoot
+ xml:space="preserve"
+ id="root-title"
+ transform="matrix(2.3469382,0,0,2.3469382,464.13874,-1200)"><flowRegion
+ id="flowRegion4303"><rect
+ id="rect4305"
+ width="679.99994"
+ height="141.42853"
+ x="-154.28572"
+ y="400"/>
+ </flowRegion>
+ <flowPara
+ id="title"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:87.5px;line-height:125%;font-family:Verdana;-inkscape-font-specification:'Verdana, Bold';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1">{title}</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="root-subtitle"
+ transform="matrix(1.8523279,0,0,1.8523279,553.97647,-380)"><flowRegion
+ id="flowRegion4303-6"><rect
+ id="rect4305-0"
+ width="852.32806"
+ height="154.51677"
+ x="-239.3591"
+ y="290"/>
+ </flowRegion>
+ <flowPara
+ id="subtitle"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:62.5px;line-height:125%;font-family:Verdana;-inkscape-font-specification:'Verdana, Bold';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1">{subtitle}</flowPara>
+ </flowRoot>
+ <flowRoot
+ id="root-version"
+ xml:space="preserve"
+ transform="translate(-2.0185547,164)"><flowRegion
+ id="flowRegion4169"><rect
+ y="501.68909"
+ x="343.32947"
+ height="99.591171"
+ width="1117.3768"
+ id="rect4171" /></flowRegion><flowPara
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:50px;line-height:125%;font-family:Verdana;-inkscape-font-specification:'Verdana, Bold';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1"
+ id="version">{version}</flowPara></flowRoot> <flowRoot
+ transform="translate(-2.0185547,238)"
+ xml:space="preserve"
+ id="root-date"><flowRegion
+ id="flowRegion4170"><rect
+ id="rect4172"
+ width="1117.3768"
+ height="99.591171"
+ x="343.32947"
+ y="501.68909" /></flowRegion><flowPara
+ id="date"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:50px;line-height:125%;font-family:Verdana;-inkscape-font-specification:'Verdana, Bold';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1">{date}</flowPara></flowRoot> </g>
+</svg>
diff --git a/docs/resources/ebook.css b/docs/resources/ebook.css
new file mode 100644
index 0000000..e8df4d8
--- /dev/null
+++ b/docs/resources/ebook.css
@@ -0,0 +1,402 @@
+/* IoT.Bzh theaming */
+
+h1 {
+ color: #330066;
+ border-bottom: 2px solid #330066;
+}
+
+h2 {
+ color: #330066;
+}
+
+h3 {
+ color: #330066;
+}
+
+h4 {
+ color: #330066;
+}
+
+
+/* GENERAL ELEMENTS */
+
+/* clear both */
+
+.clear {
+ clear: both;
+}
+
+.section> :last-child {
+ margin-bottom: 0 !important;
+}
+
+.section> :first-child {
+ margin-top: 0 !important;
+}
+
+
+/* SPECIAL ELEMENTS */
+
+
+/* page break always after element on pdf/print definition */
+
+div.pagebreak {
+ page-break-after: always;
+}
+
+
+/* no page break inside element on pdf/print definition */
+
+div.nopb {
+ page-break-inside: avoid !important;
+ margin: 4px 0 4px 0;
+}
+
+
+/* note blocks */
+
+div.note {
+ background: #FCF8E3 none repeat scroll 0% 0%;
+ color: #8A6D3B;
+ padding: 15px;
+ margin-bottom: 10px;
+ border-bottom: 5px solid #DDD;
+ border-color: #FAEBCC;
+ page-break-inside: avoid;
+}
+
+div.note p {
+ padding-bottom: 0;
+ margin-bottom: 0;
+}
+
+/* warning blocks */
+
+div.warning {
+ background: #FD9595 none repeat scroll 0% 0%;
+ color: #8A6D3B;
+ padding: 15px;
+ margin-bottom: 10px;
+ border-bottom: 5px solid #DDD;
+ border-color: #FF6B6B;
+ page-break-inside: avoid;
+}
+
+div.warning p {
+ padding-bottom: 0;
+ margin-bottom: 0;
+}
+
+/* images, figures and captions */
+
+p img {
+ /* center all images */
+ display: block;
+ margin: 0 auto;
+ padding: 10px 0;
+}
+
+figure {
+ margin: 1.0em 0px;
+ padding: 10px 0;
+ text-align: center;
+ page-break-inside: avoid;
+ display: block;
+}
+
+figure img {
+ display: block;
+ margin: 0 auto;
+}
+
+figcaption {
+ clear: left;
+ margin: 1.0em 0 0 0;
+ text-align: center;
+ font-style: italic;
+ line-height: 1.5em;
+ font-size: 80%;
+ color: #666;
+ display: block;
+}
+
+.page .section p img {
+ margin-top: 10px;
+}
+
+
+/* ul, ol list margin fix */
+
+.page .section ol,
+.page .section ul {
+ margin-bottom: 10px;
+}
+
+
+/* blockquotes */
+
+.page .section blockquote {
+ margin: 0 0 0 5%;
+ font-style: italic;
+}
+
+
+/* PAGE SPECIFIC */
+
+
+/* set summary page to right side of the paper */
+
+.page .toc h1 {
+ page-break-before: right;
+}
+
+.page .section.toc {
+ page-break-inside: always;
+}
+
+/* table headers */
+
+div#README\.md table {
+ margin-top: 30px;
+ font-size: 95%;
+}
+
+div#README\.md table thead {
+ display: none;
+}
+
+
+
+/* CITATION AND IMAGES */
+
+
+/* math image styles */
+
+.page .section p img.svg,
+.page .section p img.png {
+ margin-top: 0px;
+ margin-bottom: -2px;
+}
+
+.page .section p img.math {
+ vertical-align: middle;
+ height: auto;
+ width: auto;
+ margin-top: -4px;
+ max-height: 15px;
+}
+
+.page .section p img.math.line1 {
+ margin-top: -7px;
+ max-height: 19px;
+}
+
+.page .section p img.math.line2 {
+ margin-top: -1px;
+ max-height: 30px;
+}
+
+
+/* credits page */
+
+.page .section ul.pictures {
+ margin-left: -30px;
+}
+
+.page .section ul.pictures li {
+ list-style: outside none none;
+}
+
+.page .section ul.pictures li a {
+ float: left;
+}
+
+.page .section ul.pictures li span {
+ display: block;
+ margin-left: 100px;
+}
+
+
+
+/* sub and super script */
+
+.page .section sub {
+ font-size: 80%;
+ margin-left: 1px;
+}
+
+
+/* citations and references */
+
+.page .section sup {
+ margin-left: -1px;
+ margin-right: 2px;
+ font-size: 80%;
+}
+
+.page .section sup:before {
+ content: " ";
+}
+
+.page .section ul.citations,
+.page .section ul.references {
+ margin-left: -30px;
+}
+
+
+.page .section ul.citations li:nth-child(1) {
+ margin-top: 20px;
+ padding-top: 20px;
+ border-top: 1px solid #BBB;
+}
+
+.page .section ul.citations li,
+.page .section ul.references li {
+ list-style: outside none none;
+}
+
+.page .section ul.citations li {
+ font-size: 80%;
+}
+
+.page .section ul.citations li>span:nth-child(1),
+.page .section ul.references li>span:nth-child(1) {
+ display: block;
+ float: left;
+ text-align: left;
+ width: 70px
+}
+
+.page .section ul.citations li>span:nth-child(1) {
+ width: 50px
+}
+
+.page .section ul.references li div {
+ margin-left: 70px;
+}
+
+.page .section ul.citations li div {
+ margin-left: 50px;
+}
+
+.page .section a[href="#"],
+.page .section a[href="#"]:link,
+.page .section a[href="#"]:visited,
+.page .section a[href="#"]:hover,
+.page .section a[href="#"]:focus {
+ text-decoration: none;
+ color: inherit;
+ cursor: text;
+ font-style: italic;
+}
+
+
+/* self referential footnotes */
+
+.page .section div[type="selfref"] a[href="#"],
+.page .section div[type="selfref"] a[href="#"]:link,
+.page .section div[type="selfref"] a[href="#"]:visited,
+.page .section div[type="selfref"] a[href="#"]:hover,
+.page .section div[type="selfref"] a[href="#"]:focus {
+ font-style: normal;
+}
+
+.page .section div[type="selfref"] span:nth-child(1) {
+ display: none;
+}
+
+
+/* page break always after element on pdf/print definition */
+
+div.page-break {
+ page-break-inside: always;
+}
+
+div.page-break:before {
+ content: ' ';
+}
+
+
+/* no page break inside element on pdf/print definition */
+
+div.nopb {
+ page-break-inside: avoid;
+}
+
+/* justify text */
+p {
+ text-align: justify;
+}
+
+/* page header and footer */
+
+.pdf-footer,
+.pdf-header {
+ margin-top: 20px;
+ color: #aaa;
+}
+
+.pdf-header .header-left {
+ float: left;
+ margin-left: 2em;
+ margin-right: auto;
+}
+
+.pdf-header .header-right {
+ display: table;
+ margin-left: auto;
+ margin-right: 2em;
+}
+
+.pdf-footer .sub {
+ padding-top: 8px;
+ font-size: 70%;
+}
+
+.pdf-header .sub {
+ padding-top: 2px;
+ font-size: 70%;
+}
+
+.pdf-footer {
+ padding-top: 10px;
+ border-top: 1px solid #eee;
+}
+
+.pdf-footer .footer-left {
+ float: left;
+ margin-left: 2em;
+ margin-right: auto;
+}
+
+.pdf-footer .footer-center {
+ display: table;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.pdf-footer .footer-right {
+ float: right;
+ margin-left: auto;
+ margin-right: 2em;
+}
+
+.pdf-header {
+ padding-bottom: 10px;
+ border-bottom: 1px solid #eee;
+}
+
+.pdf-header .header-pages-count {
+ float: right;
+ text-align: right;
+}
+
+.pdf-header .header-pages-count a,
+.pdf-header .header-pages-count a:visited,
+.pdf-header .header-pages-count a:active,
+.pdf-header .header-pages-count a:focus,
+.pdf-header .header-pages-count a:link {
+ text-decoration: none;
+ color: #aaa;
+ cursor: text;
+}
diff --git a/docs/resources/make_cover.sh b/docs/resources/make_cover.sh
new file mode 100755
index 0000000..1026ecb
--- /dev/null
+++ b/docs/resources/make_cover.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+DOCS_DIR=$(cd $(dirname $0)/.. && pwd)
+BOOKFILE=$DOCS_DIR/../book.json
+
+TITLE=$(grep '"title":' $BOOKFILE | cut -d'"' -f 4)
+SUBTITLE=$(grep '"subtitle":' $BOOKFILE | cut -d'"' -f 4)
+VERSION="Version $(grep '"version":' $BOOKFILE | cut -d'"' -f 4)"
+DATE=$(grep '"published":' $BOOKFILE | cut -d'"' -f 4)
+
+[ -z "$TITLE" ] && { echo "Error TITLE not set!" ; exit 1; }
+[ -z "$VERSION" ] && { echo "Error VERSION not set!" ; exit 1; }
+[ -z "$DATE" ] && { echo "Error DATE not set!" ; exit 1; }
+
+
+cat $(dirname $0)/cover.svg | sed -e "s/{title}/$TITLE/g" \
+ -e "s/font-size:87.5px/font-size:54px/g" \
+ -e "s/{subtitle}/$SUBTITLE/g" \
+ -e "s/font-size:62.5px/font-size:40px/g" \
+ -e "s/{version}/$VERSION/g" \
+ -e "s/{date}/$DATE/g" \
+ > /tmp/cover.svg
+
+# use imagemagick convert tool (cover size must be 1800x2360)
+convert -resize "1600x2160!" -border 100 -bordercolor white -background white \
+ -flatten -quality 100 /tmp/cover.svg $DOCS_DIR/cover.jpg
+
+convert -resize "200x262!" $DOCS_DIR/cover.jpg $DOCS_DIR/cover_small.jpg
diff --git a/gendocs.sh b/gendocs.sh
new file mode 100755
index 0000000..fdeb26d
--- /dev/null
+++ b/gendocs.sh
@@ -0,0 +1,90 @@
+#!/bin/bash
+# shellcheck disable=SC2086
+
+OUTFILENAME="APP_Controller_doc"
+
+SCRIPT=$(basename "${BASH_SOURCE[@]}")
+
+VERSION=$(grep '"version":' "$(dirname "${BASH_SOURCE[@]}")/book.json" | cut -d'"' -f 4)
+[ "$VERSION" != "" ] && OUTFILENAME="${OUTFILENAME}_v${VERSION}"
+
+
+function usage() {
+ cat <<EOF >&2
+Usage: $SCRIPT [options] [pdf|serve|doxygen]
+
+Options:
+ --debug
+ enable debug when generating pdf or html documentation
+ -d|--dry
+ dry run
+ -h|--help
+ get this help
+
+Example:
+ $SCRIPT pdf
+
+EOF
+ exit 1
+}
+
+function info() {
+ echo "$@" >&2
+}
+
+#default values
+DEBUG_FLAG=""
+DRY=""
+DO_ACTION=""
+OUT_DIR=./build
+ARGS=""
+
+[ $# = 0 ] && usage
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --debug) DEBUG_FLAG="--log=debug --debug";;
+ -d|--dry) DRY="echo";;
+ -h|--help) usage;;
+ pdf | serve | doxygen) DO_ACTION=$1;;
+ --) shift; ARGS="$ARGS $*"; break;;
+ *) ARGS="$ARGS $1";;
+ esac
+ shift
+done
+
+cd "$(dirname "$0")" || exit 1
+ROOTDIR=$(pwd -P)
+
+# Create out dir if needed
+[ -d $OUT_DIR ] || mkdir -p $OUT_DIR
+
+if [ "$DO_ACTION" = "pdf" ] || [ "$DO_ACTION" = "serve" ]; then
+
+ GITBOOK=$(which gitbook)
+ [ "$?" = "1" ] && { echo "You must install gitbook first, using: sudo npm install -g gitbook-cli"; exit 1; }
+
+ which ebook-convert > /dev/null 2>&1
+ [ "$?" = "1" ] && { echo "You must install calibre first, using: 'sudo apt install calibre' or refer to https://calibre-ebook.com/download"; exit 1; }
+
+ [ ! -d "$ROOTDIR/node_modules" ] && $GITBOOK install
+
+ if [ "$DO_ACTION" = "pdf" ]; then
+
+ # Update cover when book.json has been changed
+ [[ $ROOTDIR/book.json -nt $ROOTDIR/docs/cover.jpg ]] && { echo "Update cover files"; $ROOTDIR/docs/resources/make_cover.sh || exit 1; }
+
+ OUTFILE=$OUT_DIR/$OUTFILENAME.pdf
+ if $DRY $GITBOOK pdf $ROOTDIR $OUTFILE $DEBUG_FLAG $ARGS; then
+ echo "PDF has been successfully generated in $OUTFILE"
+ fi
+ else
+ $DRY $GITBOOK serve $DEBUG_FLAG $ARGS
+ fi
+
+elif [ "$DO_ACTION" = "doxygen" ]; then
+ $DRY cd $OUT_DIR && cmake .. && make doxygen $ROOTDIR/Doxyfile
+
+else
+ echo "Unknown action !"
+ usage
+fi