diff options
Diffstat (limited to 'conf.d/project')
-rw-r--r-- | conf.d/project/alsa.d/README.md | 175 | ||||
-rw-r--r-- | conf.d/project/alsa.d/asoundrc.sample | 8 | ||||
-rw-r--r-- | conf.d/project/json.d/CMakeLists.txt | 32 | ||||
-rw-r--r-- | conf.d/project/json.d/README.md | 22 | ||||
-rw-r--r-- | conf.d/project/json.d/onload-aaaa-sample.json | 103 | ||||
-rw-r--r-- | conf.d/project/lua.d/CMakeLists.txt | 34 | ||||
-rw-r--r-- | conf.d/project/lua.d/onload-aaaa-00-utils.lua | 86 | ||||
-rw-r--r-- | conf.d/project/lua.d/onload-aaaa-01-init.lua | 48 | ||||
-rw-r--r-- | conf.d/project/lua.d/onload-aaaa-03-controls.lua | 118 | ||||
-rw-r--r-- | conf.d/project/lua.d/onload-aaaa-10-event.lua | 74 |
10 files changed, 693 insertions, 7 deletions
diff --git a/conf.d/project/alsa.d/README.md b/conf.d/project/alsa.d/README.md new file mode 100644 index 0000000..aa2a54e --- /dev/null +++ b/conf.d/project/alsa.d/README.md @@ -0,0 +1,175 @@ + +Alsa Configuration is not complex, but it's heavy and every except intuitive. + +In order to set your configuration move step by step. And verify at each new step that you did not introduce a regression. + +### Make sure your board is not taken by PulseAudio + +* Pavucontrol is your friend. Go in device configuration and switch to off the device you want to make available to ALSA. +* Check for your device list with 'play -l'. Note that just after card number your get an alias for your sound card. + it is simpler to use this alias, card number will change depending on plug/unplug of device when this alias name + is more stable (except for few stupid driver who use 'USB' as sound card name. +* When your know your sound alias (eg:v1340 in mu case) you can test it with 'speaker-test -D v1340 -c2 -twav'. You may also + use sound card number with 'speaker-test -D hw:0 -c2 -twav'. Nevertheless as said previously this number is not stable. +* you are now ready to write your $HOME/.asoundrc config + +Note that $HOME/.asoundrc is loaded within libasound client and not by Alsa kernel, this is the reason why you do not need +to activate any control or restart a daemon for modifications to be taken in account. + +To use ALSA with AAAA and the controller you need to write 1 section in your ALSA config + +* Sound Card Mixer: Allows multiple audio stream to be played on the same sound card. If hardware support mixer, Alsa will use it. If + not it will provide mixing by software. +* Audio Role Volume: They provide independent volume for each audio role. For reference, we use Alsa softvolume, depending on + your hardware you may have this directly avaliable from your sound card. +* Authorised Audio PCM: those channel are designed for applications we do not trust and then enforce AAAA control check + before granting the access to a given channel. + +### Sound Card Mixer + +``` +pcm.SoundCardMixer { + type dmix + ipc_key 1024 + ipc_key_add_uid false + ipc_perm 0666 # mixing for all users + + # Define target effective sound card (cannot be a plugin) + slave { pcm "hw:v1340" } #Jabra Solmate + + # DMIX can only map two channels + bindings { + 0 0 + 1 1 + } +} +``` + +Warning: if you have more than one Mixer each of them should have a unique ipc_key. You sound card alias move in the slave section. +When this is done you may try your mixer with: + +``` + speaker-test -D MyMixerPCM -c2 -twav +``` + + +### Audio Role + +``` +pcm.NavigationRole { + type softvol + + # Point Slave on HOOK for policies control + slave.pcm "SoundCardMixer" + + # name should match with HAL but do not set card=xx + control.name "Playback Navigation" + +} + +``` + +The slave you point to your SoundCardMixer, and the control.name should be EXACTLY the same as the one defined in your HAL. + + +WARNING: The control here "Playback Navigation" is a user defined kernel control. It means that this kernel is created in +kernel space, but that its action happen in user space. When create those control remains visible until you reset your +sound card (eg: unplug USB), but they are save each time you reboot. It is recommended to start AAAA binder before testing +your softvol audio role channel. If you do the opposite the control will be create by Alsa Plugin and this will not inherit +of the default value you have in your HAL. + +When in place you should test it with: +``` + speaker-test -D NavigationRole -c2 -twav +``` + +IMPORTANT: control volume are attache to your physical hardware and not to intermediary level (Softvol or Mixer). To see the +newly created channel you should use +``` + amixer -Dhw:v1340 controls | grep -i playback +``` + +## Authorised Audio PCM + +This PCM is supervised with the AAAA audio hook plugin. The pluging and will any application request on this PCM and will +1st request an autorisation from AAAA controller to grant access for the client application. To do so, two things: +* the plugin should be declared (only once) +* you should declare as many authorized PCM as you need. + +### Plugin declaration + +``` +pcm_hook_type.MyHookPlugin { + install "AlsaInstallHook" + lib "/home/fulup/Workspace/AGL-AppFW/audio-bindings-dev/build/Alsa-Plugin/Alsa-Policy-Hook/policy_hook_cb.so" +} + +``` + +Lib is the path where to find AAAA Alsa hook plugin, install is the name of the init function it should not be changed. + + +When your plugin is defined you may declare your authorised PCM. Those PCM like softvol will take a slave, typically a lower +level of the audio role, or directly a mixer if your goal is to protect directly the Mixer. The AAAA Plugin hook take as + +``` +pcm.AuthorisedToNavigationOnly { + type hooks + slave.pcm "NavigationRole" + # Defined used hook sharelib and provide arguments/config to install func + hooks.0 { + type "MyHookPlugin" + hook_args { + verbose true # print few log messages (default false); + + # Every Call should return OK in order PCM to open (default timeout 100ms) + uri "ws://localhost:1234/api?token=audio-agent-token&uuid=audio-agent-session" + request { + # Request autorisation to write on navigation + RequestNavigation { + api "control" + verb "dispatch" + query "{'target':'navigation', 'args':{'device':'Jabra SOLEMATE v1.34.0'}}" + } + } + # map event reception to self generated signal + event { + pause 30 + resume 31 + stop 3 + } + } + } +} + +``` + + * The slave is the PCM that application will be transfer to if access to control is granted. + * Request is a suite à control action that respond to AGL standard application framework API + * Event is the mapping of signal an appplication will receive in case AAAA controller push event to the audio application. + +When using AAAA controller, most action should be transfert to the controller that will take action to authorise/deny the access. +Nevertheless it is also possible to directly access a lower level (e.g. call alsa Use Case Manager). People may also have their +own audio policy engine and request it directly from here. + +To test this last part your need to have a controller ready to respond to your request. Otherwise control will systematically +be denied. When your AAAA controller is ready to serve your request you may check this with +``` + amixer -Dhw:AuthorisedToMusicOnly controls | grep -i playback + amixer -Dhw:AuthorisedToNavigationOnly controls | grep -i playback +``` + +IMPORTANT: you need at least to audio role to really test this part. While you may assert with one channel that your flow +to accept/deny application works. You need two simultaneous audio stream to really play with the control. Typically when playing +music if you send a navigation message then the audio will be lower during the rendering of the navigation message. + +The action on how you lower an audio role when an other one with a higger level of priority come in place not defined at the +plugin level, but in the AAAA controller, where the API control/dispatch?target=xxxxx will execute a set of actions corresponding +the set/unset accept/deny of requested control. + +Remark: to understand what is happening it is a good idea to have an alxamixer option on the your soundcard +``` + amixer -Dhw:v1340 +``` + +(!) Do not forget to replace 'hw:v1340' by what ever is the alias of your sound card.
\ No newline at end of file diff --git a/conf.d/project/alsa.d/asoundrc.sample b/conf.d/project/alsa.d/asoundrc.sample index 8976077..0716b11 100644 --- a/conf.d/project/alsa.d/asoundrc.sample +++ b/conf.d/project/alsa.d/asoundrc.sample @@ -27,13 +27,7 @@ pcm.MyMixerPCM { ipc_perm 0666 # mixing for all users # Define target effective sound card (cannot be a plugin) - slave { - pcm "hw:v1340" #Jabra Solmate - period_time 0 - period_size 1024 - buffer_size 8192 - rate 44100 - } + slave { pcm "hw:v1340" } #Jabra Solmate # DMIX can only map two channels bindings { diff --git a/conf.d/project/json.d/CMakeLists.txt b/conf.d/project/json.d/CMakeLists.txt new file mode 100644 index 0000000..8070997 --- /dev/null +++ b/conf.d/project/json.d/CMakeLists.txt @@ -0,0 +1,32 @@ +########################################################################### +# Copyright 2017 IoT.bzh +# +# author: Fulup Ar Foll <fulup@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + + +################################################## +# Control Policy Config file +################################################## +PROJECT_TARGET_ADD(ctl-config.d) + + file(GLOB XML_FILES "*.json") + + add_input_files("${XML_FILES}") + + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + LABELS "DATA" + OUTPUT_NAME ${TARGET_NAME} + ) diff --git a/conf.d/project/json.d/README.md b/conf.d/project/json.d/README.md new file mode 100644 index 0000000..0f4967d --- /dev/null +++ b/conf.d/project/json.d/README.md @@ -0,0 +1,22 @@ +By default controller searches for a config filename with the same 'middlename' as daemon process. As an example if your process name is afb-daemon then middle name is 'daemon'. + +``` + onload-middlename-xxxxx.json + + # Middlename is taken from process middlename. +``` + +You may overload config search path with environement variables + * AFB_BINDER_NAME: change patern config search path. 'export AFB_BINDER_NAME=sample' will make controller to search for a configfile name 'onload-sample-xxx.json'. + * 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 name 'onload-myconfig-test.json' do +``` + AFB_BINDER_NAME='myconfig' afb-daemon --verbose ...' +``` + +Note: you may change search pattern for Lua script by adding 'ctlname=afb-middlename-xxx' in the metadata section of your config 'onload-*.json' + +WARNING: Audio Control are the one from the HAL and not from Alsa LowLevel + diff --git a/conf.d/project/json.d/onload-aaaa-sample.json b/conf.d/project/json.d/onload-aaaa-sample.json new file mode 100644 index 0000000..c95a62f --- /dev/null +++ b/conf.d/project/json.d/onload-aaaa-sample.json @@ -0,0 +1,103 @@ +{ + "$schema": "ToBeDone", + "metadata": { + "label": "sample-aaaa-control", + "info": "Sample of Video AAAA controls", + "name": "afb-sample-controller", + "version": "1.0" + }, + "onload": [{ + "label": "onload-default", + "info": "onload initialisation config", + "require": ["alsacore","jabra-usb","intel-hda","hal-most-unicens"], + "actions": + { + "label": "control-init", + "lua": "_Audio_Controller_Init", + "args": { + "evtname": "agl-audio" + } + } + }], + "controls": + [ + { + "label": "Multimedia", + "permissions": "urn:AGL:permission:audio:public:multimedia", + "actions": { + "label": "authorize-multimedia", + "lua": "_Temporarily_Control" + } + }, { + "label": "Navigation", + "permissions": "urn:AGL:permission:audio:public:navigation", + "actions": { + "label": "authorize-navigation", + "lua": "_Temporarily_Control", + "args": { + "ctl" : "Multimedia_Playback_Volume", + "val": 40 + } + } + }, { + "label": "Telephony", + "permissions": "urn:AGL:permission:audio:public:telephony", + "actions": { + "label": "authorize-multimedia", + "lua": "_Temporarily_Control", + "args": { + "ctl" : "Multimedia_Playback_Volume", + "val": 20 + } + } + }, { + "label": "Emergency", + "permissions": "urn:AGL:permission:audio:public:emergency", + "actions": { + "label": "authorize-multimedia", + "lua": "_Temporarily_Control", + "args": { + "ctl" : "Multimedia_Playback_Switch", + "val": 0 + }, + "label": "authorize-multimedia", + "lua": "_Temporarily_Control", + "args": { + "ctl" : "Navigation_Playback_Switch", + "val": 0 + } + } + } + ], + "events": + [ + { + "label": "ReverseEngage", + "actions": [{ + "label": "adjust_volume-reverse", + "lua": "_Temporarily_Control", + "args": { + "ctl": "Multimedia_Playback_Volume", + "val": 20 + } + }, + { + "label": "prevent-telephony", + "lua": "_Temporarily_Control", + "args": { + "ctl": "Telephony_Playback_Switch", + "val": 0 + } + } + ] + }, + { + "label": "SpeedChanged", + "actions": { + "label": "adjust_volume", + "lua": "_Adjust_Volume" + } + } + ] +} + diff --git a/conf.d/project/lua.d/CMakeLists.txt b/conf.d/project/lua.d/CMakeLists.txt new file mode 100644 index 0000000..71b3371 --- /dev/null +++ b/conf.d/project/lua.d/CMakeLists.txt @@ -0,0 +1,34 @@ +########################################################################### +# Copyright 2017 IoT.bzh +# +# author: Fulup Ar Foll <fulup@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + + +################################################## +# Control Policy Config file +################################################## +PROJECT_TARGET_ADD(ctl-lua.d) + file(GLOB LUA_FILES "*.lua") + + # Romain work around to activate lua compilation + set(LUA_LIST "${LUA_FILES}" CACHE STRING "") + + add_input_files("${LUA_FILES}") + + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + LABELS "DATA" + OUTPUT_NAME ${TARGET_NAME} + )
\ No newline at end of file diff --git a/conf.d/project/lua.d/onload-aaaa-00-utils.lua b/conf.d/project/lua.d/onload-aaaa-00-utils.lua new file mode 100644 index 0000000..29d2c70 --- /dev/null +++ b/conf.d/project/lua.d/onload-aaaa-00-utils.lua @@ -0,0 +1,86 @@ +--[[ + Copyright (C) 2016 "IoT.bzh" + Author Fulup Ar Foll <fulup@iot.bzh> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Note: this file should be called before any other to assert declare function + is loaded before anything else. + + References: + http://lua-users.org/wiki/DetectingUndefinedVariables + +--]] + + +--=================================================== +--= Niklas Frykholm +-- basically if user tries to create global variable +-- the system will not let them!! +-- call GLOBAL_lock(_G) +-- +--=================================================== +function GLOBAL_lock(t) + local mt = getmetatable(t) or {} + mt.__newindex = lock_new_index + setmetatable(t, mt) +end + +--=================================================== +-- call GLOBAL_unlock(_G) +-- to change things back to normal. +--=================================================== +function GLOBAL_unlock(t) + local mt = getmetatable(t) or {} + mt.__newindex = unlock_new_index + setmetatable(t, mt) +end + +function lock_new_index(t, k, v) + if (string.sub(k,1,1) ~= "_") then + GLOBAL_unlock(_G) + error("GLOBALS are locked -- " .. k .. + " must be declared local or prefix with '_' for globals.", 2) + else + rawset(t, k, v) + end +end + +function unlock_new_index(t, k, v) + rawset(t, k, v) +end + +-- return serialised version of printable table +function Dump_Table(o) + if type(o) == 'table' then + local s = '{ ' + for k,v in pairs(o) do + if type(k) ~= 'number' then k = '"'..k..'"' end + s = s .. '['..k..'] = ' .. Dump_Table(v) .. ',' + end + return s .. '} ' + else + return tostring(o) + end +end + + +-- simulate C prinf function +printf = function(s,...) + io.write(s:format(...)) + io.write("\n") + return +end + +-- lock global variable +GLOBAL_lock(_G) diff --git a/conf.d/project/lua.d/onload-aaaa-01-init.lua b/conf.d/project/lua.d/onload-aaaa-01-init.lua new file mode 100644 index 0000000..8de0c24 --- /dev/null +++ b/conf.d/project/lua.d/onload-aaaa-01-init.lua @@ -0,0 +1,48 @@ +--[[ + Copyright (C) 2016 "IoT.bzh" + Author Fulup Ar Foll <fulup@iot.bzh> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--]] + +-- Global variable SHOULD start with _ +_Global_Context={} + +--[[ + This function is call during controller init phase as describe in onload-daemon-sample.json + It receives two argument 1st one is the source (here on load) second one is the arguments + as expose in config file. + + In this sample we create an event that take the name of args["zzzz"], the resulting handle + is save into _Global_Context for further use. + + Note: init functions are not call from a client and thus do not receive query + +--]] +function _Audio_Controller_Init(source, control) + + printf ("[--> Audio_Controller_Init -->] source=%d control=%s", source, Dump_Table(control)) + + -- create an event from configuration name + _Global_Context["event"]=AFB:evtmake(control["evtname"]) + + -- query HAL to retrieve sound card. + local err,result= AFB:servsync ("alsacore", "hallist", {}) + + if (err) then + AFB_ERROR("Fail to retrieve Audio HAL") + else + _Global_Context["registry"]=result["response"] + printf("[<-- Audio_Controller_Init <--] Active HAL=%s", Dump_Table(result["response"])) + end +end diff --git a/conf.d/project/lua.d/onload-aaaa-03-controls.lua b/conf.d/project/lua.d/onload-aaaa-03-controls.lua new file mode 100644 index 0000000..6c7c8a5 --- /dev/null +++ b/conf.d/project/lua.d/onload-aaaa-03-controls.lua @@ -0,0 +1,118 @@ +--[[ + Copyright (C) 2016 "IoT.bzh" + Author Fulup Ar Foll <fulup@iot.bzh> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Following function are called when a control activate a label with + labeller api -> APi=label VERB=dispatch + arguments are + - source (0) when requesting the label (-1) when releasing + - label comme from config given with 'control' in onload-middlename-xxxxx.json + - control is the argument part of the query as providing by control requesting the label. + +--]] + +_CurrentHalVolume={} + + +local function Apply_Hal_Control(source, label, adjustment) + local HAL = _Global_Context["registry"] + + -- check we really got some data + if (adjustment == nil) then + AFB:error ("--* (Hoops) Control should provide volume adjustment") + return 1 + end + + -- loop on each HAL save current volume and push adjustment + for key,hal in pairs(_Global_Context["registry"]) do + printf ("--- HAL=%s", Dump_Table(hal)) + + -- action set loop on active HAL and get current volume + -- if label respond then do volume adjustment + if (source == 0) then + + -- get current volume for each HAL + local err,result= AFB:servsync(hal["api"],"ctlget", {["label"]=label}) + + -- if no error save current volume and set adjustment + if (err ~= nil) then + local response= result["response"] + printf ("--- Response %s=%s", hal["api"], Dump_Table(response)) + + if (response == nil) then + printf ("--- Fail to Activate '%s'='%s' result=%s", hal["api"], label, Dump_Table(result)) + return 1 -- unhappy + end + + -- save response in global space + _CurrentHalVolume [hal["api"]] = response + + -- finally set the new value + local query= { + ["tag"]= response["tag"], + ["val"]= adjustment + } + + -- best effort to set adjustment value + AFB:servsync(hal["api"],"ctlset",query) + end + + else -- when label is release reverse action at preempt time + + if (_CurrentHalVolume [hal["api"]] ~= nil) then + + printf("--- Restoring initial volume HAL=%s Control=%s", hal["api"], _CurrentHalVolume [hal["api"]]) + + AFB:servsync(hal["api"],"ctlset", _CurrentHalVolume [hal["api"]]) + end + end + + end + return 0 -- happy end +end + + +-- Simple Happy(granted) Control +function _Temporarily_Control(source, control, client) + + printf ("[--> _Temporarily_Control -->] source=%d control=%s client=%s", source, Dump_Table(control), Dump_Table(client)) + + -- Init should have been properly done + if (_Global_Context["registry"] == nil) then + AFB:error ("--* (Hoops) No Hal in _Global_Context=%s", Dump_Table(_Global_Context)) + return 1 + end + + -- make sure label as valid + if (control["ctl"] == nil or control["val"] == nil) then + AFB:error ("--* Action Ignore no/invalid control=%s", Dump_Table(control)) + return 1 -- unhappy + end + + if (source == 0) then + AFB:info("-- Adjust %s=%d", control["ctl"], control["val"]) + local error=Apply_Hal_Control(source, control["ctl"], control["val"]) + if (error == nil) then + return 1 -- unhappy + end + AFB:notice ("[<-- _Temporarily_Control Granted<--]") + else + Apply_Hal_Control(source, control["ctl"],0) + AFB:notice ("[<-- _Temporarily_Control Restore--]") + end + + return 0 -- happy return +end + diff --git a/conf.d/project/lua.d/onload-aaaa-10-event.lua b/conf.d/project/lua.d/onload-aaaa-10-event.lua new file mode 100644 index 0000000..474ebe0 --- /dev/null +++ b/conf.d/project/lua.d/onload-aaaa-10-event.lua @@ -0,0 +1,74 @@ +--[[ + Copyright (C) 2016 "IoT.bzh" + Author Fulup Ar Foll <fulup@iot.bzh> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Provide Sample Timer Handing to push event from LUA +--]] + +-- Create event on Lua script load +_MyContext={} + +-- WARNING: call back are global and should start with '_' +function _Timer_Test_CB (timer, context) + + local evtinfo= AFB:timerget(timer) + printf ("[-- _Timer_Test_C --] evtInfo=%s", Dump_Table(evtinfo)) + + --send an event an event with count as value + AFB:evtpush (_MyContext["event"], {["label"]= evtinfo["label"], ["count"]=evtinfo["count"], ["info"]=context["info"]}) + + -- note when timerCB return!=0 timer is kill + return 0 + +end + +-- sendback event depending on count and delay +function _Simple_Timer_Test (request, client) + + local context = { + ["info"]="My 1st private Event", + } + + -- if event does not exit create it now. + if (_MyContext["event"] == nil) then + _MyContext["event"]= AFB:evtmake(client["label"]) + end + + -- if delay not defined default is 5s + if (client["delay"]==nil) then client["delay"]=5000 end + + -- if count is not defined default is 10 + if (client["count"]==nil) then client["count"]=10 end + + -- we could use directly client but it is a sample + local myTimer = { + ["label"]=client["label"], + ["delay"]=client["delay"], + ["count"]=client["count"], + } + AFB:notice ("Test_Timer myTimer=%s", myTimer) + + -- subscribe to event + AFB:subscribe (request, _MyContext["event"]) + + -- settimer take a table with delay+count as input (count==0 means infinite) + AFB:timerset (myTimer, "_Timer_Test_CB", context) + + -- nothing special to return send back + AFB:success (request, myTimer) + + return 0 +end |