Line data Source code
1 : /* SPDX-License-Identifier: Apache-2.0 */
2 : /**
3 : * Copyright (c) 2023 Samsung Electronics Co., Ltd. All Rights Reserved.
4 : *
5 : * @file ml-api-service-offloading.c
6 : * @date 26 Jun 2023
7 : * @brief ML offloading service of NNStreamer/Service C-API
8 : * @see https://github.com/nnstreamer/nnstreamer
9 : * @author Gichan Jang <gichan2.jang@samsung.com>
10 : * @bug No known bugs except for NYI items
11 : */
12 :
13 : #include <glib.h>
14 : #include <glib/gstdio.h>
15 : #include <gio/gio.h>
16 : #include <gst/gst.h>
17 : #include <gst/gstbuffer.h>
18 : #include <gst/app/app.h>
19 : #include <string.h>
20 : #include <curl/curl.h>
21 : #include <json-glib/json-glib.h>
22 : #include <nnstreamer-edge.h>
23 :
24 : #include "ml-api-internal.h"
25 : #include "ml-api-service.h"
26 : #include "ml-api-service-private.h"
27 : #include "ml-api-service-offloading.h"
28 : #include "ml-api-service-training-offloading.h"
29 :
30 : #define MAX_PORT_NUM_LEN 6U
31 :
32 : /**
33 : * @brief Data struct for options.
34 : */
35 : typedef struct
36 : {
37 : gchar *host;
38 : guint port;
39 : gchar *topic;
40 : gchar *dest_host;
41 : guint dest_port;
42 : nns_edge_connect_type_e conn_type;
43 : nns_edge_node_type_e node_type;
44 : gchar *id;
45 : } edge_info_s;
46 :
47 : /**
48 : * @brief Structure for ml_service_offloading.
49 : */
50 : typedef struct
51 : {
52 : nns_edge_h edge_h;
53 : nns_edge_node_type_e node_type;
54 :
55 : gchar *path; /**< A path to save the received model file */
56 : GHashTable *table;
57 :
58 : ml_service_offloading_mode_e offloading_mode;
59 : void *priv;
60 : } _ml_service_offloading_s;
61 :
62 : /**
63 : * @brief Get ml-service node type from ml_option.
64 : */
65 : static nns_edge_node_type_e
66 0 : _mlrs_get_node_type (const gchar * value)
67 : {
68 0 : nns_edge_node_type_e node_type = NNS_EDGE_NODE_TYPE_UNKNOWN;
69 :
70 0 : if (!value)
71 0 : return node_type;
72 :
73 0 : if (g_ascii_strcasecmp (value, "sender") == 0) {
74 0 : node_type = NNS_EDGE_NODE_TYPE_QUERY_CLIENT;
75 0 : } else if (g_ascii_strcasecmp (value, "receiver") == 0) {
76 0 : node_type = NNS_EDGE_NODE_TYPE_QUERY_SERVER;
77 : } else {
78 0 : _ml_error_report ("Invalid node type '%s', please check node type.", value);
79 : }
80 :
81 0 : return node_type;
82 : }
83 :
84 : /**
85 : * @brief Get nnstreamer-edge connection type
86 : */
87 : static nns_edge_connect_type_e
88 0 : _mlrs_get_conn_type (const gchar * value)
89 : {
90 0 : nns_edge_connect_type_e conn_type = NNS_EDGE_CONNECT_TYPE_UNKNOWN;
91 :
92 0 : if (!value)
93 0 : return conn_type;
94 :
95 0 : if (0 == g_ascii_strcasecmp (value, "TCP"))
96 0 : conn_type = NNS_EDGE_CONNECT_TYPE_TCP;
97 0 : else if (0 == g_ascii_strcasecmp (value, "HYBRID"))
98 0 : conn_type = NNS_EDGE_CONNECT_TYPE_HYBRID;
99 0 : else if (0 == g_ascii_strcasecmp (value, "MQTT"))
100 0 : conn_type = NNS_EDGE_CONNECT_TYPE_MQTT;
101 0 : else if (0 == g_ascii_strcasecmp (value, "AITT"))
102 0 : conn_type = NNS_EDGE_CONNECT_TYPE_AITT;
103 : else
104 0 : conn_type = NNS_EDGE_CONNECT_TYPE_UNKNOWN;
105 :
106 0 : return conn_type;
107 : }
108 :
109 : /**
110 : * @brief Get edge info from ml_option.
111 : */
112 : static void
113 0 : _mlrs_get_edge_info (ml_option_h option, edge_info_s ** edge_info)
114 : {
115 : edge_info_s *_info;
116 : void *value;
117 :
118 0 : *edge_info = _info = g_new0 (edge_info_s, 1);
119 :
120 0 : if (ML_ERROR_NONE == ml_option_get (option, "host", &value))
121 0 : _info->host = g_strdup (value);
122 : else
123 0 : _info->host = g_strdup ("localhost");
124 0 : if (ML_ERROR_NONE == ml_option_get (option, "port", &value))
125 0 : _info->port = (guint) g_ascii_strtoull (value, NULL, 10);
126 0 : if (ML_ERROR_NONE == ml_option_get (option, "dest-host", &value))
127 0 : _info->dest_host = g_strdup (value);
128 : else
129 0 : _info->dest_host = g_strdup ("localhost");
130 0 : if (ML_ERROR_NONE == ml_option_get (option, "dest-port", &value))
131 0 : _info->dest_port = (guint) g_ascii_strtoull (value, NULL, 10);
132 0 : if (ML_ERROR_NONE == ml_option_get (option, "connect-type", &value))
133 0 : _info->conn_type = _mlrs_get_conn_type (value);
134 : else
135 0 : _info->conn_type = NNS_EDGE_CONNECT_TYPE_UNKNOWN;
136 0 : if (ML_ERROR_NONE == ml_option_get (option, "topic", &value))
137 0 : _info->topic = g_strdup (value);
138 0 : if (ML_ERROR_NONE == ml_option_get (option, "node-type", &value))
139 0 : _info->node_type = _mlrs_get_node_type (value);
140 0 : if (ML_ERROR_NONE == ml_option_get (option, "id", &value))
141 0 : _info->id = g_strdup (value);
142 0 : }
143 :
144 : /**
145 : * @brief Set nns-edge info.
146 : */
147 : static void
148 0 : _mlrs_set_edge_info (edge_info_s * edge_info, nns_edge_h edge_h)
149 : {
150 0 : char port[MAX_PORT_NUM_LEN] = { 0, };
151 :
152 0 : nns_edge_set_info (edge_h, "HOST", edge_info->host);
153 0 : g_snprintf (port, MAX_PORT_NUM_LEN, "%u", edge_info->port);
154 0 : nns_edge_set_info (edge_h, "PORT", port);
155 :
156 0 : if (edge_info->topic)
157 0 : nns_edge_set_info (edge_h, "TOPIC", edge_info->topic);
158 :
159 0 : nns_edge_set_info (edge_h, "DEST_HOST", edge_info->dest_host);
160 0 : g_snprintf (port, MAX_PORT_NUM_LEN, "%u", edge_info->dest_port);
161 0 : nns_edge_set_info (edge_h, "DEST_PORT", port);
162 0 : }
163 :
164 : /**
165 : * @brief Release edge info.
166 : */
167 : static void
168 0 : _mlrs_release_edge_info (edge_info_s * edge_info)
169 : {
170 0 : g_free (edge_info->dest_host);
171 0 : g_free (edge_info->host);
172 0 : g_free (edge_info->topic);
173 0 : g_free (edge_info->id);
174 0 : g_free (edge_info);
175 0 : }
176 :
177 : /**
178 : * @brief Get ml offloading service type from ml_option.
179 : */
180 : static ml_service_offloading_type_e
181 0 : _mlrs_get_service_type (gchar * service_str)
182 : {
183 0 : ml_service_offloading_type_e service_type =
184 : ML_SERVICE_OFFLOADING_TYPE_UNKNOWN;
185 :
186 0 : if (!service_str)
187 0 : return service_type;
188 :
189 0 : if (g_ascii_strcasecmp (service_str, "model_raw") == 0) {
190 0 : service_type = ML_SERVICE_OFFLOADING_TYPE_MODEL_RAW;
191 0 : } else if (g_ascii_strcasecmp (service_str, "model_uri") == 0) {
192 0 : service_type = ML_SERVICE_OFFLOADING_TYPE_MODEL_URI;
193 0 : } else if (g_ascii_strcasecmp (service_str, "pipeline_raw") == 0) {
194 0 : service_type = ML_SERVICE_OFFLOADING_TYPE_PIPELINE_RAW;
195 0 : } else if (g_ascii_strcasecmp (service_str, "pipeline_uri") == 0) {
196 0 : service_type = ML_SERVICE_OFFLOADING_TYPE_PIPELINE_URI;
197 0 : } else if (g_ascii_strcasecmp (service_str, "reply") == 0) {
198 0 : service_type = ML_SERVICE_OFFLOADING_TYPE_REPLY;
199 : } else {
200 0 : _ml_error_report ("Invalid service type '%s', please check service type.",
201 : service_str);
202 : }
203 :
204 0 : return service_type;
205 : }
206 :
207 : /**
208 : * @brief Get ml offloading service activation type.
209 : */
210 : static gboolean
211 0 : _mlrs_parse_activate (const gchar * activate)
212 : {
213 0 : return (activate && g_ascii_strcasecmp (activate, "true") == 0);
214 : }
215 :
216 : /**
217 : * @brief Callback function for receving data using curl.
218 : */
219 : static size_t
220 0 : curl_mem_write_cb (void *data, size_t size, size_t nmemb, void *clientp)
221 : {
222 0 : size_t recv_size = size * nmemb;
223 0 : GByteArray *array = (GByteArray *) clientp;
224 :
225 0 : if (!array || !data || recv_size == 0)
226 0 : return 0;
227 :
228 0 : g_byte_array_append (array, data, recv_size);
229 :
230 0 : return recv_size;
231 : }
232 :
233 : /**
234 : * @brief Register model file given by the offloading sender.
235 : */
236 : static gboolean
237 0 : _mlrs_model_register (gchar * service_key, nns_edge_data_h data_h,
238 : void *data, nns_size_t data_len, const gchar * dir_path)
239 : {
240 0 : guint version = 0;
241 0 : g_autofree gchar *description = NULL;
242 0 : g_autofree gchar *name = NULL;
243 0 : g_autofree gchar *activate = NULL;
244 0 : g_autofree gchar *model_path = NULL;
245 0 : gboolean active_bool = TRUE;
246 0 : GError *error = NULL;
247 :
248 0 : if (NNS_EDGE_ERROR_NONE != nns_edge_data_get_info (data_h, "description",
249 : &description)
250 0 : || NNS_EDGE_ERROR_NONE != nns_edge_data_get_info (data_h, "name", &name)
251 0 : || NNS_EDGE_ERROR_NONE != nns_edge_data_get_info (data_h, "activate",
252 : &activate)) {
253 0 : _ml_loge ("Failed to get info from data handle.");
254 0 : return FALSE;
255 : }
256 :
257 0 : active_bool = _mlrs_parse_activate (activate);
258 0 : model_path = g_build_path (G_DIR_SEPARATOR_S, dir_path, name, NULL);
259 0 : if (!g_file_set_contents (model_path, (char *) data, data_len, &error)) {
260 0 : _ml_loge ("Failed to write data to file: %s",
261 : error ? error->message : "unknown error");
262 0 : g_clear_error (&error);
263 0 : return FALSE;
264 : }
265 :
266 : /**
267 : * @todo Hashing the path. Where is the default path to save the model file?
268 : */
269 0 : if (ML_ERROR_NONE != ml_service_model_register (service_key, model_path,
270 : active_bool, description, &version)) {
271 0 : _ml_loge ("Failed to register model, service key is '%s'.", service_key);
272 0 : return FALSE;
273 : }
274 :
275 0 : return TRUE;
276 : }
277 :
278 : /**
279 : * @brief Get path to save the model given from offloading sender.
280 : * @note The caller is responsible for freeing the returned data using g_free().
281 : */
282 : static gchar *
283 0 : _mlrs_get_model_dir_path (_ml_service_offloading_s * offloading_s,
284 : const gchar * service_key)
285 : {
286 0 : g_autofree gchar *dir_path = NULL;
287 :
288 0 : if (offloading_s->path) {
289 0 : dir_path = g_strdup (offloading_s->path);
290 : } else {
291 0 : g_autofree gchar *current_dir = g_get_current_dir ();
292 :
293 0 : dir_path = g_build_path (G_DIR_SEPARATOR_S, current_dir, service_key, NULL);
294 0 : if (g_mkdir_with_parents (dir_path, 0755) < 0) {
295 0 : _ml_loge ("Failed to create directory '%s': %s", dir_path,
296 : g_strerror (errno));
297 0 : return NULL;
298 : }
299 : }
300 :
301 0 : return g_steal_pointer (&dir_path);
302 : }
303 :
304 : /**
305 : * @brief Get data from gievn uri
306 : */
307 : static gboolean
308 0 : _mlrs_get_data_from_uri (gchar * uri, GByteArray * array)
309 : {
310 : CURL *curl;
311 : CURLcode res;
312 0 : gboolean ret = FALSE;
313 :
314 0 : curl = curl_easy_init ();
315 0 : if (curl) {
316 0 : if (CURLE_OK != curl_easy_setopt (curl, CURLOPT_URL, (gchar *) uri) ||
317 0 : CURLE_OK != curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L) ||
318 0 : CURLE_OK != curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION,
319 0 : curl_mem_write_cb) ||
320 0 : CURLE_OK != curl_easy_setopt (curl, CURLOPT_WRITEDATA,
321 : (void *) array)) {
322 0 : _ml_loge ("Failed to set option for curl easy handle.");
323 0 : ret = FALSE;
324 0 : goto done;
325 : }
326 :
327 0 : res = curl_easy_perform (curl);
328 :
329 0 : if (res != CURLE_OK) {
330 0 : _ml_loge ("curl_easy_perform failed: %s", curl_easy_strerror (res));
331 0 : ret = FALSE;
332 0 : goto done;
333 : }
334 :
335 0 : ret = TRUE;
336 : }
337 :
338 0 : done:
339 0 : if (curl)
340 0 : curl_easy_cleanup (curl);
341 0 : return ret;
342 : }
343 :
344 : /**
345 : * @brief Process ml offloading service
346 : */
347 : static int
348 0 : _mlrs_process_service_offloading (nns_edge_data_h data_h, void *user_data)
349 : {
350 : void *data;
351 : nns_size_t data_len;
352 0 : g_autofree gchar *service_str = NULL;
353 0 : g_autofree gchar *service_key = NULL;
354 0 : g_autofree gchar *dir_path = NULL;
355 : ml_service_offloading_type_e service_type;
356 0 : int ret = NNS_EDGE_ERROR_NONE;
357 0 : ml_service_s *mls = (ml_service_s *) user_data;
358 0 : _ml_service_offloading_s *offloading_s =
359 : (_ml_service_offloading_s *) mls->priv;
360 0 : ml_service_event_e event_type = ML_SERVICE_EVENT_UNKNOWN;
361 0 : ml_information_h info_h = NULL;
362 :
363 0 : ret = nns_edge_data_get (data_h, 0, &data, &data_len);
364 0 : if (NNS_EDGE_ERROR_NONE != ret) {
365 0 : _ml_error_report_return (ret,
366 : "Failed to get data while processing the ml-offloading service.");
367 : }
368 :
369 0 : ret = nns_edge_data_get_info (data_h, "service-type", &service_str);
370 0 : if (NNS_EDGE_ERROR_NONE != ret) {
371 0 : _ml_error_report_return (ret,
372 : "Failed to get service type while processing the ml-offloading service.");
373 : }
374 0 : service_type = _mlrs_get_service_type (service_str);
375 :
376 0 : ret = nns_edge_data_get_info (data_h, "service-key", &service_key);
377 0 : if (NNS_EDGE_ERROR_NONE != ret) {
378 0 : _ml_error_report_return (ret,
379 : "Failed to get service key while processing the ml-offloading service.");
380 : }
381 :
382 0 : dir_path = _mlrs_get_model_dir_path (offloading_s, service_key);
383 :
384 0 : if (offloading_s->offloading_mode == ML_SERVICE_OFFLOADING_MODE_TRAINING) {
385 0 : ret = ml_service_training_offloading_process_received_data (mls, data_h,
386 : dir_path, data, service_type);
387 0 : if (NNS_EDGE_ERROR_NONE != ret) {
388 0 : _ml_error_report_return (ret,
389 : "Failed to process received data on training offloading.");
390 : }
391 :
392 0 : if (service_type == ML_SERVICE_OFFLOADING_TYPE_REPLY) {
393 0 : if (!dir_path) {
394 0 : _ml_error_report_return (NNS_EDGE_ERROR_UNKNOWN,
395 : "Failed to get model directory path.");
396 : }
397 :
398 0 : if (!_mlrs_model_register (service_key, data_h, data, data_len, dir_path)) {
399 0 : _ml_error_report_return (NNS_EDGE_ERROR_UNKNOWN,
400 : "Failed to register model downloaded from: %s.", (gchar *) data);
401 : }
402 : }
403 : }
404 :
405 0 : switch (service_type) {
406 0 : case ML_SERVICE_OFFLOADING_TYPE_MODEL_URI:
407 : {
408 : GByteArray *array;
409 :
410 0 : if (!dir_path) {
411 0 : _ml_error_report_return (NNS_EDGE_ERROR_UNKNOWN,
412 : "Failed to get model directory path.");
413 : }
414 :
415 0 : array = g_byte_array_new ();
416 :
417 0 : if (!_mlrs_get_data_from_uri ((gchar *) data, array)) {
418 0 : g_byte_array_free (array, TRUE);
419 0 : _ml_error_report_return (NNS_EDGE_ERROR_IO,
420 : "Failed to get data from uri: %s.", (gchar *) data);
421 : }
422 :
423 0 : if (_mlrs_model_register (service_key, data_h, array->data, array->len,
424 : dir_path)) {
425 0 : event_type = ML_SERVICE_EVENT_MODEL_REGISTERED;
426 : } else {
427 0 : _ml_error_report ("Failed to register model downloaded from: %s.",
428 : (gchar *) data);
429 0 : ret = NNS_EDGE_ERROR_UNKNOWN;
430 : }
431 0 : g_byte_array_free (array, TRUE);
432 0 : break;
433 : }
434 0 : case ML_SERVICE_OFFLOADING_TYPE_MODEL_RAW:
435 : {
436 0 : if (!dir_path) {
437 0 : _ml_error_report_return (NNS_EDGE_ERROR_UNKNOWN,
438 : "Failed to get model directory path.");
439 : }
440 :
441 0 : if (_mlrs_model_register (service_key, data_h, data, data_len, dir_path)) {
442 0 : event_type = ML_SERVICE_EVENT_MODEL_REGISTERED;
443 : } else {
444 0 : _ml_error_report ("Failed to register model downloaded from: %s.",
445 : (gchar *) data);
446 0 : ret = NNS_EDGE_ERROR_UNKNOWN;
447 : }
448 0 : break;
449 : }
450 0 : case ML_SERVICE_OFFLOADING_TYPE_PIPELINE_URI:
451 : {
452 0 : GByteArray *array = g_byte_array_new ();
453 :
454 0 : ret = _mlrs_get_data_from_uri ((gchar *) data, array);
455 0 : if (!ret) {
456 0 : g_byte_array_free (array, TRUE);
457 0 : _ml_error_report_return (ret,
458 : "Failed to get data from uri: %s.", (gchar *) data);
459 : }
460 0 : ret = ml_service_pipeline_set (service_key, (gchar *) array->data);
461 0 : if (ML_ERROR_NONE == ret) {
462 0 : event_type = ML_SERVICE_EVENT_PIPELINE_REGISTERED;
463 : }
464 0 : g_byte_array_free (array, TRUE);
465 0 : break;
466 : }
467 0 : case ML_SERVICE_OFFLOADING_TYPE_PIPELINE_RAW:
468 0 : ret = ml_service_pipeline_set (service_key, (gchar *) data);
469 0 : if (ML_ERROR_NONE == ret) {
470 0 : event_type = ML_SERVICE_EVENT_PIPELINE_REGISTERED;
471 : }
472 0 : break;
473 0 : case ML_SERVICE_OFFLOADING_TYPE_REPLY:
474 : {
475 0 : ret = _ml_information_create (&info_h);
476 0 : if (ML_ERROR_NONE != ret) {
477 0 : _ml_error_report ("Failed to create information handle.");
478 0 : goto done;
479 : }
480 0 : ret = _ml_information_set (info_h, "data", (void *) data, NULL);
481 0 : if (ML_ERROR_NONE != ret) {
482 0 : _ml_error_report ("Failed to set data information.");
483 0 : goto done;
484 : }
485 0 : event_type = ML_SERVICE_EVENT_REPLY;
486 0 : break;
487 : }
488 0 : default:
489 0 : _ml_error_report ("Unknown service type '%d' or not supported yet.",
490 : service_type);
491 0 : break;
492 : }
493 :
494 0 : if (event_type != ML_SERVICE_EVENT_UNKNOWN) {
495 0 : ml_service_event_cb_info_s cb_info = { 0 };
496 :
497 0 : _ml_service_get_event_cb_info (mls, &cb_info);
498 :
499 0 : if (cb_info.cb) {
500 0 : cb_info.cb (event_type, info_h, cb_info.pdata);
501 : }
502 : }
503 :
504 0 : done:
505 0 : if (info_h) {
506 0 : ml_information_destroy (info_h);
507 : }
508 :
509 0 : return ret;
510 : }
511 :
512 : /**
513 : * @brief Edge event callback.
514 : */
515 : static int
516 0 : _mlrs_edge_event_cb (nns_edge_event_h event_h, void *user_data)
517 : {
518 0 : nns_edge_event_e event = NNS_EDGE_EVENT_UNKNOWN;
519 0 : nns_edge_data_h data_h = NULL;
520 0 : int ret = NNS_EDGE_ERROR_NONE;
521 :
522 0 : ret = nns_edge_event_get_type (event_h, &event);
523 0 : if (NNS_EDGE_ERROR_NONE != ret)
524 0 : return ret;
525 :
526 0 : switch (event) {
527 0 : case NNS_EDGE_EVENT_NEW_DATA_RECEIVED:
528 : {
529 0 : ret = nns_edge_event_parse_new_data (event_h, &data_h);
530 0 : if (NNS_EDGE_ERROR_NONE != ret)
531 0 : return ret;
532 :
533 0 : ret = _mlrs_process_service_offloading (data_h, user_data);
534 0 : break;
535 : }
536 0 : default:
537 0 : break;
538 : }
539 :
540 0 : if (data_h)
541 0 : nns_edge_data_destroy (data_h);
542 :
543 0 : return ret;
544 : }
545 :
546 : /**
547 : * @brief Create edge handle.
548 : */
549 : static int
550 0 : _mlrs_create_edge_handle (ml_service_s * mls, edge_info_s * edge_info)
551 : {
552 0 : int ret = 0;
553 0 : nns_edge_h edge_h = NULL;
554 0 : _ml_service_offloading_s *offloading_s = NULL;
555 :
556 0 : ret = nns_edge_create_handle (edge_info->id, edge_info->conn_type,
557 : edge_info->node_type, &edge_h);
558 :
559 0 : if (NNS_EDGE_ERROR_NONE != ret) {
560 0 : _ml_error_report_return_continue (ret,
561 : "Failed to create edge handle for ml-service offloading. Internal error?");
562 : }
563 :
564 0 : offloading_s = (_ml_service_offloading_s *) mls->priv;
565 0 : ret = nns_edge_set_event_callback (edge_h, _mlrs_edge_event_cb, mls);
566 0 : if (NNS_EDGE_ERROR_NONE != ret) {
567 0 : _ml_error_report
568 : ("Failed to set event callback in edge handle for ml-service offloading. Internal error?");
569 0 : goto error;
570 : }
571 :
572 0 : _mlrs_set_edge_info (edge_info, edge_h);
573 :
574 0 : ret = nns_edge_start (edge_h);
575 0 : if (NNS_EDGE_ERROR_NONE != ret) {
576 0 : _ml_error_report
577 : ("Failed to start edge for ml-service offloading. Internal error?");
578 0 : goto error;
579 : }
580 :
581 0 : if (edge_info->node_type == NNS_EDGE_NODE_TYPE_QUERY_CLIENT) {
582 0 : ret = nns_edge_connect (edge_h, edge_info->dest_host, edge_info->dest_port);
583 :
584 0 : if (NNS_EDGE_ERROR_NONE != ret) {
585 0 : _ml_error_report
586 : ("Failed to connect edge for ml-service offloading. Internal error?");
587 0 : goto error;
588 : }
589 : }
590 :
591 0 : offloading_s->edge_h = edge_h;
592 :
593 0 : error:
594 0 : if (ret != NNS_EDGE_ERROR_NONE) {
595 0 : nns_edge_release_handle (edge_h);
596 : }
597 :
598 0 : return ret;
599 : }
600 :
601 : /**
602 : * @brief Set offloading mode and private data.
603 : */
604 : int
605 0 : ml_service_offloading_set_mode (ml_service_h handle,
606 : ml_service_offloading_mode_e mode, void *priv)
607 : {
608 0 : ml_service_s *mls = (ml_service_s *) handle;
609 : _ml_service_offloading_s *offloading_s;
610 :
611 0 : if (!_ml_service_handle_is_valid (mls)) {
612 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
613 : "The parameter, 'handle' (ml_service_h), is invalid. It should be a valid ml_service_h instance.");
614 : }
615 :
616 0 : offloading_s = (_ml_service_offloading_s *) mls->priv;
617 :
618 0 : offloading_s->offloading_mode = mode;
619 0 : offloading_s->priv = priv;
620 :
621 0 : return ML_ERROR_NONE;
622 : }
623 :
624 : /**
625 : * @brief Get offloading mode and private data.
626 : */
627 : int
628 0 : ml_service_offloading_get_mode (ml_service_h handle,
629 : ml_service_offloading_mode_e * mode, void **priv)
630 : {
631 0 : ml_service_s *mls = (ml_service_s *) handle;
632 : _ml_service_offloading_s *offloading_s;
633 :
634 0 : if (!_ml_service_handle_is_valid (mls)) {
635 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
636 : "The parameter, 'handle' (ml_service_h), is invalid. It should be a valid ml_service_h instance.");
637 : }
638 :
639 0 : if (!mode || !priv) {
640 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
641 : "The parameter, mode or priv, is null. It should be a valid pointer.");
642 : }
643 :
644 0 : offloading_s = (_ml_service_offloading_s *) mls->priv;
645 :
646 0 : *mode = offloading_s->offloading_mode;
647 0 : *priv = offloading_s->priv;
648 :
649 0 : return ML_ERROR_NONE;
650 : }
651 :
652 : /**
653 : * @brief Internal function to release ml-service offloading data.
654 : */
655 : int
656 0 : ml_service_offloading_release_internal (ml_service_s * mls)
657 : {
658 : _ml_service_offloading_s *offloading_s;
659 :
660 : /* Supposed internal function call to release handle. */
661 0 : if (!mls || !mls->priv)
662 0 : return ML_ERROR_NONE;
663 :
664 0 : offloading_s = (_ml_service_offloading_s *) mls->priv;
665 :
666 0 : if (offloading_s->offloading_mode == ML_SERVICE_OFFLOADING_MODE_TRAINING) {
667 : /**
668 : * 'ml_service_training_offloading_destroy' transfers internally trained models.
669 : * So keep offloading handle.
670 : */
671 : if (ML_ERROR_NONE != ml_service_training_offloading_destroy (mls)) {
672 0 : _ml_error_report
673 : ("Failed to release ml-service training offloading handle");
674 : }
675 : }
676 :
677 0 : if (offloading_s->edge_h) {
678 0 : nns_edge_release_handle (offloading_s->edge_h);
679 0 : offloading_s->edge_h = NULL;
680 : }
681 :
682 0 : if (offloading_s->table) {
683 0 : g_hash_table_destroy (offloading_s->table);
684 0 : offloading_s->table = NULL;
685 : }
686 :
687 0 : g_free (offloading_s->path);
688 0 : g_free (offloading_s);
689 0 : mls->priv = NULL;
690 :
691 0 : return ML_ERROR_NONE;
692 : }
693 :
694 : /**
695 : * @brief Set value in ml-service offloading handle.
696 : */
697 : int
698 0 : ml_service_offloading_set_information (ml_service_h handle, const gchar * name,
699 : const gchar * value)
700 : {
701 0 : ml_service_s *mls = (ml_service_s *) handle;
702 : _ml_service_offloading_s *offloading_s;
703 0 : int ret = ML_ERROR_NONE;
704 :
705 0 : if (!_ml_service_handle_is_valid (mls)) {
706 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
707 : "The parameter, 'handle' (ml_service_h), is invalid. It should be a valid ml_service_h instance.");
708 : }
709 :
710 0 : offloading_s = (_ml_service_offloading_s *) mls->priv;
711 :
712 0 : if (g_ascii_strcasecmp (name, "path") == 0) {
713 0 : if (!g_file_test (value, G_FILE_TEST_IS_DIR)) {
714 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
715 : "The given param, dir path '%s' is invalid or the dir is not found or accessible.",
716 : value);
717 : }
718 :
719 0 : if (g_access (value, W_OK) != 0) {
720 0 : _ml_error_report_return (ML_ERROR_PERMISSION_DENIED,
721 : "Write permission to dir '%s' is denied.", value);
722 : }
723 :
724 0 : g_free (offloading_s->path);
725 0 : offloading_s->path = g_strdup (value);
726 :
727 0 : if (offloading_s->offloading_mode == ML_SERVICE_OFFLOADING_MODE_TRAINING) {
728 0 : ret = ml_service_training_offloading_set_path (mls, offloading_s->path);
729 : }
730 : }
731 :
732 0 : return ret;
733 : }
734 :
735 : /**
736 : * @brief Internal function to set the services in ml-service offloading handle.
737 : */
738 : static int
739 0 : _ml_service_offloading_set_service (ml_service_s * mls, const gchar * key,
740 : const gchar * value)
741 : {
742 : _ml_service_offloading_s *offloading_s;
743 :
744 0 : if (!STR_IS_VALID (key) || !STR_IS_VALID (value)) {
745 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
746 : "The parameter, 'key' or 'value' is null or empty string. It should be a valid string.");
747 : }
748 0 : offloading_s = (_ml_service_offloading_s *) mls->priv;
749 :
750 0 : g_hash_table_insert (offloading_s->table, g_strdup (key), g_strdup (value));
751 :
752 0 : return ML_ERROR_NONE;
753 : }
754 :
755 : /**
756 : * @brief Internal function to parse service info from config file.
757 : */
758 : static int
759 0 : _ml_service_offloading_parse_services (ml_service_s * mls, JsonObject * object)
760 : {
761 : GList *list, *iter;
762 0 : int status = ML_ERROR_NONE;
763 :
764 0 : list = json_object_get_members (object);
765 0 : for (iter = list; iter != NULL; iter = g_list_next (iter)) {
766 0 : const gchar *key = iter->data;
767 0 : JsonNode *json_node = json_object_get_member (object, key);
768 0 : gchar *val = json_to_string (json_node, TRUE);
769 :
770 0 : if (val) {
771 0 : status = _ml_service_offloading_set_service (mls, key, val);
772 0 : g_free (val);
773 :
774 0 : if (status != ML_ERROR_NONE) {
775 0 : _ml_error_report ("Failed to set service key '%s'.", key);
776 0 : break;
777 : }
778 : }
779 : }
780 0 : g_list_free (list);
781 :
782 0 : return status;
783 : }
784 :
785 : /**
786 : * @brief Internal function to create ml-offloading data with given ml-option handle.
787 : */
788 : static int
789 0 : _ml_service_offloading_create_from_option (ml_service_s * mls,
790 : ml_option_h option)
791 : {
792 : _ml_service_offloading_s *offloading_s;
793 0 : edge_info_s *edge_info = NULL;
794 0 : int ret = ML_ERROR_NONE;
795 0 : gchar *_path = NULL;
796 :
797 0 : mls->priv = offloading_s = g_try_new0 (_ml_service_offloading_s, 1);
798 0 : if (offloading_s == NULL) {
799 0 : _ml_error_report_return (ML_ERROR_OUT_OF_MEMORY,
800 : "Failed to allocate memory for the service handle's private data. Out of memory?");
801 : }
802 :
803 0 : offloading_s->table =
804 0 : g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
805 0 : if (!offloading_s->table) {
806 0 : _ml_error_report_return (ML_ERROR_OUT_OF_MEMORY,
807 : "Failed to allocate memory for the table of ml-service offloading. Out of memory?");
808 : }
809 :
810 0 : if (ML_ERROR_NONE == ml_option_get (option, "path", (void **) (&_path))) {
811 0 : ret = ml_service_offloading_set_information (mls, "path", _path);
812 0 : if (ML_ERROR_NONE != ret) {
813 0 : _ml_error_report_return (ret,
814 : "Failed to set path in ml-service offloading handle.");
815 : }
816 : }
817 :
818 0 : _mlrs_get_edge_info (option, &edge_info);
819 :
820 0 : offloading_s->node_type = edge_info->node_type;
821 0 : ret = _mlrs_create_edge_handle (mls, edge_info);
822 0 : _mlrs_release_edge_info (edge_info);
823 :
824 0 : return ret;
825 : }
826 :
827 : /**
828 : * @brief Internal function to convert json (string member) to ml-option.
829 : */
830 : static int
831 0 : _ml_service_offloading_convert_to_option (JsonObject * object,
832 : ml_option_h * option_h)
833 : {
834 0 : ml_option_h tmp = NULL;
835 0 : int status = ML_ERROR_NONE;
836 : const gchar *key, *val;
837 : GList *list, *iter;
838 :
839 0 : if (!object || !option_h)
840 0 : return ML_ERROR_INVALID_PARAMETER;
841 :
842 0 : status = ml_option_create (&tmp);
843 0 : if (status != ML_ERROR_NONE) {
844 0 : _ml_error_report_return (status,
845 : "Failed to convert json to ml-option, cannot create ml-option handle.");
846 : }
847 :
848 0 : list = json_object_get_members (object);
849 0 : for (iter = list; iter != NULL; iter = g_list_next (iter)) {
850 0 : key = iter->data;
851 :
852 0 : if (g_ascii_strcasecmp (key, "training") == 0) {
853 : /* It is not a value to set for option. */
854 0 : continue;
855 : }
856 :
857 0 : val = json_object_get_string_member (object, key);
858 :
859 0 : status = ml_option_set (tmp, key, g_strdup (val), g_free);
860 0 : if (status != ML_ERROR_NONE) {
861 0 : _ml_error_report ("Failed to set %s option: %s.", key, val);
862 0 : break;
863 : }
864 : }
865 0 : g_list_free (list);
866 :
867 0 : if (status == ML_ERROR_NONE) {
868 0 : *option_h = tmp;
869 : } else {
870 0 : ml_option_destroy (tmp);
871 : }
872 :
873 0 : return status;
874 : }
875 :
876 : /**
877 : * @brief Internal function to parse configuration file to create offloading service.
878 : */
879 : int
880 0 : ml_service_offloading_create (ml_service_h handle, JsonObject * object)
881 : {
882 0 : ml_service_s *mls = (ml_service_s *) handle;
883 : int status;
884 0 : ml_option_h option = NULL;
885 : JsonObject *offloading;
886 :
887 0 : if (!mls || !object) {
888 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
889 : "Failed to create offloading handle, invalid parameter.");
890 : }
891 :
892 0 : offloading = json_object_get_object_member (object, "offloading");
893 0 : if (!offloading) {
894 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
895 : "Failed to get 'offloading' member from configuration file.");
896 : }
897 :
898 0 : status = _ml_service_offloading_convert_to_option (offloading, &option);
899 0 : if (status != ML_ERROR_NONE) {
900 0 : _ml_error_report ("Failed to set ml-option from configuration file.");
901 0 : goto done;
902 : }
903 :
904 0 : status = _ml_service_offloading_create_from_option (mls, option);
905 0 : if (status != ML_ERROR_NONE) {
906 0 : _ml_error_report ("Failed to create ml-service offloading.");
907 0 : goto done;
908 : }
909 :
910 0 : if (json_object_has_member (object, "services")) {
911 : JsonObject *svc_object;
912 :
913 0 : svc_object = json_object_get_object_member (object, "services");
914 0 : status = _ml_service_offloading_parse_services (mls, svc_object);
915 0 : if (status != ML_ERROR_NONE) {
916 0 : _ml_logw ("Failed to parse services from configuration file.");
917 : }
918 : }
919 :
920 0 : if (json_object_has_member (offloading, "training")) {
921 0 : status = ml_service_training_offloading_create (mls, offloading);
922 0 : if (status != ML_ERROR_NONE) {
923 0 : _ml_logw ("Failed to parse training from configuration file.");
924 : }
925 : }
926 :
927 0 : done:
928 0 : if (option)
929 0 : ml_option_destroy (option);
930 :
931 0 : return status;
932 : }
933 :
934 : /**
935 : * @brief Internal function to start ml-service offloading.
936 : */
937 : int
938 0 : ml_service_offloading_start (ml_service_h handle)
939 : {
940 0 : ml_service_s *mls = (ml_service_s *) handle;
941 : _ml_service_offloading_s *offloading_s;
942 0 : int ret = ML_ERROR_NONE;
943 :
944 0 : if (!_ml_service_handle_is_valid (mls)) {
945 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
946 : "The parameter, 'handle' (ml_service_h), is invalid. It should be a valid ml_service_h instance.");
947 : }
948 :
949 0 : offloading_s = (_ml_service_offloading_s *) mls->priv;
950 :
951 0 : if (offloading_s->offloading_mode == ML_SERVICE_OFFLOADING_MODE_TRAINING) {
952 0 : ret = ml_service_training_offloading_start (mls);
953 0 : if (ret != ML_ERROR_NONE) {
954 0 : _ml_error_report ("Failed to start training offloading.");
955 : }
956 : }
957 :
958 0 : return ret;
959 : }
960 :
961 : /**
962 : * @brief Internal function to stop ml-service offloading.
963 : */
964 : int
965 0 : ml_service_offloading_stop (ml_service_h handle)
966 : {
967 0 : ml_service_s *mls = (ml_service_s *) handle;
968 : _ml_service_offloading_s *offloading_s;
969 0 : int ret = ML_ERROR_NONE;
970 :
971 0 : if (!_ml_service_handle_is_valid (mls)) {
972 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
973 : "The parameter, 'handle' (ml_service_h), is invalid. It should be a valid ml_service_h instance.");
974 : }
975 :
976 0 : offloading_s = (_ml_service_offloading_s *) mls->priv;
977 :
978 0 : if (offloading_s->offloading_mode == ML_SERVICE_OFFLOADING_MODE_TRAINING) {
979 0 : ret = ml_service_training_offloading_stop (mls);
980 0 : if (ret != ML_ERROR_NONE) {
981 0 : _ml_error_report ("Failed to stop training offloading.");
982 : }
983 : }
984 :
985 0 : return ret;
986 : }
987 :
988 : /**
989 : * @brief Register new information, such as neural network models or pipeline descriptions, on a offloading server.
990 : */
991 : int
992 0 : ml_service_offloading_request (ml_service_h handle, const char *key,
993 : const ml_tensors_data_h input)
994 : {
995 0 : ml_service_s *mls = (ml_service_s *) handle;
996 0 : _ml_service_offloading_s *offloading_s = NULL;
997 0 : const gchar *service_key = NULL;
998 0 : nns_edge_data_h data_h = NULL;
999 0 : int ret = NNS_EDGE_ERROR_NONE;
1000 0 : const gchar *service_str = NULL;
1001 0 : const gchar *description = NULL;
1002 0 : const gchar *name = NULL;
1003 0 : const gchar *activate = NULL;
1004 0 : ml_tensors_data_s *_in = NULL;
1005 : JsonNode *service_node;
1006 : JsonObject *service_obj;
1007 : guint i;
1008 :
1009 0 : if (!_ml_service_handle_is_valid (mls)) {
1010 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
1011 : "The parameter, 'handle' (ml_service_h), is invalid. It should be a valid ml_service_h instance.");
1012 : }
1013 :
1014 0 : if (!STR_IS_VALID (key)) {
1015 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
1016 : "The parameter, 'key' is NULL. It should be a valid string.");
1017 : }
1018 :
1019 0 : if (!input)
1020 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
1021 : "The parameter, input (ml_tensors_data_h), is NULL. It should be a valid ml_tensor_data_h instance, which is usually created by ml_tensors_data_create().");
1022 :
1023 0 : offloading_s = (_ml_service_offloading_s *) mls->priv;
1024 :
1025 0 : service_str = g_hash_table_lookup (offloading_s->table, key);
1026 0 : if (!service_str) {
1027 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
1028 : "The given service key, %s, is not registered in the ml-service offloading handle.",
1029 : key);
1030 : }
1031 :
1032 0 : service_node = json_from_string (service_str, NULL);
1033 0 : if (!service_node) {
1034 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
1035 : "Failed to parse the json string, %s.", service_str);
1036 : }
1037 0 : service_obj = json_node_get_object (service_node);
1038 0 : if (!service_obj) {
1039 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
1040 : "Failed to get the json object from the json node.");
1041 : }
1042 :
1043 0 : service_str = json_object_get_string_member (service_obj, "service-type");
1044 0 : if (!service_str) {
1045 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
1046 : "Failed to get service type from the json object.");
1047 : }
1048 :
1049 0 : service_key = json_object_get_string_member (service_obj, "service-key");
1050 0 : if (!service_key) {
1051 0 : _ml_error_report_return (ML_ERROR_INVALID_PARAMETER,
1052 : "Failed to get service key from the json object.");
1053 : }
1054 :
1055 0 : ret = nns_edge_data_create (&data_h);
1056 0 : if (NNS_EDGE_ERROR_NONE != ret) {
1057 0 : _ml_error_report ("Failed to create an edge data.");
1058 0 : return ret;
1059 : }
1060 :
1061 0 : ret = nns_edge_data_set_info (data_h, "service-type", service_str);
1062 0 : if (NNS_EDGE_ERROR_NONE != ret) {
1063 0 : _ml_error_report ("Failed to set service type in edge data.");
1064 0 : goto done;
1065 : }
1066 0 : ret = nns_edge_data_set_info (data_h, "service-key", service_key);
1067 0 : if (NNS_EDGE_ERROR_NONE != ret) {
1068 0 : _ml_error_report ("Failed to set service key in edge data.");
1069 0 : goto done;
1070 : }
1071 :
1072 0 : description = json_object_get_string_member (service_obj, "description");
1073 0 : if (description) {
1074 0 : ret = nns_edge_data_set_info (data_h, "description", description);
1075 0 : if (NNS_EDGE_ERROR_NONE != ret) {
1076 0 : _ml_logi ("Failed to set description in edge data.");
1077 : }
1078 : }
1079 :
1080 0 : name = json_object_get_string_member (service_obj, "name");
1081 0 : if (name) {
1082 0 : ret = nns_edge_data_set_info (data_h, "name", name);
1083 0 : if (NNS_EDGE_ERROR_NONE != ret) {
1084 0 : _ml_logi ("Failed to set name in edge data.");
1085 : }
1086 : }
1087 :
1088 0 : activate = json_object_get_string_member (service_obj, "activate");
1089 0 : if (activate) {
1090 0 : ret = nns_edge_data_set_info (data_h, "activate", activate);
1091 0 : if (NNS_EDGE_ERROR_NONE != ret) {
1092 0 : _ml_logi ("Failed to set activate in edge data.");
1093 : }
1094 : }
1095 0 : _in = (ml_tensors_data_s *) input;
1096 0 : for (i = 0; i < _in->num_tensors; i++) {
1097 : ret =
1098 0 : nns_edge_data_add (data_h, _in->tensors[i].data, _in->tensors[i].size,
1099 : NULL);
1100 0 : if (NNS_EDGE_ERROR_NONE != ret) {
1101 0 : _ml_error_report ("Failed to add camera data to the edge data.");
1102 0 : goto done;
1103 : }
1104 : }
1105 :
1106 0 : ret = nns_edge_send (offloading_s->edge_h, data_h);
1107 0 : if (NNS_EDGE_ERROR_NONE != ret) {
1108 0 : _ml_error_report
1109 : ("Failed to publish the data to register the offloading service.");
1110 : }
1111 :
1112 0 : done:
1113 0 : if (data_h)
1114 0 : nns_edge_data_destroy (data_h);
1115 0 : return ret;
1116 : }
|