#define DBUS_ML_BUS_NAME "org.tizen.machinelearning.service"
#define DBUS_ML_PATH "/Org/Tizen/MachineLearning/Service"
+/* Pipeline Interface */
#define DBUS_PIPELINE_INTERFACE "org.tizen.machinelearning.service.pipeline"
#define DBUS_PIPELINE_PATH "/Org/Tizen/MachineLearning/Service/Pipeline"
#define DBUS_PIPELINE_I_GET_STATE_HANDLER "handle_get_state"
#define DBUS_PIPELINE_I_GET_DESCRIPTION_HANDLER "handle_get_description"
+/* Model Interface */
+#define DBUS_MODEL_INTERFACE "org.tizen.machinelearning.service.model"
+#define DBUS_MODEL_PATH "/Org/Tizen/MachineLearning/Service/Model"
+
+#define DBUS_MODEL_I_HANDLER_SET_PATH "handle_set_path"
+#define DBUS_MODEL_I_HANDLER_GET_PATH "handle_get_path"
+#define DBUS_MODEL_I_HANDLER_DELETE "handle_delete"
+
#endif /* __GDBUS_INTERFACE_H__ */
#include "pipeline-dbus.h"
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
/**
* @brief DBus signal handler information to connect
*/
* @brief Disconnect the DBus message bus.
*/
void gdbus_put_system_connection (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
#endif /* __GDBUS_UTIL_H__ */
#ifndef __MODULES_H__
#define __MODULES_H__
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
/**
* @brief Data structure contains the name and callback functions for a specific DBus interface.
*/
* @param[in] module DBus interface information.
*/
void remove_module (const struct module_ops *module);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
#endif /* __MODULES_H__ */
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * @file service-db.hh
+ * @date 28 Mar 2022
+ * @brief NNStreamer/Service Database Interface
+ * @see https://github.com/nnstreamer/api
+ * @author Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#ifndef __SERVICE_DB_HH__
+#define __SERVICE_DB_HH__
+
+#include <leveldb/c.h>
+#include <iostream>
+
+/**
+ * @brief Interface for database operation of ML service
+ */
+class IMLServiceDB
+{
+public:
+ /**
+ * @brief Destroy the IMLServiceDB object
+ */
+ virtual ~IMLServiceDB ()
+ {
+ };
+ virtual void connectDB () = 0;
+ virtual void disconnectDB () = 0;
+ virtual void put (const std::string key, const std::string value) = 0;
+ virtual void get (const std::string name,
+ std::string & out_value) = 0;
+ virtual void del (const std::string name) = 0;
+};
+
+/**
+ * @brief Class for implementation of IMLServiceDB
+ */
+class MLServiceLevelDB : public IMLServiceDB
+{
+public:
+ MLServiceLevelDB (const MLServiceLevelDB &) = delete;
+ MLServiceLevelDB (MLServiceLevelDB &&) = delete;
+ MLServiceLevelDB & operator= (const MLServiceLevelDB &) = delete;
+ MLServiceLevelDB & operator= (MLServiceLevelDB &&) = delete;
+
+ virtual void connectDB () override;
+ virtual void disconnectDB () override;
+ virtual void put (const std::string name,
+ const std::string value) override;
+ virtual void get (std::string name,
+ std::string & out_value) override;
+ virtual void del (std::string name);
+
+ static IMLServiceDB & getInstance (void);
+
+private:
+ MLServiceLevelDB (std::string path);
+ virtual ~MLServiceLevelDB ();
+
+ std::string path;
+ leveldb_t *db_obj;
+ leveldb_readoptions_t *db_roptions;
+ leveldb_writeoptions_t *db_woptions;
+};
+
+#endif /* __SERVICE_DB_HH__ */
nns_ml_agent_gen_srcs = []
nns_ml_agent_incs = include_directories('includes')
+ dlog_dep = dependency('dlog')
+ libsystemd_dep = dependency('libsystemd')
+
+ nns_ml_agent_srcs += join_paths('main.c')
+ nns_ml_agent_srcs += join_paths('modules.c')
+ nns_ml_agent_srcs += join_paths('gdbus-util.c')
+ nns_ml_agent_srcs += join_paths('service-db.cc')
+ nns_ml_agent_srcs += join_paths('pipeline-module.c')
+ nns_ml_agent_srcs += join_paths('model-dbus-impl.cc')
+
# Generate GDbus header and code
gdbus_prog = find_program('gdbus-codegen', required : true)
gdbus_gen_src = custom_target('gdbus-gencode',
'--generate-c-code', 'pipeline-dbus',
'--output-directory', meson.current_build_dir(),
'@INPUT@'])
-
nns_ml_agent_gen_srcs += gdbus_gen_src
- nns_ml_agent_srcs += join_paths('main.c')
- nns_ml_agent_srcs += join_paths('modules.c')
- nns_ml_agent_srcs += join_paths('gdbus-util.c')
- nns_ml_agent_srcs += join_paths('pipeline-module.c')
+ gdbus_gen_model_src = custom_target('gdbus-model-gencode',
+ input : '../dbus/model-dbus.xml',
+ output : ['model-dbus.h', 'model-dbus.c'],
+ command : [gdbus_prog, '--interface-prefix', 'org.tizen',
+ '--generate-c-code', 'model-dbus',
+ '--output-directory', meson.current_build_dir(),
+ '@INPUT@'])
+ nns_ml_agent_gen_srcs += gdbus_gen_model_src
# Enable ML Agent Test
gdbus_gen_test_src = custom_target('gdbus-gencode-test',
gdbus_gen_header_dep = declare_dependency(sources : nns_ml_agent_gen_srcs)
gdbus_gen_header_test_dep = declare_dependency(sources : [nns_ml_agent_gen_srcs, gdbus_gen_test_src])
- dlog_dep = dependency('dlog')
- libsystemd_dep = dependency('libsystemd')
+
ai_service_daemon_deps = [
gdbus_gen_header_dep,
glib_dep,
gio_unix_dep,
gst_dep,
dlog_dep,
+ leveldb_dep,
libsystemd_dep
]
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * @file model-dbus-impl.cc
+ * @date 29 Jul 2022
+ * @brief DBus implementation for Model Interface
+ * @see https://github.com/nnstreamer/api
+ * @author Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <glib.h>
+#include <errno.h>
+
+#include "common.h"
+#include "modules.h"
+#include "gdbus-util.h"
+#include "dbus-interface.h"
+#include "model-dbus.h"
+#include "service-db.hh"
+
+static MachinelearningServiceModel *g_gdbus_instance = NULL;
+
+/**
+ * @brief Utility function to get the DBus proxy of Model interface.
+ */
+static MachinelearningServiceModel *
+gdbus_get_model_instance (void)
+{
+ return machinelearning_service_model_skeleton_new ();
+}
+
+/**
+ * @brief Utility function to release DBus proxy of Model interface.
+ */
+static void
+gdbus_put_model_instance (MachinelearningServiceModel ** instance)
+{
+ g_clear_object (instance);
+}
+
+/**
+ * @brief The callback function of SetPath method.
+ *
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target model.
+ * @param path the file path of target.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+dbus_cb_model_set_path (MachinelearningServiceModel *obj,
+ GDBusMethodInvocation *invoc,
+ const gchar *name,
+ const gchar *path)
+{
+ int ret = 0;
+ IMLServiceDB &db = MLServiceLevelDB::getInstance ();
+
+ try {
+ db.connectDB();
+ db.put (name, std::string (path));
+ }
+ catch (const std::runtime_error & e)
+ {
+ ret = -EIO;
+ }
+ catch (const std::invalid_argument & e)
+ {
+ ret = -EINVAL;
+ }
+ catch (const std::exception & e)
+ {
+ ret = -EIO;
+ }
+
+ db.disconnectDB ();
+ machinelearning_service_model_complete_set_path (obj, invoc, ret);
+
+ return TRUE;
+}
+
+/**
+ * @brief The callback function of GetPath method
+ *
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target model.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+dbus_cb_model_get_path (MachinelearningServiceModel *obj,
+ GDBusMethodInvocation *invoc,
+ const gchar *name)
+{
+ int ret = 0;
+ std::string ret_path;
+ IMLServiceDB & db = MLServiceLevelDB::getInstance ();
+
+ try {
+ db.connectDB ();
+ db.get (name, ret_path);
+ }
+ catch (const std::invalid_argument & e)
+ {
+ ret = -EINVAL;
+ }
+ catch (const std::runtime_error & e)
+ {
+ ret = -EIO;
+ }
+ catch (const std::exception & e)
+ {
+ ret = -EIO;
+ }
+
+ db.disconnectDB ();
+ machinelearning_service_model_complete_get_path (obj, invoc, ret_path.c_str(), ret);
+
+ return TRUE;
+}
+
+/**
+ * @brief The callback function of Delete method
+ *
+ * @param obj Proxy instance.
+ * @param invoc Method invocation handle.
+ * @param name The name of target model.
+ * @return @c TRUE if the request is handled. FALSE if the service is not available.
+ */
+static gboolean
+gdbus_cb_model_delete (MachinelearningServiceModel *obj,
+ GDBusMethodInvocation *invoc,
+ const gchar *name)
+{
+ int ret = 0;
+ IMLServiceDB & db = MLServiceLevelDB::getInstance ();
+
+ try {
+ db.connectDB ();
+ db.del (name);
+ }
+ catch (const std::invalid_argument & e)
+ {
+ ret = -EINVAL;
+ }
+ catch (const std::runtime_error & e)
+ {
+ ret = -EIO;
+ }
+ catch (const std::exception & e)
+ {
+ ret = -EIO;
+ }
+
+ db.disconnectDB ();
+ machinelearning_service_model_complete_delete (obj, invoc, ret);
+
+ return TRUE;
+}
+
+/**
+ * @brief Event handler list of Model interface
+ */
+static struct gdbus_signal_info handler_infos[] = {
+ {
+ .signal_name = DBUS_MODEL_I_HANDLER_SET_PATH,
+ .cb = G_CALLBACK (dbus_cb_model_set_path),
+ .cb_data = NULL,
+ .handler_id = 0,
+ }, {
+ .signal_name = DBUS_MODEL_I_HANDLER_GET_PATH,
+ .cb = G_CALLBACK (dbus_cb_model_get_path),
+ .cb_data = NULL,
+ .handler_id = 0,
+ }, {
+ .signal_name = DBUS_MODEL_I_HANDLER_DELETE,
+ .cb = G_CALLBACK (gdbus_cb_model_delete),
+ .cb_data = NULL,
+ .handler_id = 0,
+ },
+};
+
+/**
+ * @brief The callback function for probing Model Interface module.
+ */
+static int
+probe_model_module (void *data)
+{
+ int ret = 0;
+ g_debug ("probe_model_module");
+
+ g_gdbus_instance = gdbus_get_model_instance ();
+ if (NULL == g_gdbus_instance) {
+ g_critical ("cannot get a dbus instance for the %s interface\n",
+ DBUS_MODEL_INTERFACE);
+ return -ENOSYS;
+ }
+
+ ret = gdbus_connect_signal (g_gdbus_instance,
+ ARRAY_SIZE(handler_infos), handler_infos);
+ if (ret < 0) {
+ g_critical ("cannot register callbacks as the dbus method invocation "
+ "handlers\n ret: %d", ret);
+ ret = -ENOSYS;
+ goto out;
+ }
+
+ ret = gdbus_export_interface (g_gdbus_instance, DBUS_MODEL_PATH);
+ if (ret < 0) {
+ g_critical ("cannot export the dbus interface '%s' "
+ "at the object path '%s'\n", DBUS_MODEL_INTERFACE, DBUS_MODEL_PATH);
+ ret = -ENOSYS;
+ goto out_disconnect;
+ }
+
+ return 0;
+
+out_disconnect:
+ gdbus_disconnect_signal (g_gdbus_instance,
+ ARRAY_SIZE (handler_infos), handler_infos);
+
+out:
+ gdbus_put_model_instance (&g_gdbus_instance);
+
+ return ret;
+}
+
+/**
+ * @brief The callback function for initializing Model Interface module.
+ */
+static void
+init_model_module (void *data) { }
+
+/**
+ * @brief The callback function for exiting Model Interface module.
+ */
+static void
+exit_model_module (void *data)
+{
+ gdbus_disconnect_signal (g_gdbus_instance,
+ ARRAY_SIZE (handler_infos), handler_infos);
+ gdbus_put_model_instance (&g_gdbus_instance);
+}
+
+static const struct module_ops model_ops = {
+ .name = "model-interface",
+ .probe = probe_model_module,
+ .init = init_model_module,
+ .exit = exit_model_module,
+};
+
+MODULE_OPS_REGISTER (&model_ops)
--- /dev/null
+/* SPDX-License-Identifier: Apache-2.0 */
+/**
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved.
+ *
+ * @file service-db.cc
+ * @date 29 Jul 2022
+ * @brief Database implementation of NNStreamer/Service C-API
+ * @see https://github.com/nnstreamer/api
+ * @author Sangjung Woo <sangjung.woo@samsung.com>
+ * @bug No known bugs except for NYI items
+ */
+
+#include <glib.h>
+
+#include "service-db.hh"
+
+#define ML_DATABASE_PATH DB_PATH"/.ml-service-leveldb"
+
+/**
+ * @brief Get an instance of IMLServiceDB, which is created only once at runtime.
+ * @return IMLServiceDB& IMLServiceDB instance
+ */
+IMLServiceDB & MLServiceLevelDB::getInstance (void)
+{
+ static MLServiceLevelDB instance (ML_DATABASE_PATH);
+
+ return instance;
+}
+
+/**
+ * @brief Construct a new MLServiceLevelDB object
+ * @param path database path
+ */
+MLServiceLevelDB::MLServiceLevelDB (std::string path)
+: path (path), db_obj (nullptr), db_roptions (nullptr), db_woptions (nullptr)
+{
+ db_roptions = leveldb_readoptions_create ();
+ db_woptions = leveldb_writeoptions_create ();
+ leveldb_writeoptions_set_sync (db_woptions, 1);
+}
+
+/**
+ * @brief Destroy the MLServiceLevelDB object
+ */
+MLServiceLevelDB::~MLServiceLevelDB ()
+{
+ leveldb_readoptions_destroy (db_roptions);
+ leveldb_writeoptions_destroy (db_woptions);
+}
+
+/**
+ * @brief Connect the level DB and initialize the private variables.
+ */
+void
+MLServiceLevelDB::connectDB ()
+{
+ char *err = nullptr;
+ leveldb_options_t *db_options;
+
+ if (db_obj)
+ return;
+
+ db_options = leveldb_options_create ();
+ leveldb_options_set_create_if_missing (db_options, 1);
+
+ db_obj = leveldb_open (db_options, path.c_str (), &err);
+ leveldb_options_destroy (db_options);
+ if (err != nullptr) {
+ g_warning ("Error! Failed to open database located at '%s': leveldb_open() has returned an error: %s",
+ path.c_str (), err);
+ leveldb_free (err);
+ throw std::runtime_error ("Failed to connectDB()!");
+ }
+ return;
+}
+
+/**
+ * @brief Disconnect the level DB
+ * @note LevelDB does not support multi-process and it might cause
+ * the IO exception when multiple clients write the key simultaneously.
+ */
+void
+MLServiceLevelDB::disconnectDB ()
+{
+ if (db_obj) {
+ leveldb_close (db_obj);
+ db_obj = nullptr;
+ }
+}
+
+/**
+ * @brief Set the value with the given name.
+ * @note If the name already exists, the pipeline description is overwritten.
+ * @param[in] name Unique name to retrieve the associated pipeline description.
+ * @param[in] value The pipeline description to be stored.
+ */
+void
+MLServiceLevelDB::put (const std::string name,
+ const std::string value)
+{
+ char *err = NULL;
+
+ if (name.empty() || value.empty())
+ throw std::invalid_argument ("Invalid name or value parameters!");
+
+ leveldb_put (db_obj, db_woptions, name.c_str (), name.size (),
+ value.c_str (), value.size (), &err);
+ if (err != nullptr) {
+ g_warning
+ ("Failed to call leveldb_put () for the name, '%s' of the pipeline description (size: %zu bytes / description: '%.40s')",
+ name.c_str (), value.size (),
+ value.c_str ());
+ g_warning ("leveldb_put () has returned an error: %s", err);
+ leveldb_free (err);
+ throw std::runtime_error ("Failed to put()!");
+ }
+}
+
+/**
+ * @brief Get the value with the given name.
+ * @param[in] name The unique name to retrieve.
+ * @param[out] value The pipeline corresponding with the given name.
+ */
+void
+MLServiceLevelDB::get (const std::string name,
+ std::string & out_value)
+{
+ char *err = NULL;
+ char *value = NULL;
+ gsize read_len;
+
+ if (name.empty())
+ throw std::invalid_argument ("Invalid name parameters!");
+
+ value = leveldb_get (db_obj, db_roptions, name.c_str (), name.size (),
+ &read_len, &err);
+ if (err != nullptr) {
+ g_warning
+ ("Failed to call leveldb_get() for the name %s. Error message is %s.",
+ name.c_str (), err);
+ leveldb_free (err);
+ leveldb_free (value);
+ throw std::runtime_error ("Failed to get()!");
+ }
+
+ if (!value) {
+ g_warning
+ ("Failed to find the key %s. The key should be set before reading it",
+ name.c_str ());
+ throw std::invalid_argument ("Fail to find the key");
+ }
+
+ out_value = std::string (value, read_len);
+ leveldb_free (value);
+ return;
+}
+
+/**
+ * @brief Delete the value with a given name.
+ * @param[in] name The unique name to delete
+ */
+void
+MLServiceLevelDB::del (const std::string name)
+{
+ char *err = NULL;
+ char *value = NULL;
+ gsize read_len;
+
+ if (name.empty())
+ throw std::invalid_argument ("Invalid name parameters!");
+
+ /* Check whether the key exists or not. */
+ value = leveldb_get (db_obj, db_roptions, name.c_str (), name.size (),
+ &read_len, &err);
+ if (!value) {
+ g_warning
+ ("Failed to find the key %s. The key should be set before reading it",
+ name.c_str ());
+ throw std::invalid_argument ("Fail to find the key");
+ }
+ leveldb_free (value);
+
+ leveldb_delete (db_obj, db_woptions, name.c_str (), name.size (), &err);
+ if (err != nullptr) {
+ g_warning ("Failed to delete the key %s. Error message is %s", name.c_str (),
+ err);
+ leveldb_free (err);
+ throw std::runtime_error ("Failed to del()!");
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<node name="/Org/Tizen/MachineLearning/Service">
+ <interface name="org.tizen.machinelearning.service.model">
+ <!-- Set the file path of the designated neural network model -->
+ <method name="SetPath">
+ <arg type="s" name="name" direction="in" />
+ <arg type="s" name="path" direction="in" />
+ <arg type="i" name="result" direction="out" />
+ </method>
+ <!-- Get the file path of the designated neural network model -->
+ <method name="GetPath">
+ <arg type="s" name="name" direction="in" />
+ <arg type="s" name="path" direction="out" />
+ <arg type="i" name="result" direction="out" />
+ </method>
+ <!-- Delete the file path of the designated neural network model -->
+ <method name="Delete">
+ <arg type="s" name="name" direction="in" />
+ <arg type="i" name="result" direction="out" />
+ </method>
+ </interface>
+</node>