summaryrefslogtreecommitdiffstats
path: root/ahl-binding
diff options
context:
space:
mode:
authorLoïc Collignon <loic.collignon@iot.bzh>2018-06-07 15:05:53 +0200
committerStephane Desneux <stephane.desneux@iot.bzh>2018-06-12 16:23:44 +0200
commit9a631c30c9c8792865ce2aa0ec06a1bb5fd16751 (patch)
treee1bd8c4ce3cc408144aedcf0f26fd873d8db8634 /ahl-binding
parent322f8932476eda944c7d3ac65eafde12c69b2ae9 (diff)
Add some policy emulation
Add a very simplistic policy emulation just for demo. The real policy engine will be brought back soon. Change-Id: I6f77c8dc58ba335eabd1a1d858354a84559d9e7f Signed-off-by: Loïc Collignon <loic.collignon@iot.bzh>
Diffstat (limited to 'ahl-binding')
-rw-r--r--ahl-binding/CMakeLists.txt6
-rw-r--r--ahl-binding/ahl-binding.cpp235
-rw-r--r--ahl-binding/ahl-binding.hpp34
-rw-r--r--ahl-binding/interrupt.cpp34
-rw-r--r--ahl-binding/interrupt.hpp28
-rw-r--r--ahl-binding/jsonc_utils.hpp1
-rw-r--r--ahl-binding/role.cpp7
-rw-r--r--ahl-binding/role.hpp5
8 files changed, 275 insertions, 75 deletions
diff --git a/ahl-binding/CMakeLists.txt b/ahl-binding/CMakeLists.txt
index b36cb1e..0e5df43 100644
--- a/ahl-binding/CMakeLists.txt
+++ b/ahl-binding/CMakeLists.txt
@@ -24,6 +24,7 @@ PROJECT_TARGET_ADD(audiohighlevel)
config_entry.cpp
role.cpp
ahl-4a.cpp
+ interrupt.cpp
ahl-binding.cpp
)
@@ -47,9 +48,8 @@ PROJECT_TARGET_ADD(audiohighlevel)
# Find package for GLIB does not seem to export
TARGET_LINK_LIBRARIES(${TARGET_NAME}
#ahl-policy
- #ahl-utilities
- afb-utilities
- #afb-helpers
+
+ afb-helpers
ctl-utilities
${GLIB_PKG_LIBRARIES}
${link_libraries}
diff --git a/ahl-binding/ahl-binding.cpp b/ahl-binding/ahl-binding.cpp
index 6493267..fa4c74b 100644
--- a/ahl-binding/ahl-binding.cpp
+++ b/ahl-binding/ahl-binding.cpp
@@ -20,24 +20,16 @@
afb_dynapi* AFB_default; // BUG: Is it possible to get rid of this ?
-ahl_binding_t::ahl_binding_t()
- : handle_{nullptr}
- , apihandle_{nullptr}
-{
-}
-
-ahl_binding_t& ahl_binding_t::instance()
-{
- static ahl_binding_t s;
- return s;
-}
-
-int ahl_binding_t::build(afb_dynapi* handle)
+/**
+ * @brief Entry point for dynamic API.
+ * @param[in] handle Handle to start with for API creation.
+ * @return Status code, zero if success.
+ */
+int afbBindingVdyn(afb_dynapi* handle)
{
using namespace std::placeholders;
+ assert(handle != nullptr);
- if (!handle || handle_) return -1;
- handle_ = handle;
AFB_default = handle;
return afb_dynapi_new_api(
@@ -45,28 +37,48 @@ int ahl_binding_t::build(afb_dynapi* handle)
HL_API_NAME,
HL_API_INFO,
1,
- [](void*, afb_dynapi* h) {
- return ahl_binding_t::instance().preinit(h);
- },
- nullptr);
+ ahl_api_create,
+ nullptr
+ );
+}
+
+/**
+ * @brief Callback to create the new api.
+ * @param[in] handle Handle to the new api.
+ * @return Status code, zero if success.
+ */
+int ahl_api_create(void*, struct afb_dynapi* handle)
+{
+ return ahl_binding_t::instance().preinit(handle);
+}
+
+ahl_binding_t::ahl_binding_t()
+ : handle_{nullptr}
+{
+}
+
+ahl_binding_t& ahl_binding_t::instance()
+{
+ static ahl_binding_t s;
+ return s;
}
int ahl_binding_t::preinit(afb_dynapi* handle)
{
- apihandle_ = handle;
+ handle_ = handle;
try
{
load_static_verbs();
load_controller_api();
- if (afb_dynapi_on_event(handle,
+ if (afb_dynapi_on_event(handle_,
[](afb_dynapi*, const char* e, struct json_object* o)
{ ahl_binding_t::instance().event(e, o); }
)
) throw std::runtime_error("Failed to register event handler callback.");
- if (afb_dynapi_on_init(handle,
+ if (afb_dynapi_on_init(handle_,
[](afb_dynapi*) { return ahl_binding_t::instance().init(); }
)) throw std::runtime_error("Failed to register init handler callback.");
}
@@ -83,42 +95,63 @@ int ahl_binding_t::init()
{
using namespace std::placeholders;
- if (afb_dynapi_require_api(apihandle_, HAL_MGR_API, 1))
+ if (afb_dynapi_require_api(handle_, HAL_MGR_API, 1))
{
- AFB_DYNAPI_ERROR(apihandle_, "Failed to require '%s' API!", HAL_MGR_API);
+ AFB_DYNAPI_ERROR(handle_, "Failed to require '%s' API!", HAL_MGR_API);
return -1;
}
- AFB_DYNAPI_NOTICE(apihandle_, "Required '%s' API found!", HAL_MGR_API);
+ AFB_DYNAPI_NOTICE(handle_, "Required '%s' API found!", HAL_MGR_API);
+ if (afb_dynapi_require_api(handle_, "smixer", 1))
+ {
+ AFB_DYNAPI_ERROR(handle_, "Failed to require 'smixer' API!");
+ return -1;
+ }
+ AFB_DYNAPI_NOTICE(handle_, "Required 'smixer' API found!");
+
// Requires corresponding API
for(const auto& h : config_.hals())
{
- if (afb_dynapi_require_api(apihandle_, h.c_str(), 1))
+ if (afb_dynapi_require_api(handle_, h.c_str(), 1))
{
- AFB_DYNAPI_ERROR(apihandle_, "Failed to require '%s' API!", h.c_str());
+ AFB_DYNAPI_ERROR(handle_, "Failed to require '%s' API!", h.c_str());
return -1;
}
- AFB_DYNAPI_NOTICE(apihandle_, "Required '%s' API found!", h.c_str());
+ AFB_DYNAPI_NOTICE(handle_, "Required '%s' API found!", h.c_str());
json_object* result = nullptr;
- if(afb_dynapi_call_sync(apihandle_, h.c_str(), "init-mixer", nullptr, &result))
- {
- AFB_DYNAPI_ERROR(apihandle_, "Failed to call 'init-mixer' verb on '%s' API!", h.c_str());
- if (result) AFB_DYNAPI_NOTICE(apihandle_, "%s", json_object_to_json_string(result));
- return -1;
- }
- AFB_DYNAPI_NOTICE(apihandle_, "Mixer initialized using '%s' API.", h.c_str());
+
+ // if(afb_dynapi_call_sync(handle_, h.c_str(), "init-mixer", nullptr, &result))
+ // {
+ // AFB_DYNAPI_ERROR(handle_, "Failed to call 'init-mixer' verb on '%s' API!", h.c_str());
+ // if (result) AFB_DYNAPI_NOTICE(handle_, "%s", json_object_to_json_string(result));
+ // return -1;
+ // }
+ // AFB_DYNAPI_NOTICE(handle_, "Mixer initialized using '%s' API.", h.c_str());
+
+ // json_object* response = nullptr;
+ // json_object* verbose = json_object_new_object();
+ // json_object_object_add(verbose, "verbose", json_object_new_int(1));
+ // if (afb_dynapi_call_sync(handle_, h.c_str(), "list", verbose, &response))
+ // {
+ // AFB_DYNAPI_ERROR(handle_, "Failed to call 'list' verb on '%s' API!", h.c_str());
+ // if (result) AFB_DYNAPI_NOTICE(handle_, "%s", json_object_to_json_string(result));
+ // return -1;
+ // }
json_object* response = nullptr;
- json_object* verbose = json_object_new_object();
- json_object_object_add(verbose, "verbose", json_object_new_int(1));
- if (afb_dynapi_call_sync(apihandle_, h.c_str(), "list", verbose, &response))
+ json_object* arg = json_object_new_object();
+ json_object_object_add(arg, "streams", json_object_new_boolean(TRUE));
+
+ if (afb_dynapi_call_sync(handle_, "smixer", "info", arg, &response))
{
- AFB_DYNAPI_ERROR(apihandle_, "Failed to call 'list' verb on '%s' API!", h.c_str());
- if (result) AFB_DYNAPI_NOTICE(apihandle_, "%s", json_object_to_json_string(result));
+ AFB_DYNAPI_ERROR(handle_, "Failed to call 'list' verb on '%s' API!", h.c_str());
+ if (result) AFB_DYNAPI_NOTICE(handle_, "%s", json_object_to_json_string(result));
return -1;
- }
+ }
+
+
json_object* streams = json_object_object_get(response, "response");
- AFB_DYNAPI_DEBUG(apihandle_, "Called 'list' verb on '%s' API: %s", h.c_str(), json_object_to_json_string(streams));
+ AFB_DYNAPI_DEBUG(handle_, "Called 'list' verb on '%s' API: %s", h.c_str(), json_object_to_json_string(streams));
json_object* array = json_object_object_get(streams, "streams");
size_t streams_count = json_object_array_length(array);
for(size_t i = 0; i < streams_count; ++i)
@@ -127,31 +160,33 @@ int ahl_binding_t::init()
std::string device_uri;
json_object* item = json_object_array_get_idx(array, i);
- jcast(stream_name, item, "name");
- jcast(device_uri, item, "cardId");
+ jcast(stream_name, item, "uid");
+ jcast(device_uri, item, "alsa");
config_.set_device_uri(stream_name, device_uri);
}
}
- afb_dynapi_seal(apihandle_);
- AFB_DYNAPI_NOTICE(apihandle_, "API is now sealed!");
+ afb_dynapi_seal(handle_);
+ AFB_DYNAPI_NOTICE(handle_, "API is now sealed!");
actions_["volume"] = std::bind(&ahl_binding_t::volume, this, _1, _2, _3, _4);
actions_["open"] = std::bind(&ahl_binding_t::open, this, _1, _2, _3, _4);
+ actions_["close"] = std::bind(&ahl_binding_t::close, this, _1, _2, _3, _4);
+ actions_["interrupt"] = std::bind(&ahl_binding_t::interrupt, this, _1, _2, _3, _4);
return 0;
}
void ahl_binding_t::event(std::string name, json_object* arg)
{
- AFB_DYNAPI_DEBUG(apihandle_, "Event '%s' received with the following arg: %s", name.c_str(), json_object_to_json_string(arg));
+ AFB_DYNAPI_DEBUG(handle_, "Event '%s' received with the following arg: %s", name.c_str(), json_object_to_json_string(arg));
}
void ahl_binding_t::load_static_verbs()
{
if (afb_dynapi_add_verb(
- apihandle_,
+ handle_,
"get_roles",
"Retrieve array of available audio roles",
[](afb_request* r) { ahl_binding_t::instance().get_roles(r); },
@@ -193,10 +228,10 @@ int ahl_binding_t::load_controller_config(const std::string& path)
{
CtlConfigT* controller_config;
- controller_config = CtlLoadMetaData(apihandle_, path.c_str());
+ controller_config = CtlLoadMetaData(handle_, path.c_str());
if (!controller_config)
{
- AFB_DYNAPI_ERROR(apihandle_, "Failed to load controller from config file!");
+ AFB_DYNAPI_ERROR(handle_, "Failed to load controller from config file!");
return -1;
}
@@ -219,7 +254,7 @@ int ahl_binding_t::load_controller_config(const std::string& path)
{.key = nullptr, .uid = nullptr, .info = nullptr, .loadCB = nullptr, .handle = nullptr, .actions = nullptr}
};
- CtlLoadSections(apihandle_, controller_config, controller_sections);
+ CtlLoadSections(handle_, controller_config, controller_sections);
return 0;
}
@@ -231,10 +266,10 @@ int ahl_binding_t::load_config(CtlSectionT* section, json_object* o)
// Add corresponding verbs
for(const auto& r : config_.roles())
{
- AFB_DYNAPI_NOTICE(apihandle_, "New audio role: %s", r.name().c_str());
+ AFB_DYNAPI_NOTICE(handle_, "New audio role: %s", r.name().c_str());
if (afb_dynapi_add_verb(
- apihandle_,
+ handle_,
r.name().c_str(),
r.description().c_str(),
[](afb_request* r) { ahl_binding_t::instance().audiorole(r); },
@@ -251,12 +286,6 @@ int ahl_binding_t::load_config(CtlSectionT* section, json_object* o)
return 0;
}
-int afbBindingVdyn(afb_dynapi* handle)
-{
- if (!handle) return -1;
- return ahl_binding_t::instance().build(handle);
-}
-
void ahl_binding_t::audiorole(afb_request* req)
{
std::string verb = afb_request_get_verb(req);
@@ -305,10 +334,10 @@ void ahl_binding_t::volume(afb_request* req, std::string role, std::string strea
json_object* a = json_object_new_object();
json_object_object_add(a, "volume", value);
- AFB_DYNAPI_DEBUG(apihandle_, "Call the HAL with the following argument: %s", json_object_to_json_string(a));
+ AFB_DYNAPI_DEBUG(handle_, "Call the HAL with the following argument: %s", json_object_to_json_string(a));
afb_dynapi_call(
- apihandle_,
+ handle_,
config_.hals()[0].c_str(), // BUG: What to do if multiple hals ?
stream.c_str(),
a,
@@ -331,14 +360,86 @@ void ahl_binding_t::open(afb_request* req, std::string role, std::string stream,
{
if (r.name() == role)
{
- AFB_DYNAPI_DEBUG(apihandle_, "HELLO");
-
- json_object* result = json_object_new_object();
- json_object_object_add(result, "device_uri", json_object_new_string(r.device_uri().c_str()));
-
- afb_request_success(req, result, nullptr);
+ if (
+ ext::cfind_if(opened_roles_,
+ [&role](const role_t& r){ return r.name() == role;}) != opened_roles_.end()
+ )
+ {
+ afb_request_fail(req, "This role is already opened!", nullptr);
+ return;
+ }
+ // Execute policy for current asked role
+ policy_open(req, r);
return;
}
}
afb_request_fail(req, "Can't open the specified role!", nullptr);
}
+
+void ahl_binding_t::policy_open(afb_request* req, const role_t& role)
+{
+ if(role.interrupts().size())
+ {
+ const interrupt_t& i = role.interrupts()[0];
+ /*if (i.type() == "mute")
+ {
+ }
+ else if (i.type() == "continue")
+ {
+ }
+ else if (i.type() == "cancel")
+ {
+ }
+ else */if (i.type() == "ramp")
+ {
+ for(const auto& r: opened_roles_)
+ {
+ if (role.priority() > r.priority())
+ {
+ // { "ramp" : { "uid" : "ramp-slow", "volume" : 30 } }
+ json_object* arg = json_object_new_object();
+ json_object_object_add(arg, "ramp", i.args());
+ json_object_get(i.args());
+ json_object* result = nullptr;
+
+ AFB_DYNAPI_NOTICE(handle_, "Call 'smixer'/'%s' '%s", r.stream().c_str(), json_object_to_json_string(arg));
+
+ if(afb_dynapi_call_sync(handle_, "smixer", r.stream().c_str(), arg, &result))
+ {
+ afb_request_fail(req, "Failed to call 'ramp' action on stream", nullptr);
+ return;
+ }
+ AFB_DYNAPI_NOTICE(handle_, "POLICY: Applying a ramp to '%s' stream because '%s' is opened and have higher priority!", r.stream().c_str(), role.stream().c_str());
+ }
+ }
+ }
+ else
+ {
+ afb_request_fail(req, "Unkown interrupt uid!", nullptr);
+ return;
+ }
+ }
+
+ json_object* result = json_object_new_object();
+ json_object_object_add(result, "device_uri", json_object_new_string(role.device_uri().c_str()));
+ afb_request_success(req, result, nullptr);
+ opened_roles_.push_back(role);
+}
+
+void ahl_binding_t::close(afb_request* req, std::string role, std::string stream, json_object* arg)
+{
+ AFB_DYNAPI_DEBUG(handle_, "Got this arg: %s", json_object_to_json_string(arg));
+ auto it = ext::cfind_if(opened_roles_, [&role](const role_t& r) { return r.name() == role; });
+ if (it == opened_roles_.cend())
+ {
+ afb_request_fail(req, "This role is already closed!", nullptr);
+ return;
+ }
+ opened_roles_.erase(it);
+ afb_request_success(req, nullptr, "Role closed!");
+}
+
+void ahl_binding_t::interrupt(afb_request* req, std::string role, std::string stream, json_object* arg)
+{
+ afb_request_fail(req, "Not implemented yet!", nullptr);
+}
diff --git a/ahl-binding/ahl-binding.hpp b/ahl-binding/ahl-binding.hpp
index 34d6492..62a518e 100644
--- a/ahl-binding/ahl-binding.hpp
+++ b/ahl-binding/ahl-binding.hpp
@@ -22,6 +22,7 @@
#include <sstream>
#include <vector>
#include <map>
+#include <cassert>
#include "config_entry.hpp"
#include "ahl-4a.hpp"
@@ -37,19 +38,41 @@ extern "C" {
#include <string.h>
#include <ctl-config.h>
- int afbBindingVdyn(afb_dynapi* handle);
+ int afbBindingVdyn(afb_dynapi* handle);
+ int ahl_api_create(void*, struct afb_dynapi*);
};
+namespace ext
+{
+ template<class T>
+ typename T::iterator find(T& container, const typename T::value_type& value)
+ {
+ return std::find(std::begin(container), std::end(container), value);
+ }
+
+ template<class T, class UnaryPredicate>
+ typename T::iterator find_if(T& container, UnaryPredicate pred)
+ {
+ return std::find_if(std::begin(container), std::end(container), pred);
+ }
+
+ template<class T, class UnaryPredicate>
+ typename T::iterator cfind_if(T& container, UnaryPredicate pred)
+ {
+ return std::find_if(std::begin(container), std::end(container), pred);
+ }
+}
+
class ahl_binding_t
{
using role_action = std::function<void(afb_request*, std::string, std::string, json_object*)>;
-private:
+private:
afb_dynapi* handle_;
- afb_dynapi* apihandle_;
ahl_4a_t config_;
std::map<std::string, role_action> actions_;
+ std::vector<role_t> opened_roles_;
explicit ahl_binding_t();
@@ -60,10 +83,13 @@ private:
void volume(afb_request* req, std::string role, std::string stream, json_object* arg);
void open(afb_request* req, std::string role, std::string stream, json_object* arg);
+ void close(afb_request* req, std::string role, std::string stream, json_object* arg);
+ void interrupt(afb_request* req, std::string role, std::string stream, json_object* arg);
+
+ void policy_open(afb_request* req, const role_t& role);
public:
static ahl_binding_t& instance();
- int build(afb_dynapi* handle);
int preinit(afb_dynapi* handle);
int init();
void event(std::string name, json_object* arg);
diff --git a/ahl-binding/interrupt.cpp b/ahl-binding/interrupt.cpp
new file mode 100644
index 0000000..fc54139
--- /dev/null
+++ b/ahl-binding/interrupt.cpp
@@ -0,0 +1,34 @@
+#include "interrupt.hpp"
+
+interrupt_t::interrupt_t(json_object* o)
+{
+ jcast(type_, o, "type");
+ args_ = json_object_object_get(o, "args");
+}
+
+interrupt_t& interrupt_t::operator<<(json_object* o)
+{
+ jcast(type_, o, "type");
+ args_ = json_object_object_get(o, "args");
+ return *this;
+}
+
+std::string interrupt_t::type() const
+{
+ return type_;
+}
+
+json_object* interrupt_t::args() const
+{
+ return args_;
+}
+
+void interrupt_t::type(std::string v)
+{
+ type_ = v;
+}
+
+void interrupt_t::args(json_object* v)
+{
+ args_ = v;
+}
diff --git a/ahl-binding/interrupt.hpp b/ahl-binding/interrupt.hpp
new file mode 100644
index 0000000..caf5cda
--- /dev/null
+++ b/ahl-binding/interrupt.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "jsonc_utils.hpp"
+
+class interrupt_t
+{
+private:
+ std::string type_;
+ json_object* args_;
+
+public:
+ explicit interrupt_t() = default;
+ explicit interrupt_t(const interrupt_t&) = default;
+ explicit interrupt_t(interrupt_t&&) = default;
+ ~interrupt_t() = default;
+
+ interrupt_t& operator=(const interrupt_t&) = default;
+ interrupt_t& operator=(interrupt_t&&) = default;
+
+ explicit interrupt_t(json_object* o);
+ interrupt_t& operator<<(json_object* o);
+
+ std::string type() const;
+ json_object* args() const;
+
+ void type(std::string v);
+ void args(json_object* v);
+};
diff --git a/ahl-binding/jsonc_utils.hpp b/ahl-binding/jsonc_utils.hpp
index 02e113b..5e82fca 100644
--- a/ahl-binding/jsonc_utils.hpp
+++ b/ahl-binding/jsonc_utils.hpp
@@ -31,6 +31,7 @@ inline T& jcast(T& v, json_object* o)
template<class T>
inline T& jcast_array(T& v, json_object* o)
{
+ if (o == nullptr) return v;
auto sz = json_object_array_length(o);
for(auto i = 0; i < sz ; ++i)
{
diff --git a/ahl-binding/role.cpp b/ahl-binding/role.cpp
index f0f744f..9338436 100644
--- a/ahl-binding/role.cpp
+++ b/ahl-binding/role.cpp
@@ -34,6 +34,7 @@ role_t::role_t(json_object* j)
jcast(description_, j, "description");
jcast(priority_, j, "priority");
jcast(stream_, j, "stream");
+ jcast_array(interrupts_, j, "interrupts");
}
role_t& role_t::operator<<(json_object* j)
@@ -43,6 +44,7 @@ role_t& role_t::operator<<(json_object* j)
jcast(description_, j, "description");
jcast(priority_, j, "priority");
jcast(stream_, j, "stream");
+ jcast_array(interrupts_, j, "interrupts");
return *this;
}
@@ -105,3 +107,8 @@ void role_t::device_uri(std::string v)
{
device_uri_ = v;
}
+
+const std::vector<interrupt_t>& role_t::interrupts() const
+{
+ return interrupts_;
+}
diff --git a/ahl-binding/role.hpp b/ahl-binding/role.hpp
index 5b3d6b1..fa1bcd4 100644
--- a/ahl-binding/role.hpp
+++ b/ahl-binding/role.hpp
@@ -17,7 +17,8 @@
* limitations under the License.
*/
-#include "jsonc_utils.hpp"
+#include <vector>
+#include "interrupt.hpp"
class role_t
{
@@ -28,6 +29,7 @@ private:
std::string stream_;
std::string device_uri_;
int priority_;
+ std::vector<interrupt_t> interrupts_;
public:
explicit role_t() = default;
@@ -50,6 +52,7 @@ public:
std::string stream() const;
int priority() const;
std::string device_uri() const;
+ const std::vector<interrupt_t>& interrupts() const;
void uid(std::string v);
void name(std::string v);