[C-Api/Service] define API for ml-resource
authorJaeyun Jung <jy1210.jung@samsung.com>
Wed, 5 Jul 2023 12:16:07 +0000 (21:16 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Fri, 28 Jul 2023 09:15:50 +0000 (18:15 +0900)
Define new API set for ml-resource control.

Signed-off-by: Jaeyun Jung <jy1210.jung@samsung.com>
c/include/ml-api-common.h
c/include/ml-api-service.h
c/src/ml-api-service-agent-client.c
tests/capi/unittest_capi_service_agent_client.cc

index cdd4740..d3b1961 100644 (file)
@@ -495,6 +495,22 @@ int ml_option_set (ml_option_h option, const char *key, void *value, ml_data_des
  */
 int ml_option_get (ml_option_h option, const char *key, void **value);
 
+/******************
+ * ML INFORMATION *
+ ******************/
+
+/**
+ * @brief A handle of a ml-information instance.
+ * @since_tizen 8.0
+ */
+typedef void *ml_information_h;
+
+/**
+ * @brief A handle of a list of ml-information instance.
+ * @since_tizen 8.0
+ */
+typedef void *ml_information_list_h;
+
 /**
  * @}
  */
index 576c7a5..1488f74 100644 (file)
@@ -333,6 +333,95 @@ int ml_service_model_get_all (const char *name, ml_option_h *info_list[], unsign
 int ml_service_model_delete (const char *name, const unsigned int version);
 
 /**
+ * @brief Adds new information of machine learning resources those contain images, audio samples, binary files, and so on.
+ * @since_tizen 8.0
+ * @remarks If same name is already registered in machine learning service, this returns no error and the list of resource files will be updated.
+ * @remarks %http://tizen.org/privilege/mediastorage is needed if model file is relevant to media storage.
+ * @remarks %http://tizen.org/privilege/externalstorage is needed if model file is relevant to external storage.
+ * @param[in] name The unique name to indicate the resources.
+ * @param[in] path The path to machine learning resources.
+ * @param[in] description Nullable, description for machine learning resources.
+ * @return 0 on success. Otherwise a negative error value.
+ * @retval #ML_ERROR_NONE Successful.
+ * @retval #ML_ERROR_NOT_SUPPORTED Not supported.
+ * @retval #ML_ERROR_PERMISSION_DENIED The application does not have the privilege to access to the storage.
+ * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
+ * @retval #ML_ERROR_IO_ERROR The operation of DB or filesystem has failed.
+ *
+ * Here is an example of the usage:
+ * @code
+ * // The machine-learning resource API provides a method to share the data files those can be used for training or inferencing AI model.
+ * // Users may generate preprocessed data file, and add it into machine-learning service.
+ * // Then an application can fetch the data set for retraining an AI model.
+ *
+ * const char *my_resources[3] = {
+ *   "/path/to/resources/my_res1.dat",
+ *   "/path/to/resources/my_res2.dat"
+ *   "/path/to/resources/my_res3.dat"
+ * };
+ *
+ * int status;
+ * unsigned int i, length;
+ * ml_information_list_h resources;
+ * ml_information_h res;
+ * char *path_to_resource;
+ *
+ * // Add resource files with name "my_resource".
+ * for (i = 0; i < 3; i++) {
+ *   status = ml_service_resource_add ("my_resource", my_resources[i], "This is my resource data file.");
+ *   if (status != ML_ERROR_NONE) {
+ *     // Handle error case.
+ *   }
+ * }
+ *
+ * // Get the resources with specific name.
+ * status = ml_service_resource_get ("my_resource", &resources);
+ * if (status != ML_ERROR_NONE) {
+ *   // Handle error case.
+ * }
+ *
+ * status = ml_information_list_length (resources, &length);
+ * for (i = 0; i < length; i++) {
+ *   status = ml_information_list_get (resources, i, &res);
+ *   // Get the path of added resources.
+ *   status = ml_information_get (res, "path", (void **) &path_to_resource);
+ * }
+ *
+ * // Release the information handle of resources.
+ * status = ml_information_list_destroy (resources);
+ * @endcode
+ */
+int ml_service_resource_add (const char *name, const char *path, const char *description);
+
+/**
+ * @brief Deletes the information of the resources from machine learning service.
+ * @since_tizen 8.0
+ * @remarks This does not remove the resource files from file system.
+ * @param[in] name The unique name to indicate the resources.
+ * @return 0 on success. Otherwise a negative error value.
+ * @retval #ML_ERROR_NONE Successful.
+ * @retval #ML_ERROR_NOT_SUPPORTED Not supported.
+ * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
+ * @retval #ML_ERROR_IO_ERROR The operation of DB or filesystem has failed.
+ */
+int ml_service_resource_delete (const char *name);
+
+/**
+ * @brief Gets the information of the resources from machine learning service.
+ * @since_tizen 8.0
+ * @remarks If the function succeeds, the @a res should be released using ml_information_list_destroy().
+ * @param[in] name The unique name to indicate the resources.
+ * @param[out] res The handle of the machine learning resources.
+ * @return 0 on success. Otherwise a negative error value.
+ * @retval #ML_ERROR_NONE Successful.
+ * @retval #ML_ERROR_NOT_SUPPORTED Not supported.
+ * @retval #ML_ERROR_INVALID_PARAMETER Given parameter is invalid.
+ * @retval #ML_ERROR_IO_ERROR The operation of DB or filesystem has failed.
+ * @retval #ML_ERROR_OUT_OF_MEMORY Failed to allocate required memory.
+ */
+int ml_service_resource_get (const char *name, ml_information_list_h *res);
+
+/**
  * @}
  */
 #ifdef __cplusplus
index af79ca1..ef5f619 100644 (file)
@@ -863,3 +863,192 @@ ml_service_model_delete (const char *name, const unsigned int version)
 
   return ret;
 }
+
+/**
+ * @brief Adds new information of machine learning resources.
+ */
+int
+ml_service_resource_add (const char *name, const char *path,
+    const char *description)
+{
+  int ret = ML_ERROR_NONE;
+  g_autoptr (GError) err = NULL;
+
+  check_feature_state (ML_FEATURE_SERVICE);
+
+  if (!name) {
+    _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
+        "The parameter, 'name' is NULL. It should be a valid string.");
+  }
+
+  ret = _ml_service_check_path (path);
+  if (ret != ML_ERROR_NONE)
+    return ret;
+
+  /**
+   * @todo Check whether the given path is in the app's resource directory.
+   * Implement common function later, see ml_service_model_register().
+   */
+  ret = ml_agent_dbus_interface_resource_add (name, path,
+      description ? description : "", &err);
+  if (ret < 0) {
+    _ml_error_report ("Failed to invoke the method resource_add (%s).",
+        err ? err->message : "Unknown error");
+  }
+
+  return ret;
+}
+
+/**
+ * @brief Deletes the information of the resources from machine learning service.
+ */
+int
+ml_service_resource_delete (const char *name)
+{
+  int ret = ML_ERROR_NONE;
+  g_autoptr (GError) err = NULL;
+
+  check_feature_state (ML_FEATURE_SERVICE);
+
+  if (!name) {
+    _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
+        "The parameter, 'name' is NULL. It should be a valid string.");
+  }
+
+  ret = ml_agent_dbus_interface_resource_delete (name, &err);
+  if (ret < 0) {
+    _ml_error_report ("Failed to invoke the method resource_delete (%s).",
+        err ? err->message : "Unknown error");
+  }
+
+  return ret;
+}
+
+/**
+ * @brief Gets the information of the resources from machine learning service.
+ */
+int
+ml_service_resource_get (const char *name, ml_information_list_h * res)
+{
+  int ret = ML_ERROR_NONE;
+  ml_info_list_s *_info_list = NULL;
+  g_autofree gchar *res_info = NULL;
+  g_autoptr (GError) err = NULL;
+  guint i, n;
+
+  check_feature_state (ML_FEATURE_SERVICE);
+
+  if (!name) {
+    _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
+        "The parameter, 'name' is NULL. It should be a valid string.");
+  }
+
+  if (res == NULL) {
+    _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
+        "The argument for 'res' should not be NULL.");
+  }
+
+  if (*res != NULL) {
+    _ml_logw (WARN_MSG_DPTR_SET_OVER, "ml_information_list_h res = NULL");
+  }
+  *res = NULL;
+
+  ret = ml_agent_dbus_interface_resource_get (name, &res_info, &err);
+  if (ML_ERROR_NONE != ret || !res_info) {
+    _ml_error_report_return (ret,
+        "Failed to invoke the method resource_get (%s).",
+        err ? err->message : "Unknown error");
+  }
+
+  _info_list = g_try_new0 (ml_info_list_s, 1);
+  if (NULL == _info_list) {
+    _ml_error_report_return (ML_ERROR_OUT_OF_MEMORY,
+        "Failed to allocate memory for ml_information_list_h. Out of memory?");
+  }
+
+  /* Parse json string (res_info). */
+  {
+    g_autoptr (JsonParser) parser = NULL;
+    JsonNode *rnode = NULL;
+    JsonArray *array = NULL;
+
+    parser = json_parser_new ();
+    if (!parser) {
+      _ml_error_report_return (ML_ERROR_OUT_OF_MEMORY,
+          "Failed to allocate memory for JsonParser. Out of memory?");
+    }
+
+    if (!json_parser_load_from_data (parser, res_info, -1, &err)) {
+      _ml_error_report ("Failed to parse the json string (%s).",
+          err ? err->message : "Unknown error");
+      return ML_ERROR_INVALID_PARAMETER;
+    }
+
+    rnode = json_parser_get_root (parser);
+    if (!rnode) {
+      _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
+          "Failed to get the root node of json string.");
+    }
+
+    array =
+        (JSON_NODE_HOLDS_ARRAY (rnode) ? json_node_get_array (rnode) : NULL);
+    if (!array) {
+      _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
+          "Failed to get array from json string.");
+    }
+
+    n = json_array_get_length (array);
+    if (n == 0U) {
+      _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
+          "Failed to retrieve the length of the json array.");
+    }
+
+    _info_list->info = g_try_new0 (ml_info_s *, n);
+    if (!_info_list->info) {
+      g_free (_info_list);
+      _ml_error_report_return (ML_ERROR_OUT_OF_MEMORY,
+          "Failed to allocate memory for list of ml_information_list_h. Out of memory?");
+    }
+    _info_list->length = n;
+
+    for (i = 0; i < n; i++) {
+      g_autoptr (GList) members = NULL;
+      JsonObject *jobj = NULL;
+      GList *l;
+
+      ret = ml_option_create ((ml_information_h *) (&_info_list->info[i]));
+      if (ret != ML_ERROR_NONE) {
+        _ml_error_report
+            ("Failed to allocate memory for ml_information_list_h. Out of memory?");
+        n = i;
+        goto error;
+      }
+
+      jobj = json_array_get_object_element (array, i);
+      members = json_object_get_members (jobj);
+      for (l = members; l != NULL; l = l->next) {
+        const gchar *key = l->data;
+        const gchar *val = json_object_get_string_member (jobj, key);
+
+        ml_option_set (_info_list->info[i], key, g_strdup (val), g_free);
+      }
+      /** @todo add app_info and fetch it from service-db */
+    }
+  }
+
+  *res = _info_list;
+  return ML_ERROR_NONE;
+
+error:
+  if (_info_list) {
+    for (i = 0; i < n; i++) {
+      if (_info_list->info[i])
+        ml_option_destroy (_info_list->info[i]);
+    }
+
+    g_free (_info_list->info);
+    g_free (_info_list);
+  }
+
+  return ret;
+}
index 0646acf..3f71baf 100644 (file)
@@ -1296,6 +1296,92 @@ TEST_F (MLServiceAgentTest, resource_gdbus_call_n)
 }
 
 /**
+ * @brief Negative testcase of ml-service-resource - add invalid param.
+ */
+TEST (MLServiceResource, addInvalidParam01_n)
+{
+  const gchar *root_path = g_getenv ("MLAPI_SOURCE_ROOT_PATH");
+  g_autofree gchar *test_model = NULL;
+  int ret;
+
+  /* Adding res file into ml-service-agent requires absolute path, ignore this case. */
+  if (root_path == NULL)
+    return;
+
+  test_model = g_build_filename (root_path, "tests", "test_models", "models",
+      "mobilenet_v1_1.0_224_quant.tflite", NULL);
+  ASSERT_TRUE (g_file_test (test_model, G_FILE_TEST_EXISTS));
+
+  ret = ml_service_resource_add (NULL, test_model, "unittest-add-resource");
+  EXPECT_NE (ret, ML_ERROR_NONE);
+}
+
+/**
+ * @brief Negative testcase of ml-service-resource - add invalid param.
+ */
+TEST (MLServiceResource, addInvalidParam02_n)
+{
+  int ret;
+
+  ret = ml_service_resource_add ("unittest-res", NULL, "unittest-add-resource");
+  EXPECT_NE (ret, ML_ERROR_NONE);
+}
+
+/**
+ * @brief Negative testcase of ml-service-resource - add invalid param.
+ */
+TEST (MLServiceResource, addInvalidParam03_n)
+{
+  const gchar *root_path = g_getenv ("MLAPI_SOURCE_ROOT_PATH");
+  g_autofree gchar *test_model = NULL;
+  int ret;
+
+  /* Adding res file into ml-service-agent requires absolute path, ignore this case. */
+  if (root_path == NULL)
+    return;
+
+  test_model = g_build_filename (root_path, "tests", "test_models", "models",
+      "restest_invalid.tflite", NULL);
+
+  ret = ml_service_resource_add ("unittest-res", test_model, "unittest-add-resource");
+  EXPECT_NE (ret, ML_ERROR_NONE);
+}
+
+/**
+ * @brief Negative testcase of ml-service-resource - delete invalid param.
+ */
+TEST (MLServiceResource, deleteInvalidParam01_n)
+{
+  int ret;
+
+  ret = ml_service_resource_delete (NULL);
+  EXPECT_NE (ret, ML_ERROR_NONE);
+}
+
+/**
+ * @brief Negative testcase of ml-service-resource - get invalid param.
+ */
+TEST (MLServiceResource, getInvalidParam01_n)
+{
+  int ret;
+  ml_option_h res = NULL;
+
+  ret = ml_service_resource_get (NULL, &res);
+  EXPECT_NE (ret, ML_ERROR_NONE);
+}
+
+/**
+ * @brief Negative testcase of ml-service-resource - get invalid param.
+ */
+TEST (MLServiceResource, getInvalidParam02_n)
+{
+  int ret;
+
+  ret = ml_service_resource_get ("unittest-res", NULL);
+  EXPECT_NE (ret, ML_ERROR_NONE);
+}
+
+/**
  * @brief Negative test for pipeline. With DBus unconnected.
  */
 TEST (MLServiceAgentTestDbusUnconnected, pipeline_n)