--- /dev/null
+/*
+ * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
+ *
+ * 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.
+ */
+
+#include <stdarg.h>
+#include <sys/socket.h>
+#include <iostream>
+#include <algorithm>
+#include <thread>
+#include <errno.h>
+#include <libsoundmanager/libsoundmanager.hpp>
+
+#define ELOG(args,...) _ELOG(__FUNCTION__,__LINE__,args,##__VA_ARGS__)
+#define DLOG(args,...) _DLOG(__FUNCTION__,__LINE__,args,##__VA_ARGS__)
+
+using namespace std;
+
+static void _DLOG(const char* func, const int line, const char* log, ...);
+static void _ELOG(const char* func, const int line, const char* log, ...);
+static bool has_verb(const string& verb);
+static const char API[] = "soundmanager";
+
+static void _on_hangup_static(void *closure, struct afb_wsj1 *wsj)
+{
+ static_cast<LibSoundmanager*>(closure)->on_hangup(NULL,wsj);
+}
+
+static void _on_call_static(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
+{
+ /* LibSoundmanager is not called from other process */
+}
+
+static void _on_event_static(void* closure, const char* event, struct afb_wsj1_msg *msg)
+{
+ static_cast<LibSoundmanager*>(closure)->on_event(NULL,event,msg);
+}
+
+static void _on_reply_static(void *closure, struct afb_wsj1_msg *msg)
+{
+ static_cast<LibSoundmanager*>(closure)->on_reply(NULL,msg);
+}
+
+
+/**
+ * This function is constructor
+ *
+ * #### Parameters
+ * - port [in] : This argument should be specified to the port number to be used for websocket
+ * - token [in] : This argument should be specified to the token to be used for websocket
+ *
+ * #### Rreturn
+ * Nothing
+ *
+ * #### Note
+ * Use this constructor
+ *
+ */
+LibSoundmanager::LibSoundmanager(const int port, const string& token)
+{
+ int ret;
+ if(port > 0 && token.size() > 0)
+ {
+ mport = port;
+ mtoken = token;
+ }
+ else
+ {
+ ELOG("port and token should be > 0, Initial port and token uses.");
+ }
+
+ ret = initialize_websocket();
+ if(ret != 0 )
+ {
+ ELOG("Failed to initialize websocket");
+ }
+ else{
+ DLOG("Initialized");
+ }
+}
+
+LibSoundmanager::~LibSoundmanager()
+{
+ if(mploop)
+ {
+ sd_event_unref(mploop);
+ }
+ if(sp_websock != NULL)
+ {
+ free(sp_websock);
+ }
+}
+
+/**
+ * This function register callback function for reply/event message from sound manager
+ *
+ * #### Parameters
+ * - event_cb [in] : This argument should be specified to the callback for subscribed event
+ * - reply_cb [in] : This argument should be specified to the reply callback for call function
+ *
+ * #### Rreturn
+ * - Returns 0 on success or -1 in case of error.
+ *
+ * #### Note
+ * Event callback is invoked by sound manager for event you subscribed.
+ * If you would like to get event, please call subscribe function before/after this function
+ */
+void LibSoundmanager::register_callback(
+ void (*event_cb)(const std::string& event, struct json_object* event_contents),
+ void (*reply_cb)(struct json_object* reply_contents),
+ void (*hangup_cb)(void))
+{
+ onEvent = event_cb;
+ onReply = reply_cb;
+ onHangup = hangup_cb;
+}
+
+int LibSoundmanager::initialize_websocket()
+{
+ mploop = NULL;
+ onEvent = nullptr;
+ onReply = nullptr;
+ int ret = sd_event_default(&mploop);
+ if(ret < 0)
+ {
+ ELOG("Failed to create event loop");
+ goto END;
+ }
+ /* Initialize interface from websocket */
+
+ minterface.on_hangup = _on_hangup_static;
+ minterface.on_call = _on_call_static; /* Is this necessary? */
+ minterface.on_event = _on_event_static;
+ muri += "ws://localhost:" + to_string(mport) + "/api?token=" + mtoken; /*To be modified*/
+ sp_websock = afb_ws_client_connect_wsj1(mploop, muri.c_str(), &minterface, this);
+ if(sp_websock == NULL)
+ {
+ ELOG("Failed to create websocket connection");
+ goto END;
+ }
+
+ /* creates the evsrc */
+ //ret = sd_event_add_io(mploop,&mevent_src, sp_websock->fd, EPOLLIN, event_callback, NULL);
+
+ return 0;
+END:
+ if(mploop)
+ {
+ sd_event_unref(mploop);
+ }
+ return -1;
+}
+
+static void *event_loop_run(void *args)
+{
+ struct sd_event* loop = (struct sd_event*)(args);
+ DLOG("start eventloop");
+ for(;;)
+ sd_event_run(loop, 30000000);
+}
+
+/**
+ * This function start receiving the reply/event message from sound manager
+ *
+ * #### Parameters
+ * Nothing
+ * #### Rreturn
+ * - Returns thread_id on success or -1 in case of error.
+ *
+ * #### Note
+ *
+ */
+int LibSoundmanager::run_eventloop()
+{
+ if(mploop && sp_websock)
+ {
+ pthread_t thread_id;
+ int ret = pthread_create(&thread_id, NULL, event_loop_run, mploop);
+ if(ret != 0)
+ {
+ ELOG("Cannot run eventloop due to error:%d", errno);
+ return -1;
+ }
+ else
+ return thread_id;
+ }
+ else
+ {
+ ELOG("Connecting is not established yet");
+ return -1;
+ }
+}
+
+/**
+ * This function calls the API of Audio Manager via WebSocket
+ *
+ * #### Parameters
+ * - verb [in] : This argument should be specified to the API name (e.g. "connect")
+ * - arg [in] : This argument should be specified to the argument of API. And this argument expects JSON object
+ *
+ * #### Rreturn
+ * - Returns 0 on success or -1 in case of error.
+ *
+ * #### Note
+ * To call Audio Manager's APIs, the application should set its function name, arguments to JSON format.
+ *
+ */
+int LibSoundmanager::call(const string& verb, struct json_object* arg)
+{
+ int ret;
+ if(!sp_websock)
+ {
+ return -1;
+ }
+ if (!has_verb(verb))
+ {
+ ELOG("verb doesn't exit");
+ return -1;
+ }
+ ret = afb_wsj1_call_j(sp_websock, API, verb.c_str(), arg, _on_reply_static, this);
+ if (ret < 0) {
+ ELOG("Failed to call verb:%s",verb.c_str());
+ }
+ return ret;
+}
+
+/**
+ * This function calls the API of Audio Manager via WebSocket
+ * This function is overload function of "call"
+ *
+ * #### Parameters
+ * - verb [in] : This argument should be specified to the API name (e.g. "connect")
+ * - arg [in] : This argument should be specified to the argument of API. And this argument expects JSON object
+ *
+ * #### Rreturn
+ * - Returns 0 on success or -1 in case of error.
+ *
+ * #### Note
+ * To call Audio Manager's APIs, the application should set its function name, arguments to JSON format.
+ *
+ */
+int LibSoundmanager::call(const char* verb, struct json_object* arg)
+{
+ int ret;
+ if(!sp_websock)
+ {
+ return -1;
+ }
+ if (!has_verb(string(verb)))
+ {
+ ELOG("verb doesn't exit");
+ return -1;
+ }
+ ret = afb_wsj1_call_j(sp_websock, API, verb, arg, _on_reply_static, this);
+ if (ret < 0) {
+ ELOG("Failed to call verb:%s",verb);
+ }
+ return ret;
+}
+
+/**
+ * Register callback function for each event
+ *
+ * #### Parameters
+ * - event_name [in] : This argument should be specified to the event name
+ *
+ * #### Rreturn
+ * - Returns 0 on success or -1 in case of error.
+ *
+ * #### Note
+ * This function enables to get an event to your callback function.
+ * Regarding the list of event name, please refer to CommandSender API and RountingSender API.
+ *
+ */
+int LibSoundmanager::subscribe(const string& event_name)
+{
+ if(!sp_websock)
+ {
+ return -1;
+ }
+ struct json_object* j_obj = json_object_new_object();
+ json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
+
+ int ret = afb_wsj1_call_j(sp_websock, API, "subscribe", j_obj, _on_reply_static, this);
+ if (ret < 0) {
+ ELOG("Failed to call verb:%s",__FUNCTION__);
+ }
+ return ret;
+}
+
+/**
+ * Unregister callback function for each event
+ *
+ * #### Parameters
+ * - event_name [in] : This argument should be specified to the event name
+ *
+ * #### Rreturn
+ * - Returns 0 on success or -1 in case of error.
+ *
+ * #### Note
+ * This function disables to get an event to your callback function.
+ *
+ */
+int LibSoundmanager::unsubscribe(const string& event_name)
+{
+ if(!sp_websock)
+ {
+ return -1;
+ }
+ struct json_object* j_obj = json_object_new_object();
+ json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str()));
+
+ int ret = afb_wsj1_call_j(sp_websock, API, "unsubscribe", j_obj, _on_reply_static, this);
+ if (ret < 0) {
+ ELOG("Failed to call verb:%s",__FUNCTION__);
+ }
+ return ret;
+}
+
+am_Error_e LibSoundmanager::connect(const am_sourceID_t sourceID, const am_sinkID_t sinkID, am_mainConnectionID_t& mainConnectionID)
+{
+ /*int ret;
+ char *key;
+ rc = asprintf(&key, "%d:%s/%s", ++num, api, "connect");
+ ret = afb_wsj1_call_s(wsj1, api, verb, object, on_reply, key);
+ if(ret < 0)
+ {
+ fprintf(stderr, "calling %s/%s(%s) failed: %m\n", api, verb, object);
+
+ }*/
+ /* open the json scripts */
+ // get mainconnedction ID */
+ //mainConnectionID = xx;
+ return E_OK;
+}
+
+am_Error_e LibSoundmanager::disconnect(const am_mainConnectionID_t mainConnectionID)
+{
+ return E_OK;
+}
+
+/*const struct afb_wsj1* LibSoundmanager::get_websocket_handler()
+{
+ if(sp_websock)
+ {
+ return sp_websock;
+ }
+ return nullptr;
+}
+
+const struct sd_event* LibSoundmanager::get_sd_event()
+{
+ if(mploop)
+ {
+ return mploop;
+ }
+ return nullptr;
+}*/
+
+/************* Callback Function *************/
+
+void LibSoundmanager::on_hangup(void *closure, struct afb_wsj1 *wsj)
+{
+ DLOG("%s called", __FUNCTION__);
+ if(onHangup != nullptr)
+ {
+ onHangup();
+ }
+}
+
+void LibSoundmanager::on_call(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
+{
+}
+
+/*
+* event is like "soundmanager/newMainConnection"
+* msg is like {"event":"soundmanager\/newMainConnection","data":{"mainConnectionID":3,"sourceID":101,"sinkID":100,"delay":0,"connectionState":4},"jtype":"afb-event"})}
+* ^key^ ^^^^^^^^^^^^ value ^^^^^^^^^^^^
+* so you can get
+ event name : struct json_object obj = json_object_object_get(msg,"event")
+*/
+void LibSoundmanager::on_event(void *closure, const char *event, struct afb_wsj1_msg *msg)
+{
+ cout << "ON-EVENT:" << event << "(" << afb_wsj1_msg_object_s(msg) << ")" << endl;
+ if(onEvent != nullptr)
+ {
+ const string ev(event);
+ struct json_object* ev_contents = afb_wsj1_msg_object_j(msg);
+ onEvent(ev, ev_contents);
+ }
+}
+
+void LibSoundmanager::on_reply(void *closure, struct afb_wsj1_msg *msg)
+{
+ cout << "ON-REPLY:" << "(" << afb_wsj1_msg_object_s(msg) << ")" << endl;
+ if(onReply != nullptr)
+ {
+ struct json_object* reply = afb_wsj1_msg_object_j(msg);
+ onReply(reply);
+ }
+}
+
+/* Internal Function in libsoundmanager */
+
+static void _ELOG(const char* func, const int line, const char* log, ...)
+{
+ char *message;
+ va_list args;
+ va_start(args, log);
+ if (log == NULL || vasprintf(&message, log, args) < 0)
+ message = NULL;
+ cout << "[ERROR]" << func << "(" << line << "):" << message << endl;
+ va_end(args);
+ free(message);
+}
+
+static void _DLOG(const char* func, const int line, const char* log, ...)
+{
+ char *message;
+ va_list args;
+ va_start(args, log);
+ if (log == NULL || vasprintf(&message, log, args) < 0)
+ message = NULL;
+ cout << "[DEBUG]" << func << "(" << line << "):" << message << endl;
+ va_end(args);
+ free(message);
+}
+
+static bool has_verb(const string& verb)
+{
+ DLOG("verb is %s", verb.c_str());
+ if(find(api_list.begin(), api_list.end(), verb) != api_list.end())
+ return true;
+ else
+ return false;
+}